Table of Contents
영속성 컨텍스트(persistence Context)의 개념
EntityManagerFactory와 EntityManager
EntityManagerFactory는 고객의 요청이 올 때마다 (스레드가 하나 생성될 때마다) EntityManager를 생성한다. EntityManager는 내부적으로 DB connection pool을 사용해서 DB에 접근한다.
EntityManagerFactory는 애플리케이션이 로딩되는 시점에 데이터베이스 당 딱 하나만 생성한다.
EntityManager는 실제 트랜잭션 단위가 수행될 때마다 생성되고, 클라이언트의 요청이 들어올 때 생성했다가 요청이 끝나면 닫는다. → 스레드 간에 공유를 하면 안된다.
영속성 컨텍스트
JPA를 이해하는데 가장 중요한 용어 - “엔티티를 영구 저장하는 환경”
영속성 컨텍스트는 논리적인 개념으로 엔티티 매니저를 통해 영속성 컨텍스트에 접근 가능하다.
엔티티 매니저로 Entity를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티리를 보관하고 관리한다.
1EntityManager.persist(entity);
persist() 메소드는 엔티티 매니저를 사용해서 Entity를 영속성 컨텍스트 안에 저장한다.
엔티티의 생성주기
![](/images/spring-persistence-context/1.png)
- 비영속 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속 (managed) : 영속성 컨텍스트에 관리되는 상태
- 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제 (removed) : 삭제된 상태
비영속
순수한 객체 상태이며 아직 저장하지 않은 상태 → 영속성 컨텍스트나 데이터베이스와는 관련 없음.
1// 객체를 실행한 상태
2Member member = new Member();
3member.setId("member1");
4member.setUsername("회원1");
영속
영속성 컨텍스트에 의해 관리되는 상태.
1EntityManager em = emf.createEntityManager();
2em.getTransaction().begin();
3//객체를 저장한 상태(영속)
4em.persist(member);
준영속, 삭제
1//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
2em.detach(member);
3
4//객체를 삭제한 상태(삭제)
5em.remove(member);
준영속 : 영속 상태의 엔티티가 영속성 컨텍스트에서 분리
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
- 식별자 값을 가지고 있다. - 한번 영속 상태였으므로
- 지연 로딩을 할 수 없다.
삭제 : 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제
준영속 상태로 만드는 방법
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환em.clear()
: 영속성 컨텍스트를 완전 초기화em.close()
: 영속성 컨텍스트를 종
영속성 컨텍스트의 특징
- 식별자 값 : 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다. (식별자 값이 없으면 예외 발생)
- 데이터베이스 저장 : 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 저장된 엔티티를 데이터베이스에 반영
영속성 컨텍스트의 이점
-
1차 캐시에서 조회 : 영속 컨텍스트에서 1차 캐시에서 데이터를 찾은 후 없으면 DB에서 조회한다.
-
영속 엔티티의 동일성(identity) 보장
: 반복 가능한 읽기 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공1Member a = em.find(Member.class, "member1"); 2Member b = em.find(Member.class, "member1"); 3System.out.println(a == b); //동일성 비교 true
-
엔티티를 등록할 때 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
: 커밋을 하는 순간에 INSERT가 가능, 버퍼링 기능1EntityManager em = emf.createEntityManager(); 2EntityTransaction transaction = em.getTransaction(); 3//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다. 4transaction.begin(); // [트랜잭션] 시작 5em.persist(memberA); 6em.persist(memberB); 7//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다. 8//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다. 9transaction.commit(); // [트랜잭션] 커밋
-
엔티티를 수정할 때 변경 감지(Dirty Checking)
: commit 시 엔티티와 스냅샷(값을 읽어온 최초 시점)을 비교하여 UPDATE 쿼리를 생성한다. (JPA의 기본 전략은 엔티티의 모든 필드를 업데이트한다.) -
지연 로딩(Lazy Loading)
flush
영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화한다.
⚠️ flush를 실행한다고 해서 영속성 컨텍스트 내용을 지우지 않는다.
트랜잭션이라는 작업 단위가 중요 - 커밋 직전에 동기화하면 된다.
영속성 컨텍스트를 flush하려면 세가지 방법이 있다.
- em.flush() : 직접 호출 (거의 사용하지 않음)
- 트랜잭션 commit : flush 자동 호출 - 트랜잭션만 commit하면 데이터베이스에 반영되지 않기 때문에 JPA는 자동으로 flush를 호출된다.
- JPQL 쿼리 실행 : flush 자동 호출