출처: https://blog.iroot.kr/341 [RootKR] 출처: https://blog.iroot.kr/341 [RootKR]

 병합은 준영속 상태의 엔티티를 다시 영속 상태로 변경할 때 사용한다. merge() 메서드는 준영속 상태의 엔티티를 받아 그 정보로 새로운 영속 상태의 엔티티를 반환한다. 

 

@RequiredArgsConstructor
public class ExMain {
   private final EntityManager em;


   @Transactional
   public void update() {
       // 준영속 상태의 객체
       Member member = new Member("오세진", 32);
       member.setAge(30);
   }
}

 위 코드에서 member는 준영속 상태의 객체다. 아직 JPA에 관리된 상태가 아닌 순수한 객체이기 때문이다. 여기서 아무리 setXXX 메서드로 값을 변경해 보았자, 변경감지에 걸리지 않아 디비에 반영이 되지 않는다. 

 

 이를 변경하고 싶다면 준영속 상태의 엔티티를 영속 상태로 변경해야 하는데, 이때 merge()를 사용하면 된다. 

@RequiredArgsConstructor
public class ExMain {
   private final EntityManager em;

   @Transactional
   public  void update() {
       // 준영속 상태의 객체
       Member member = new Member("오세진", 32);
       member.setAge(30);

       // 영속 상태의 객체
       Member mergedMember = em.merge(member);
       mergedMember.setAge(32); // 변경감지
   }
}

 위 코드에서 영속상태의 객체를 변경감지 동작을 통해 변경된 데이터가 Transaction이 끝나면서 데이터베이스에 반영된다. 여기서 주의할 점은 파라미터로 넘어온 member 객체는 여전히 준영속 상태라는 점이다. 

 

merge()의 동작 방식

  1. merge()를 실행
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회
    • 만약 1차 캐시에 엔티티가 없으면 데이터베이스에 엔티티를 조회하고 1차 캐시에 저장.
    • 무조건 1번은 db 조회를 하므로 성능에 좋지 않을 수 있다. 
  3. 조회한 영속 엔티티에 member 엔티티의 값을 채워 넣음
    • 이때 member의 모든 값을 영속 엔티티에 채워 넣기 때문에 null 값이 들어갈 수 도 있는 문제가 생긴다.
    • 이래서 업데이트 시 merge()보단 변경 감지를 사용하자.
  4. 영속 상태의 객체를 반환

 

 병합은 파라미터로 넘어온 엔티티의 식별자 값으로 영속성 컨텍스트를 조회하고, 찾는 엔티티가 없으면 데이터베이스에서 조회한다. 만약 데이터베이스에도 없다면 새로운 엔티티를 생성해 병합한다. 따라서 병합은 save or update 기능을 수행한다고 볼 수 있다.

 

현업에서 jpa의 동작을 재대로 이해하지 못한 체 데이터를 update 치려다 수많은 삽질을 했던 경험이 있다. 이 기능들을 잘 이해하고 나니 update에 대한 이해와 흐름을 파악하며 개발할 수 있을 것 같다.

+ Recent posts