kimkkatta::LOG
Backend❯JPA2022-02-282 min read

[JPA] 영속성 컨텍스트와 엔티티 동일성(Identity) 보장 원리

토이 프로젝트 진행 중 궁금했던 JPA의 엔티티 동일성(==) 보장과 준영속(Detached) 상태에서 변경 감지(Dirty Checking)가 동작하지 않는 원인을 직접 테스트 코드로 검증하며 정리합니다.

#JPA 변경감지#JPA 엔티티 동일성#영속성 컨텍스트#준영속 상태

문제 의식

토이 프로젝트에 JPA를 적용하던 중 문득 이런 궁금증이 생겼습니다.

"DB에서 같은 id로 조회한 엔티티를 두 번 가져왔을 때, 자바 메모리상에서 같은 객체(==)일까? 만약 영속성 컨텍스트를 비우고 수정하면 어떻게 될까?"

직접 테스트 코드를 작성해 영속 상태와 준영속 상태에서의 동작 차이를 검증해 보았습니다.


1. 영속 상태에서의 동일성(==) 보장

먼저 Member 엔티티를 생성해 저장(persist)한 뒤, 같은 트랜잭션 안에서 해당 ID로 다시 find()를 호출해 참조 주소를 비교해 보았습니다.

같은 영속성 컨텍스트 내 엔티티 동일성 확인

결과는 **동일성(true)**이 보장됩니다. 같은 EntityManager의 1차 캐시에 저장되어 있던 동일한 인스턴스를 그대로 반환하기 때문입니다.


2. flush() & clear() 이후(준영속 상태)의 객체 비교

이번에는 저장 후 entityManager.flush()와 entityManager.clear()를 호출해 영속성 컨텍스트를 초기화한 뒤, 동일한 ID로 다시 조회해 비교해 보았습니다.

영속성 컨텍스트 초기화 후 객체 불일치 확인

clear()가 호출되면 기존 객체는 영속성 컨텍스트의 관리 대상에서 벗어나 준영속(Detached) 상태가 됩니다. 이후 find()를 호출하면 DB에서 새로 데이터를 조회해 새로운 자바 객체 인스턴스를 생성해 반환하므로, 이전 객체와 비교했을 때 참조 주소가 달라 불일치(false)가 발생합니다.


3. 준영속 상태에서는 변경 감지(Dirty Checking)도 동작하지 않는다

동일성이 보장되지 않는 준영속 상태의 객체는 영속성 컨텍스트의 추적을 받지 못합니다. 따라서 객체의 필드를 수정하더라도 트랜잭션 커밋 시 DB에 UPDATE 쿼리가 실행되지 않습니다.

준영속 상태 엔티티 필드 수정 시 미반영

위 테스트처럼 clear() 이후 기존 참조 변수(member)의 주소 필드를 변경했음에도 DB에는 반영되지 않는 것을 확인할 수 있습니다.


해결 방법: 영속화 후 수정하기

변경 감지(Dirty Checking)를 통해 엔티티를 안전하게 수정하려면 반드시 **영속 상태(Managed)**여야 합니다.

따라서 영속성 컨텍스트를 벗어난 상태에서 수정이 필요한 경우에는 find()로 엔티티를 다시 조회하여 1차 캐시에 영속화한 뒤 필드를 변경해야 합니다.

find로 재조회 영속화 후 변경 감지 정상 동작


핵심 요약

  1. 영속 상태에서는 1차 캐시 덕분에 같은 트랜잭션 내 엔티티의 동일성(==)이 100% 보장됩니다.
  2. em.clear() / em.detach() 등으로 준영속 상태가 된 엔티티는 1차 캐시 스냅샷 추적이 끊기므로, 값을 변경해도 DB에 반영되지 않습니다.
  3. 수정 작업 시에는 항상 repository.findById() 등으로 엔티티를 영속화한 상태에서 도메인 메서드를 통해 값을 변경해야 합니다.
COMMENTS // GITHUB DISCUSSIONS (GISCUS)