준영속 Entity 업데이트 방법

kangsan·2021년 2월 14일
0

inflearn 김영한님의 강의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 중 웹 계층 개발 챕터를 듣고 배운것을 정리한 내용입니다.

Entity 업데이트 시 주의점

Item 이라는 Entity 를 수정하는 Method 를 만든다고 치자.

// controller
@PostMapping("items/{itemId}/edit")
public String updateItem(
	@PathVariable Long itemId,
	@ModelAttribute("form") ItemForm formdata) {
    
    Item item = new Item();
    item.setId(formdata.getId());
    item.setName(formdata.getName());
    item.setPrice(formdata.getPrice());
    
    itemService.saveItem(item);

}

위와 같이 업데이트 할 아이템을 받아와 새로운 Item 객체를 만들어 서비스에 넘기고 있다.

이때 파라미터로 넘기는 Item 객체는 수정 폼을 통해 올라온 formdata 에 의해 new 로 재조립 된 Item 엔티티이고 영속성 컨텍스트에서 관리하는 엔티티가 아닌, 준영속 상태에 있는 엔티티라고 할 수 있다.

(준영속 상태의 기준은 식별자를 기준으로 영속상태가 되어 DB에 저장된 적이 있는지로 판단한다)

업데이트를 위해 준영속 상태의 엔티티를 em.persist(item) 때리면 어떻게 될까?

org.hibernate.PersistentObjectException: 
detached entity passed to persist: jpabook.jpashop.domain.item.Item

detached entity 가 persist 하려고 해! 하며 에러가 난다.

준영속 상태를 영속 상태로 만드는 방법

두가지가 있다.

GOOD - 영속성 컨텍스트에서 엔티티를 조회

parameter 의 key 값을 바탕으로 영속성 컨텍스트의 entity 를 조회해서 가져오는 방법이다.

@Transactional
void update(Item param) {
    Item item = em.find(Item.class, param.getId());
    item.setName(param.getName());
    /*...*/
}

추천하는 방법이고 영속성 컨텍스트에서 다시 가져왔으므로 commit 시점에 dirty checking을 통해 업데이트 쿼리가 날아간다.

그러므로 애초에 Controller 단에선 어설프게 엔티티 객체를 만들어서 주지 않고, form data에서 entity를 수정할 정보만 던져주면 된다. (어차피 영속성 컨텍스트에서 조회 후 업데이트 할 것이므로)

수정할 정보의 양이 많은 경우, DTO를 고려한다.

BAD - merge 사용

@Transactional
void update(Item param) {
    Item item = em.merge(param);
}

merge의 동작 방식

  1. 영속성 엔티티 조회
    merge() 메서드는 파라미터로 받은 준영속 엔티티의 id(식별자) 값으로 엔티티를 조회한다.
    만일, 영속성 컨텍스트에서 찾았다면 해당 엔티티를 반환하고 없으면 DB에서 해당 식별자로 조회해온 후 영속성 컨텍스트에 올린 후 반환한다.

  2. 조회한 영속 엔티티를 준영속 엔티티 값으로 모두 교체(병합)
    여기가 문제가 되는 지점
    원하는 필드만 바꿀 수 있는게 아니라 모든 속성이 준영속 엔티티 값으로 교체됨
    만일 정책상 상품 이름은 등록 후 수정이 안된다고 할 때 수정 필드가 없을텐데 그러면 필드에서 받은 값으로 만든 준영속 상태 엔티티는 name 이 비어있을 것. 그러면 영속 상태의 엔티티도 null로 업데이트 쳐버린다.

  3. 영속 상태인 병합된 엔티티를 반환

결론

엔티티 수정 시 id로 엔티티 조회 후 수정하자

0개의 댓글