JPA 영속성 관리
영속성 컨텍스트
- 엔티티를 영구 저장하는 환경을 의미
- 논리적인 개념이며 눈에 보이지 않는다.
- 엔티티 매니저를 통해 영속성 컨텍스트에 접근할 수 있다.
엔티티의 생명주기
- 비영속(new/transient) : 영속성 컨텍스트와 관계 없는 상태
- 영속(managed) : 영속성 컨텍스트에 관리되는 상태
- 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed) : 삭제된 상태
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
1차 캐시
객체를 저장하고 조회할 경우 해당 데이터를 DB에서 찾아보기 전에 1차 캐시에서 조회하게 된다. 만약 1차 캐시에 존재하지 않는 데이터라면 DB에 접근하여 해당 데이터를 1차 캐시에 저장한 뒤, 그 값을 반환하게 된다.
애플리케이션 전체에서 공유하는 캐시는 2차 캐시라고 한다.
동일성 보장
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
트랜잭션을 지원하는 쓰기 지연
트랜잭션을 시작하고 persist()
를 통해 데이터를 DB에 저장한다고 하면, 내부적으로 INSERT SQL을 데이터베이스에 날리게 된다. 이 때 메서드가 사용될 때마다 한 번씩 SQL문이 전달되는 것이 아니라 해당 명령어들을 쓰기 지연 SQL 저장소에 저장해 놓은 뒤 커밋하는 순간 한 번에 DB로 모든 SQL문을 날리게 된다.
쓰기 지연 SQL 저장소와 같은 중간 계층이 생김으로써 추가적인 성능 개선이 된다.
변경 감지
트랜잭션이 커밋되는 시점에 flush()
가 발생한다. flush가 발생하면 우선 엔티티와 스냅샷을 비교한다. 엔티티와 스냅샷을 비교하여 변경된 부분이 있다면 쓰기 지연 SQL 저장소에 update 쿼리를 만들어 저장한다. 그리고 쓰기지연 SQL 저장소에 있는 update 쿼리가 실제 DB에 적용된다.
플러시
영속성 컨텍스트의 변경내용을 DB에 동기화한다. 이 때 영속성 컨텍스트는 비우지 않는다. 즉, 플러시가 발생해도 1차 캐시의 내용은 그대로 유지된다. 이러한 메커니즘이 가능한 이유는 트랜잭션 커밋 직전에만 동기화하면 되기 때문이다.
플러시 발생 시, 수행 동작
- 변경 감지
- 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송
영속성 컨텍스트에서 플러시가 수행되는 경우
entityManager.flush()
(직접 호출)- 트랜잭션 커밋 (자동 호출)
- JPQL 쿼리 실행 (자동 호출)
준영속
영속 상태를 전환하는 메서드
entityManager.detach(entity)
: 특정 엔티티만 준영속 상태로 전환entityManager.clear()
: 영속성 컨텍스트를 완전히 초기화entityManager.close()
: 영속성 컨텍스트를 종료
준영속 상태의 특징
- 영속성 컨텍스트가 관리하지 않으므로 1차 캐시, 쓰기 지연 등 어떠한 동작도 수행하지 않기 때문에 비영속 상태에 가깝다.
- 비영속 상태는 식별자 값이 없을 수도 있지만 준영속 상태는 반드시 식별자 값을 가진다.
- 지연 로딩을 할 수 없다.
지연 로딩은 실제 객체 대신 프록시 객체를 로딩하여 영속성 컨텍스트를 통해 데이터를 불러오는 방법
병합 merge()
준영속 상태의 엔티티를 다시 영속 상태로 변경하는 기능을 수행한다.
// member 엔티티의 name을 변경하여 DB에 반영하는 예
member.setName("홍길동");
entityManager.merge(member);
파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시 엔티티를 조회한다. 만약 1차 캐시에 엔티티가 없으면 DB에서 조회한다. DB에 값이 있으면 해당 값을 받아오고, 입력받은 member 객체의 값을 복사하여 DB에서 가져온 객체에 복사한다. 병합이 끝나면 트랜잭션 커밋을 통해 변경 감지 기능이 동작하여 변경 내용이 DB에 반영된다. 즉, SELECT 후 INSERT가 발생하는 방식이다.
merge()
는 비영속 엔티티도 영속 상태로 변경 가능하다. 이 때 새로운 엔티티를 생성해서 병합하게 된다.
Comments