-
[JPA] em.merge(entity)는 내부 코드 들여다보기개발 지식/JPA 2023. 11. 29. 11:53
- SimpleJpaRepository의 save() 메서드의 em.merge(entity)는 어떻게 동작하는지 궁금해서 디버깅을 통해 내부 코드를 살펴보자.
- 준영속 상태의 엔티티를 save하는 메서드를 실행하고 위의 부분에 브레이크포인트를 걸고 내부로 들어가보자
- 대충 읽어보면 공유된 엔티티 매니저 프록시가 em에 할당되었음을 알 수 있다. (정확한 원리는 다음에!)
em.merge(entity)에서 내부로 들어가게 되면 SharedEntityManagerCreator의 private static class인 SharedEntityManagerInvocationHandler의 invoke() 메서드로 이동하게 된다.
SharedEntityManagerInvocationHandler의 invoke 메서드
- 해당 메서드는 java.lang.reflect 패키지의 InvocationHandler 인터페이스의 메서드를 구현한 것으로 프록시를 통해 호출된 메서드를 처리하고 실제 결과값을 반환하는 메서드이다.
- 현재 상황에서 proxy 매개변수에는 shared EntityManager 프록시 객체가 할당되어 있고 method 매개변수에는 org.hibernate.Session.merge가 할당되어 있다.
- SharedEntityManagerInvocationHandler의 invoke() 메서드 내부에서 위의 로직을 통해 EntityManager가 target 변수에 할당된다.
- target에는 SessionImpl이라는 타입이 할당되어 있는데 org.hibernate.internal.SessionImpl 클래스는 Hibernate에서 영속성 컨텍스트를 나타내는 인터페이스인 Session의 구현체 중 하나이다.
- 주석처럼 이 부분에서 현재 엔티티 매니저인 target의 메서드를 호출한다. 여기서 매개변수인 args에는 파라미터의 정보(여기서는 entity)가 들어있다.(memberId, email, password 등)
- 내부로 들어가보자.
SessionImpl의 merge 메서드
- checkOpen() 메서드를 쭉 타고 들어가면 세션이나 엔티티 매니저가 닫혔는지 확인하는 간단한 로직이다.
- fireMerge 메서드로 들어가보자.
SessionImpl의 fireMerge 메서드
- 언뜻봐도 check 메서드를 제외하고 fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener(event, MergeEventListener::onMerge); 부분이 핵심 동작을 하는 부분 같아 보인다.
- fireEventOnEachListener 메서드로 들어가보자.
EventListenerGroupImpl의 fireEventOnEachListener 메서드
- 여기서 fire event란 이벤트를 발생시킨다는 뜻이다.
- 내부로 들어가보자.
DefaultMergeEventListener의 onMerge 메서드
- EntityCopyObserver와 MergeContext를 생성하여 onMerge 메서드를 다시 호출한다.
- original 변수에 엔티티를 할당하고 해당 엔티티가 프록시 객체임을 확인한다. 여기서는 실제 객체가 할당되어 있으므로 해당 로직은 스킵한다.
- 그 다음 else if 문에도 걸리지 않아서 entity에는 단순히 객체를 할당한다.
- copyCache의 size가 0이기 때문에 아래 else 로직을 타게 된다.
- EntityEntry는 엔티티의 상태를 추적하고 관리하는 인터페이스이다.
- 여기에선 entry가 null이 아니기 때문에 entityState는 DETACHED가 된다.
DefaultMergeEventListener의 entityIsDetached 메서드
- 메서드 코드 끝에 있는 이 부분이 merge 하는 부분으로 추정된다. 마지막 줄에 머지 결과를 MergeEvent에 세팅하는 부분을 볼 수 있다.
- 호출한 부분으로 돌아와서 이 메서드도 끝이 나서 다시 호출한 부분으로 돌아간다.
- entityCopyObserver와 mergeContext를 둘 다 clear 해준다.
- 호출한 부분으로 돌아온다.
- 여기서 fireEventOnEachListener의 두번째 파라미터가 onMerge였기 때문에 위의 accept 메서드가 onMerge를 호출한 것이다.
- 여기서 result는 Member 엔티티이기 때문에 단순히 리턴한다.
끝!
merge의 동작 방식
출처: 책 자바 ORM 표준 JPA 프로그래밍 3.6.5 1. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.(만약 없으면 db에서 엔티티를 조회하고 1차 캐시에 저장한다.)
2. 조회한 영속 엔티티에 준영속 엔티티의 값을 채워 넣는다. (모든 값이 변경된다.)
3. 영속 상태인 엔티티를 반환한다.
cf. 변경 감지
- 준영속 엔티티를 수정하는 방법에는 변경 감지와 병합이 있는데 변경 감지의 경우 원하는 속성만 값을 변경할 수 있다.
참고 자료: https://leveloper.tistory.com/37
[JPA] JPA 변경 감지와 병합(merge)
준영속 엔티티란? 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다. 임의로 만들어낸 엔티티라도 기존 식별자를 가지고 있는 경우(JPA가 식별할 수 있는 id를 가지고 있는 경우)에는 준영
leveloper.tistory.com
'개발 지식 > JPA' 카테고리의 다른 글
[JPA] 영속성 전이(CASCADE)와 고아 객체 정리 (0) 2023.11.30 [JPA] Referential integrity constraint violation (0) 2023.11.29 [JPA] JpaRepository 관련 코드 들여다보기 (0) 2023.11.28 [JPA] Entity 클래스의 @NoArgsConstructor(access = AccessLevel.PROTECTED) (0) 2023.11.07