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가 뭐길래?

준영속 상태의 코드를 영속상태로 바꿔벌임

병합 동작 방식

  1. merge()가 실행한다.
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
  3. 만약 1차 캐시에 엔티티가 없으면 DB에서 엔티티를 조회하고, 1차 캐시에 저장한다.
  4. 조회한 영속성 엔티티의 값을 채워넣는다. 새로운 값으로
  5. 영속 상태인 엔티티를 반환한다.

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은 트랜잭션 커밋 시점에 실행된다!!!