[JPA] 변경 감지(Dirty Checking) 와 병합(merge)
변경 감지(Dirty Checking) 와 병합( merge )
* 준영속 상태?
- 영속 상태였다가 더는 영속성 컨텍스트가 관리하지 않는 상태
- 영속성 컨텍스트로부터 분리된 상태
* 준영속 상태를 만드는 3가지 방법
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료
* 준영속 엔티티?
- 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.
- 엔티티의 데이터를 가지고 있는 클라이언트 사이드로 넘기기 위한 객체
* 준영속 엔티티를 수정하는 2가지 방법
1. 변경 감지 기능 사용
2. 병합( merge ) 사용
* 변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이 변경된다.
1. 변경 감지 기능
// 트랜잭션 안에서 entity를 조회해야 영속성 상태로 조회가 되고,
// 또 값을 변경해면 변경 감지(dirty checking)가 일어난다.
@Transactional(readOnly = false)
public void updateItem(Long itemId, Item item) { // item : 파리미터로 넘어온 준영속 상태의 엔티티
// itemId, name, price, stockQuantity : 수정할 값
// 영속성 컨텍스트에서 엔티티를 다시 조회한다.
Item findItem = itemRepository.findOne(itemId);
// 데이터를 수정한다.
findItem.setName(item.getName());
findItem.setPrice(item.getPrice());
findItem.setStockQuantity(item.getStockQuantity());
}
@Transactional으로 인하여 로직이 끝날 때 JPA에서 트랜잭션 commit 시점에 변경 감지(Dirty Checking)한 후 Flush를 하게 되기 때문에 따로 itemRepository.save() 할 필요가 없다.
2. 병합
병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.
// 트랜잭션 안에서 entity를 조회해야 영속성 상태로 조회가 되고,
// 또 값을 변경해면 변경 감지(dirty checking)가 일어난다.
@Transactional(readOnly = false)
public void updateItem(Item item) { // item : 파리미터로 넘어온 준영속 상태의 엔티티
// 병합 한다.
Item mergeItem = em.merge(item);
// == itemRepository.save(item.getId);
}
* 병합 동작 방식
* 병합시 동작 방식을 간단히 정리
1. 준영속 엔티티의 식별자 값으로 영속 엔티티를 조회한다.
2. 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체한다.(병합한다.)
3. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 데이터베이스에 UPDATE SQL이 실행된다.
* 주의사항
변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이
변경된다. 병합시 값이 없으면 null 로 업데이트 할 위험도 있다. (병합은 모든 필드를 교체한다.)
* 정리
엔티티를 변경할 때는 항상 "변경 감지"를 사용하세요 / 병합(merge)는 최대한 자제하자.
Reference
https://ultrakain.gitbooks.io/jpa/content/chapter3/chapter3.6.html