[JPA] 영속성 컨텍스트와 엔티티 동일성(Identity) 보장 원리
토이 프로젝트 진행 중 궁금했던 JPA의 엔티티 동일성(==) 보장과 준영속(Detached) 상태에서 변경 감지(Dirty Checking)가 동작하지 않는 원인을 직접 테스트 코드로 검증하며 정리합니다.
문제 의식
토이 프로젝트에 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차 캐시에 영속화한 뒤 필드를 변경해야 합니다.

핵심 요약
- 영속 상태에서는 1차 캐시 덕분에 같은 트랜잭션 내 엔티티의 동일성(
==)이 100% 보장됩니다. em.clear()/em.detach()등으로 준영속 상태가 된 엔티티는 1차 캐시 스냅샷 추적이 끊기므로, 값을 변경해도 DB에 반영되지 않습니다.- 수정 작업 시에는 항상
repository.findById()등으로 엔티티를 영속화한 상태에서 도메인 메서드를 통해 값을 변경해야 합니다.
[프로그래머스] 양궁대회 (Level 2, Java) - 중복 조합 DFS와 동점 우선순위 큐
NEXT LOG →[JPA] Auditing @LastModifiedDate가 벌크성 쿼리에서 동작하지 않는 이유와 해결 방법