💡 영속성 컨텍스트란?
영속성 컨텍스트(Persistence Context)는 Entity를 영구 저장하는 환경으로,
애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다.
- JPA는 EntityManager, 영속성 컨텍스트를 통해 데이터의 상태 변화를 감지하고 상황에 맞는 쿼리를 수행한다.
- EntityManager에 Entity를 저장하거나 조회하면 EntityManager는 영속성 컨텍스트에 Entity를 보관하고 관리한다.
* EntityManager
- EntityManager는 JPA에서 제공하는 인터페이스로, 영속성 컨텍스트 내에서 Entity를 관리한다.
💡 Entity 생명주기
Entity의 생명주기에는 4가지 상태가 있다.
✔️ 비영속 상태 (new / transient)
- 영속성 컨텍스트와 전혀 관계가 없는 상태로, Entity 객체를 생성했지만 영속성 컨텍스트에 저장하지 않은 상태
- 순수 객체
User user = new User();
✔️ 영속 상태 (managed)
- 영속성 컨텍스트에 저장되어 Entity가 영속성 컨텍스트에 의해 관리되는 상태
- 영속 상태가 되었다고 바로 DB에 저장되는 것이 아닌, 변경 사항이 Transaction Commit 하는 시점에 저장된다.
- 이때, JPA는 영속성 컨텍스트에 쌓아둔 변경 사항을 데이터베이스에 반영하기 위한 SQL 쿼리를 생성하고 실행한다.
EntityManager entityManager = EntityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
User user = new User();
entityManager.persist(user); // 객체를 영속성 컨텍스트에 저장
✔️ 준영속 상태 (detached)
- 영속성 컨텍스트에 저장되어 관리되던 상태에서 분리된 상태 (더 이상 관리하지 않는 상태)
- 특징 : 영속성 컨텍스트가 제공하는 어떠한 기능(Ex. 1차 캐시, 쓰기 지연, ...)도 동작하지 않는다, 식별자 값을 가지고 있다.
entityManager.detach(user); // 객체를 영속성 컨텍스트에서 분리
entityManager.clear(); // 영속성 컨텍스트 초기화
entityManager.close(); // 영속성 컨텍스트 종료
entityManager.merge(user); // 준영속 -> 영속
- detach(entity) : 특정 entity만 영속성 컨텍스트에서 분리하여 준영속 상태로 만들며, 다른 엔티티는 여전히 영속성 컨텍스트에서 관리
- clear() : 영속성 컨텍스트를 초기화하여 모든 엔티티를 분리하고 준영속 상태로 전환
- close() : 영속성 컨텍스트를 종료하여 모든 엔티티를 분리하고 준영속 상태로 전환
- merge(entity) : 준영속 상태의 Entity를 영속 상태로 전환
✔️ 삭제 (removed)
- 영속성 컨텍스트와 데이터베이스에서 해당 Entity를 삭제하여 삭제된 상태
entityManager.remove(user); // user 삭제
💡 영속성 컨텍스트 특징
- EntityManager를 생성할 때 영속성 컨텍스트가 생성된다. (1:1)
- EntityManager를 통해 해당 영속성 컨텍스트에 접근/관리 할 수 있다.
- Entity를 식별자 값(@id)으로 구분한다.
- 영속성 상태에는 반드시 식별자 값이 있어야 한다.
- 트랜잭션을 Commit 하는 시점에 영속성 컨텍스트의 변경 사항이 DB에 반영된다. -> 플러시(Flush)
💡 영속성 컨텍스트 장점
1. 1차 캐시
1차 캐시는 영속성 컨텍스트 내부에 존재하는 캐시(Chache)를 의미한다.
영속 상태의 Entity가 1차 캐시에 저장된다.
- 캐시는 Map 형태로 구성되어 있다. Key는 @Id로 매핑한 식별자, Value는 Entity 인스턴스이다.
- 식별자 값은 데이터베이스 기본키와 매핑된다.
- 영속성 컨텍스트에서 데이터를 저장하고 조회하는 모든 기준은 DB 기본 키 값이다.
- 조회 과정
- 1차 캐시에서 해당 Key에 대한 Entity가 존재하는지 조회한다.
- 존재하면, DB를 조회하지 않고 메모리에 있는 1차 캐시에서 해당 Entity를 조회한다.
- 존재하지 않으면, DB에서 조회 후, 해당 Entity를 1차 캐시에 저장하여 영속 상태로 만들고 반환한다.
2. 동일성(Identity) 보장
영속성 컨텍스트는 영속 Entity의 동일성을 보장한다.
동일성은 값뿐만 아니라 실제 인스터스가 동일하다는 것을 의미한다.
User a = entityManager.find(User.class, "1");
User b = entityManager.find(User.class, "1");
System.out.print(a == b); // true
동일성이 보장되어 Id 값이 '1'인 user를 2번 조회한 결과의 동일성을 비교했을 때 true가 출력되는 것을 확인할 수 있다.
3. 트랜잭션을 지원하는 쓰기 지연
EntityManager는 트랜잭션을 Commit 하기 전까지 변경 사항을 DB에 반영하지 않는다.
EntityManager 내부에 존재하는 SQL 저장소에 INSERT SQL을 별도로 저장하고,
트랜잭션이 Commit 되는 시점에 SQL 저장소에 있는 모든 INSERT SQL을 DB에 날린다.
이를 쓰기 지연이라고 한다.
- 트랜잭션이 Commit 될 때 EntityManager는 flush()를 실행한다.
- flush() : 1차 캐시를 지우지 않고 쿼리를 날려 DB와 동기화하는 역할을 한다.
- 트랜잭션 Commit 요청 -> flush() 실행 -> DB 쿼리 요청(동기화) -> DB Commit
4. 변경 감지(Dirty Checking)
변경 감지(Dirty Checking)는 영속성 컨텍스트 내부의 영속 상태 Entity를 대상으로,
Entity의 수정이 발생되었을 때, 영속성 컨텍스트에 따로 알려주지 않아도 알아서 변경 사항을 감지하여 반영해주는 것을 의미한다.
- 1차 캐시에 Entity를 저장할 때, 스냅샷 필드를 따로 저장하여 트랜잭션 Commit 요청이 왔을 때 해당 Entity와 스냅샷을 비교해서 변경 사항이 있으면 알아서 UPDATE SQL을 생성하여 DB에 날린다.
- Dirty Checking 과정
- 트랜잭션 Commit 요청 후, flush() 실행
- Entity와 스냅샷을 비교하여 변경된 Entity를 찾는다.
- 변경된 Entity가 존재하면, UPDATE 쿼리를 생성해서 쓰기 지연 SQL 저장소에 저장한다.
- 쓰기 지연 SQL 저장소에 있는 SQL을 flush() 한다.
- DB Commit을 한다.
💡 플러시 (Flush)
플러시(Flush)는 영속성 컨텍스트의 변경 사항을 DB에 반영하는 역할을 한다.
영속성 컨텍스트를 지우는 것이 아닌(1차 캐시를 지우지 않음), 변경 사항을 DB와 동기화하는 것이다.
✔️ flush() 방법
- flush() 호출
- 트랜잭션 Commit 시, 자동 호출
- JPQL 쿼리 실행 시, 자동 호출
출처
긴 글 읽어주셔서 감사합니다 🍀
잘못 작성된 내용은 피드백 주시면 반영하겠습니다 😎
'JPA' 카테고리의 다른 글
[JPA] Hibernate Dialect(방언) (0) | 2024.01.02 |
---|---|
[JPA] Hibernate 1차 캐시와 2차 캐시 (0) | 2023.12.27 |
[JPA] ORM과 JPA (0) | 2023.12.18 |