JPA
[JPA] 변경 감지(dirty checking)와 병합(merge)
코리늬
2020. 5. 2. 12:41
본 내용은 인프런 김영한님의 JPA 활용 강의를 수강하며 정리한 내용입니다.
준영속 엔티티란?
- 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.
- 객체가 이미 DB에 한번 저장되어 식별자가 존재하는 경우, 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있다.
- jpa가 관리를 하지 않음,
persist 상태가 아니기 때문에
값을 바꿔도 DB변경이 일어나지 않음
준영속 엔티티를 수정하는 2가지 방법
- dirtyChecking을 사용하여 값을 저장
@Transactional
void update(Item itemParam) {
//itemParam: 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회
findItem.setPrice(itemParam.getPrice()); //dirtyChecking
}
//set 로직이 끝나면 transactional이 commit된다
//그럼 flush할때 바뀐값을 dirty checking 해서 최종 값으로 commit을 날림
- merge를 사용해서 값을 저장
merge가 뭐길래?
준영속 상태의 코드를 영속상태로 바꿔벌임
병합 동작 방식
- merge()가 실행한다.
- 파라미터로 넘어온 준영속 엔티티의 식별자 값으로
1차 캐시에서 엔티티를 조회
한다. - 만약 1차 캐시에 엔티티가 없으면 DB에서 엔티티를 조회하고, 1차 캐시에 저장한다.
- 조회한 영속성 엔티티의 값을 채워넣는다. 새로운 값으로
- 영속 상태인 엔티티를 반환한다.
merge의 코드화
@Transactional
void update(Item itemParam) {
//itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}
Save에서의 merge
public void save(Item item) {
if (item.getId() == null) {
em.persist(item);
} else {
em.merge(item);
}
}
save 메소드의 내부 동작과정을 보면, 식별자가 없으면 새로운 엔티티로 판단하여 persist()
로 영속화하고, 식별자 값이 있다면 이미 한 번 영속화 되었던 엔티티로 판단하여 merge()
로 수정한다.
이렇게 함으로써 메소드를 사용하는 클라이언트는 저장과 수정을 구분하지 않아도 되기 때문에 클라이언트의 로직이 단순해진다.
주의사항 !!!
- dirty checking을 사용하면 원하는 속성만 변경할 수 있지만, 병합을 사용 할 경우 모든 속성이 변경된다.
- 병합시 값이 없으면
null
로 업데이트 해버릴 수 있다. (실무에서 null로 잘못 업데이트 하다가는 큰 장애가 발생할 수 있다.)
그래서 영속성 상태인 객체를 사용하여 값을 저장을 해야한다.
또한 dirty checking은 트랜잭션 커밋 시점에 실행된다!!!