3장 영속성 관리
2장 JPA 시작은 초기 설정 관련 내용 이기에 스킵한다.
EntityManagerFactory
EntityManager 를 관리하는 곳
Persistence 객체가 설정 정보를 조회, 바탕으로 만들어진다.
설정 정보에 명시된 DB를 연결, 클라이언트 요청이 들어오면 커넥션 풀에 커넥션을 확인 후 EntityManager 생성, 커넥션과 연결해준다.
여러 스레드에서 접근, 공유 가능하다.
보통 DB 하나 당 하나만 생성, 공유
EntityManager
말 그대로 Entity (객체)를 관리하고 기능을 수행하는 역할이다.
객체를 관리하는 작은 DB라고 이해했다.
EntityManagerFactory 에서 생성되며 각 트랜잭션 (요청 쓰레드) 하나당 하나 씩만 사용된다.
EntityManagerFactory 와 는 다르게 공유, 재사용이 불가 (커넥션 관련 동시성 문제 때문)
실제 DB와 커넥션을 가지고 트랜잭션을 수행한다. 고로 공유될수없다.

EntityTransaction
조회를 제외한 거의모든 DB의 접근하는 로직은 EntityTransaction 안에서 수행되어야 한다.
만약 이렇게 처리 되지 않은 명령들은 TransactionRequiredException을 일으킨다.
EntitiyManager 가 얻을수있으며 트랜잭션 begin ~ commit 사이에서 DB 접근이 가능하다.
Persistence Context (영속성 컨텍스트)
Entity 를 영구히 저장하는 환경이라 칭하지만 필자는 EntityManager를 통해 em.persist(Entity) ~ em.detached 하기 전 까지는 영구히 저장하는 환경이라 이해했다.
애플리케이션 ~ DB 사이에서 객체를 보관하는 가상의 DB 역할
EntityManager 통해 객체를 저장하거나 조회하면 영속성 컨텍스트에 저장함
Entity 의 생명주기
비영속
영속성 컨텍스트에 영속되지 않은 상태를 의미
영속
em.persist(), em.find(), JPQL로 조회한 엔티티 등이 영속 상태에 속한다.
JPQL 로 조회하면 영속 상태에 속하게됨
** JPQL 은 조회 시 먼저 DB 에 접근하여 데이터를 가져오고 해당 데이터가 영속성 컨텍스트에 존재한다면 가져온 데이터를 날리고 영속성 컨텍스트에 있는 데이터를 사용한다.
준영속
em.detach : 특정 엔티티를 준영속 상태로 전환한다.
em.close (영속성 컨텍스트 종료), em.clear(영속성 컨텍스트 초기화) 를 할 경우 해당 영속성 컨텍스트에 있던 엔티티는 준영속 상태가 된다.
영속 상태에 있다가 영속상태가 종료된 상태를 의미
비영속에 가까우며 영속성 컨텍스트가 제공하는 기능 사용 X
식별자값 보유 (이미 한번 영속 됬었기 때문)
삭제
em.remove(entity) : 엔티티를 DB, 영속성 컨텍스트 에서 삭제한다.

영속성 컨텍스트의 특징
영속성 컨텍스트는 식별자 값을 가지고 있다. 흔히 @Id를 통해 테이블과 매핑한 값이다.
Map 형태로 되어있고 식별자값 : Entity(인스턴스) 같은 형식으로 이루어져있다.
장점
1차 캐시 사용
em.find(entity) 를 호출하면 1차 캐시 (영속성 컨텍스트) 를 조회해서 객체가 없다면 DB를 조회해 객체를 가져온뒤 1차 캐시에 보관한다. 만약 1차 캐시 안에 있다면 그 엔티티를 가져온다.
후에 조회 시에는 1차 캐시를 통해서만 조회하기에 이점을 드러낸다.
반면에 위에 써놨듯이 JPQL은 호출 시 1차캐시가 아닌 DB를 우선으로 조회한다. SQL 문으로 바꿔서 호출하기 때문이다.
동일성 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b) // 동일성 보장두 조회문 모두 1차 캐시, 즉 영속성 컨텍스트에서 가져오기에 동일성이 보장된다.
변경 감지 (Dirty Check)
영속성 컨텍스트에서 엔티티를 조회 한 뒤 객체.SET() 을 이용해 수정하는 로직이 있다.
이때 조회한 엔티티를 업데이트하는 코드가 없어도 트랜잭션을 커밋한다면 Entity Manager 내부에서 플러시를 호출한다.
영속성 컨텍스트가 기존 *스냅샷 과 수정된 1차 캐시를 비교하여 자동으로 UPDATE에 해당하는 SQL을 생성해준다. (영속 상태의 엔티티에만 적용)
(그래서 인지 JPA에는 따로 업데이트 쿼리메소드가 없다. 필자도 업데이트 관련 로직은 따로 set 메서드를 만들어 사용했다.)
** 스냅샷이란 영속성 컨텍스트가 엔티티를 저장할때, 컬렉션으로 또 다른 복사본을 저장하게 되는것을 의미한다.
(주의) JPA 의 기본전략은 엔티티의 모든 필드를 업데이트 한다는것이다. 내가 수정한 컬럼 뿐 아니라 모든 컬럼을 업데이트 하는것 고로 DB에 보내지는 데이터 양이 증가한다. 하지만 수정 쿼리가 항상 같아서 미리 생성해두거나 이전에 한번 파싱한 쿼리는 재사용이 가능하다.
지연로딩 ( Lazy Loading)
연관 관계가 있는 엔티티 조회 시 일단 연관 관계에 있는 엔티티는 프록시 객체로 반환하고, 실제로 사용할때 쿼리를 날려 가져오는 기능이다.
플러시 (Flush)
영속성 컨텍스트에 있는 변경내용을 DB에 반영한다.
변경감지가 동작하고 수정 상황을 스냅샷과 비교하여 업데이트 쿼리를 생성 한 뒤 지연로딩 SQL 저장소에 보내고 해당 지연로딩 SQL 저장소가 쿼리를 DB로 보내는 일련의 작업을 시작하는 명령어이다.
플러시를 사용하는 방법
em.flush() 호출
트랜잭션 커밋 시
JPQL 쿼리 실행
Merge()
준영속 상태의 엔티티를 가져와서 해당값을 영속 상태의 엔티티에 넣은 뒤 반환한다.
준영속 상태의 엔티티의 식별자값을 가지고 1차캐시 ~ DB를 조회 한 뒤 준영속 상태의 엔티티의 값을 조회한 영속 상태의 엔티티에 담아서 반환 한다. Save or Update 기능이라고 할수있다.
정리
EntityFactoryManager 에서 EntityManager가 만들어지며 EntityManager는 영속성 컨텍스트를 가진다.
EntityFactoryManager 는 DB 당 하나이며 공유가 가능하지만 EntityManager 는 요청 당 하나이며 공유가 불가능하다.
영속성 컨텍스트에는 다양한 기능을 제공해주며 엔티티의 생명주기 상태에 따라 해당 기능의 적용 여부가 달라진다.
Last updated