Persistence Context(영속성 컨텍스트)
엔티티를 영구 저장하는 환경
생명 주기

비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속(managed): 영속성 컨텍스트에 관리되는 상태
준영속(detached): 영속성 컨텍스트에 저장되어있다가 분리된 상태
삭제(removed): 삭제된 상태
영속성 컨텍스트 특징
1차 캐시
entityManager.persist(member)
처럼 엔티티를 영속성 컨텍스트에 저장하면, 영속성 컨텍스트는 엔티티를 1차 캐시에 저장하고, 식별자 키를 통해 엔티티를 관리한다.
1
Member(id=1, username="회원1")
위 상태에서 entityManager.find(Member.class, 1)
을 호출하면, 데이터베이스에서 조회하기 전에 1차 캐시에서 조회하고, 없으면 데이터베이스에서 조회한다.
마찬가지로 데이터베이스에 없어서 조회한 경우에도 그 결과를 1차 캐시에 저장한다.(캐시를 통해 조회하게 되는 경우 DB 조회 쿼리를 실행하지 않음)
동일성(identity) 보장
1차 캐시를 통해 조회하면, 동일한 트랜잭션에서 조회한 엔티티는 같은 인스턴스를 반환하기 때문에 동일한 인스턴스를 반환한다.
트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
persist(*)
를 호출하면, 엔티티를 1차 캐시에 저장하고, 그와 동시에 INSERT SQL을 생성하고 바로 데이터베이스에 보내지 않고, 쓰기 지연 SQL 저장소에 저장하게 된다.
그리고 트랜잭션을 커밋하면, 쓰기 지연 SQL 저장소에 저장된 쿼리를 데이터베이스에 보내게 된다.
변경 감지(Dirty Checking)
트랜잭션을 커밋(flush)하면, 1차 캐시에 있는 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾고, UPDATE SQL을 생성해서 데이터베이스에 보낸다. 비슷하게 엔티티 삭제하는 경우에도, 1차 캐시에 있는 엔티티와 스냅샷을 비교해서 삭제 쿼리를 생성해서 데이터베이스에 보낸다.
플러시(Flush)
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것으로, 아래와 같은 절차로 진행된다.
변경 감지
수정된 엔티티 쓰기 지연 SQL 저장소에 등록
쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
플러시는 기본적으로 트랜잭션 커밋을 호출하면 자동 호출되고, 아래의 상황에서 플러시가 호출된다.
1. 트랜잭션 커밋
2. JPQL 쿼리 실행
해당 트랜잭션 내에서 반영 된 변경 내용을 JPQL 쿼리를 통해 조회하려고 할 때, 플러시가 호출된다.
3. 기본 키 생성을 데이터베이스에 위임하는 IDENTITY
전략 사용 시 persist()
호출 시점
IDENTITY
전략 사용 시 persist()
호출 시점1차 캐시에 저장하고 영속성으로 관리하기 위해선 PK가 필요하기 때문에 데이터베이스에 저장하고 PK를 가져올 수 있도록 플러시를 호출한다.
4. entityManager.flush()
메서드 직접 호출(거의 사용하지 않으나 테스트에서 사용)
entityManager.flush()
메서드 직접 호출(거의 사용하지 않으나 테스트에서 사용)플러시를 직접 호출한다고 해서 영속성 컨텍스트를 비우는 것은 아니며, 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 것이다.
플러시 모드 옵션
FlushModeType.AUTO
: 커밋이나 쿼리를 실행할 때 플러시(기본값)FlushModeType.COMMIT
: 커밋할 때만 플러시
준영속 상태
영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 상태를 말하며, 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
준영속 상태로 만드는 방법
entityManager.detach(entity)
: 특정 엔티티만 준영속 상태로 전환entityManager.clear()
: 영속성 컨텍스트를 완전히 초기화entityManager.close()
: 영속성 컨텍스트를 종료
병합(merge)
준영속 상태의 엔티티를 영속 상태로 변경하는 기능으로 동작 방식은 아래와 같다.
준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티 조회
만약 1차 캐시에 없으면 데이터베이스에서 조회 후 1차 캐시에 저장
조회한 영속 엔티티(mergeItem)에 준영속 엔티티(itemParam)의 값을 채워넣어 변경
영속 엔티티(mergeItem)를 반환
여기서 준영속 엔티티의 값을 모두 교체하기 때문에 null
로 업데이트 될 위험이 있어 사용하지 않는 것이 좋다.
때문에 merge()
를 통한 변경보다는 변경 감지 기능을 사용하여 변경하는 것이 좋다.
참고자료
Last updated
Was this helpful?