Skip to content

JPA Section7

SH-Seol edited this page May 28, 2024 · 1 revision

홈 화면과 레이아웃

  • thymeleaf를 심도 있게 쓰려면 Hierarchical-style의 layouts을 사용해라.
  • Intellij가 resource를 인지하지 못하면 synchronize하고 rebuild하자
public class MemberForm {

    @NotEmpty(message = "회원 이름은 필수 입니다.")
    private String name;

    private String city;
    private String street;
    private String zipcode;
}
@PostMapping("/members/new")
    public String create(@Valid MemberForm form, BindingResult result){
        
        if(result.hasErrors()){
            return "members/createMemberForm";
        }
        
        Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
        Member member = new Member();
        member.setName(form.getName());
        member.setAddress(address);
        
        memberService.join(member);
        
        return "redirect:/";
    }
  • @Valid를 통해 위 @NotEmpty를 적용하고, BindingResult는 에러를 담아두고 실행한다.
  • Member가 아니라 MemberForm을 parameter로 받는 이유 → 심플하면 바로 entity를 받으면 되지만 실무에서는 단순하지 않음. Form으로 받고 controller로 정재하는 것이 좋고, Member로 받으면 Member에 필요한 작업을 해야해서 점점 지저분해짐.

Form이냐 Entity냐?

요구사항이 정말 단순한 경우에는 Entity를 써도 된다. 그런데 요구사항이 실무에서는 일대일 매칭인 경우가 거의 없다. 그런 상황에서 Entity를 쓰면 결국 화면 기능 때문에 Entity가 지저분해지고 이는 유지보수가 어려워질 수 있음을 의미한다.

  • API를 반환할 때에는 절대 Entity를 외부로 반환하면 안된다.
    • Member Entity를 반환하게 되면, API스팩이 변화하게 되고, 해당 요소가 외부로 노출이 된다.
@PostMapping("items/{itemId}/edit")
    public String updateItem(@PathVariable String itemId, @ModelAttribute("form") BookForm form){
        
        Book book = new Book();
        book.setId(form.getId());
        book.setIsbn(form.getIsbn());
        book.setName(form.getName());
        book.setStockQuantity(form.getStockQuantity());
        book.setPrice(form.getPrice());
        book.setAuthor(form.getAuthor());
        
        itemService.saveItem(book);
        
        return "redirect:items";
    }
  • PathVariable로 itemId를 넘기기에 보안에 문제가 발생할 수 있음

    • Session, permission 등으로 해결할 수 있다.
  • Book은 새로운 객체이지만, 이미 DB에 저장된 것을 가져와서 식별자가 존재한다.

  • 식별자가 이미 존재한 것은 바로 준영속 엔티티라고 한다. JPA가 관리를 안 한다. → 아무리 바꾸더라도 DB는 바뀌지 않는다.

변경 감지와 병합

  • 변경 감지 (Dirty checking)
  • 병합 (Merge)

변경 감지

@Transactional
    public void updateItem(Long itemId, Book param){
        Item findItem = itemRepository.findOne(itemId);
        findItem.setPrice(param.getPrice());
    }
  • 엔티티 조회 후 변경할 값 선택, 이후 트랜잭션 커밋 시점에 변경 감지(Dirty checking)하고 DB변경

병합

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(itemParam);
}
  • 동작 방식은 아래의 코드와 같다.
@Transactional
    public Item updateItem(Long itemId, Book param){
        Item findItem = itemRepository.findOne(itemId);
        findItem.setPrice(param.getPrice());
        return findItem;
    }

Untitled

  • 그런데 만약 setPrice()를 설정하지 않으면 findItem.price는 null이 된다.
  • 따라서 최대한 merge를 쓰지 않고 DirtyChecking을 쓰자.
@Transactional
    public void updateItem(Long itemId, String name, int price, int stockQuantity){
        Item findItem = itemRepository.findOne(itemId);
        findItem.setName(name);
        findItem.setPrice(price);
        findItem.setStockQuantity(stockQuantity);
    }
  • 이렇게 코드를 바꾸고 dirty checking하는 것이 좋다.
  • 만약 바꿀 것이 너무 많다면 dto를 만들어서 하는 것이 좋다.
  • setter없이 entity안에서 바로 추적할 수 있는 것이 좋다.
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity){
    Item findItem = itemRepository.findOne(itemId);
    findItem.updateDetails(name, price, stockQuantity);
}

Clone this wiki locally