[JPA] 변경감지(더티체킹) vs 병합(merge)
JPA에서 값을 수정하는 방식
JPA를 이용하여 객체를 영속화하고 사용할 때 결국 CRUD에서 수정하는 일을 만나게 된다.
JPA는 수정하는 방법에 있어서 변경 감지(더티 체킹)혹은 병합 방식을 사용하게 된다. 오늘은 이 두 방식의 차이에 대해 정리하려한다.
Dirty Check?
JPA의 장점이자 특징으로 더티체킹이라는 것이 있다. 직역을 하면 이상한 뜻이 되곤 하는데 풀어 설명을 해보자면 다음과 같다.
Dirty의 의미는 상태의 변화가 생긴 정도를 의미한다고 하며 Dirty Checking을 설명하자면 상태 변경 검사로 생각하면 된다.
병합 merge는 준영속 상태의 엔티티를 영속화해주는 것이라고 이해하면 된다.
즉, 더티체킹은 영속화된 엔티티에 대해서 변경 감지를 하여 변경에 대한 부분을 적용시켜 주는 것이고, merge는 준영속 상태를 영속화해주어 변경된 점이 있다면 변경해주는 것으로 결국 데이터를 `변경`해준다는 점에서 보면 같다.
그런데 이 둘은 큰 차이점으로 인해 사용시 주의해야만 하는 점이 있다.
준영속 상태에 대한 정의
3. 영속성 관리 - 내부 동작 방식
영속성 관리 - 내부 동작 방식 영속성 컨텍스트 영속성 컨텍스트 : "엔티티를 영구 저장하는 환경"이라는 뜻. EntityManager.Persist(entity); EntityManager를 통해서 DB에 저장하는 것이 아닌 영속성 컨텍스
dul2.tistory.com
변경감지(더티체킹) vs 병합(merge)
다음 코드로 이해해보자.
// Item Service
//준영속 엔티티 수정시 merge를 이용한 방법.
@Transactional
public void saveItem(Item item) {
itemRepository.save(item);
}
//준영속 엔티티 수정시 변경감지(더티체킹)을 이용한 방법.
@Transactional
public void updateItem(Long itemId, Book param) {
Book findItem = (Book) itemRepository.findOne(itemId);
findItem.setName(param.getName());
findItem.setPrice(param.getPrice());
findItem.setStockQuantity(param.getStockQuantity());
findItem.setAuthor(param.getAuthor());
findItem.setIsbn(param.getIsbn());
}
위의 코드를 보면 saveItem()과 updateItem(), 두 메서드 모두 업데이트가 가능하다.
ItemRepository save Code 보기 - 더보기 클릭
//ItemRepository
public void save(Item item) {
if (item.getId() == null) {
em.persist(item);
} else {
em.merge(item); // 업데이트 개념
}
}
객체를 save시에 id값을 체크해서 null이 아닌 경우에는 DB를 다녀온 경우이므로 merge를 사용하여 값을 변경한다.
먼저, 변경감지 방식을 보면 레포지토리에서 ID값을 이용해 조회를 한다. 이 조회를 통해 JPA는 findItem 객체를 영속화하게 된다. 영속화 함에 따라 JPA는 변경되는 객체를 감지하고 Transaction이 끝날 때 변경된 부분을 수정하기 위한 update 쿼리를 내보낸다.
하지만, merge시에는 모든 필드에 대한 수정이 이루어진다는 차이점이 존재한다.
이 때 문제가 발생하게 되는데 예시 상황을 확인해보자.
만약 saveItem의 파라미터로 사용되는 item에 이름과 수량 등의 수정할 데이터가 있지만 가격은 변경하고 싶지 않아 price의 값 따로 설정하지 않았다고 생각해보자. 이 때, item.price는 null인 상태이므로 merge를 시킨다면 merge는 모든 필드에 대해서 변경을 진행하기 때문에 가격이 사라지는 문제가 발생하게 된다.
잘모르고 merge를 사용하게 되면 이름과 수량을 변경하려는 의도와는 다르게 다른 값까지 변경할 가능성이 있으므로 merge 사용할 때에는 각별히 주의해야한다.
실무에서는 더 복잡한 경우가 발생하고 개발을 간단하게 하기위해 변경감지를 이용한 수정 방식을 선택하는 것이 좋다.
즉, 먼저 JPA에 영속화(조회)하는 과정을 거쳐 수정 로직을 짜도록 하자.
참고로, 위 코드처럼 Setter방식으로 변경하는 것은 옳지 못하다.
각 메서드의 이름에는 의미를 주어 Entity에 모이도록 설계하는 것이 좋다.