DB를 하나만 사용하는 애플리케이션은 일반적으로 EntityManagerFactory를 하나만 생성한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hardy");
EntityManagerFactory는 여러 스레드가 동시에 접근해도 안전하므로 스레드 간에 공유해도 되지만, EntityManager는 스레드 간에 절대 공유하면 안된다.
EntityManager는 DB 연결이 필요한 시점까지 커넥션을 얻지 않는다.
- JPA를 이해하는데 가장 중요한 용어 (
Entity를 영구 저장하는 환경
이라는 뜻) - EntityManager.persist(entity);
- 영속성 컨텍스트는 논리적 개념 (눈에 보이지 않는다.)
- EntityManager를 통해 영속성 컨텍스트에 접근한다.
비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
Member member = new Member();
member.setId(1L);
member.setName("회원1");
영속(managed): 영속성 컨텍스트에 저장된 상태
Member member = new Member();
member.setId(1L);
member.setName("회원1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(member);
준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member); // 특정 엔티티만 준영속 상태로 전환
또는
em.clear(); // 영속성 컨텍스트 초기화
또는
em.close(); // 영속성 컨텍스트 종료
삭제(removed): 삭제된 상태
em.remove(member);
1차 캐시 & 동일성(identity) 보장
반복 가능한 읽기(REPEATABLE READ) 등급의 트랙잭션 격리 수준을 DB가 아닌 애플리케이션단에서 제공
Member a = em.find(Member.class, 1L);
Member b = em.find(Member.class, 2L);
System.out.println(a == b); // true
트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(a);
em.persist(b);
// 여기까지는 INSERT 쿼리를 DB에 보내지 않는다.
transaction.commit(); // 커밋하는 순간 DB에 쿼리를 보낸다.
변경 감지 (Dirty Checking)
JPA의 기본 전략은 Entity의 모든 필드 업데이트
@DynamicUpdate로 수정된 데이터만 동적으로 UPDATE하는 SQL 생성도 가능
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Member a = em.find(Member.class, 1L);
a.setName("Hardy");
a.setAge(30);
// em.update(a); 이런 코드가 없어도 된다.
transaction.commit();
// 1차 캐시를 만들 때 만들어둔 스냅샷과 비교하여 변경된 부분의 UPDATE 쿼리를 수행한다.
지연 로딩 (Lazy Loading)
영속성 컨텍스트의 변경 내용을 DB에 반영한다.
- 변경 감지가 동작하여 영속성 컨텍스트에 있는 모든 Entity를 스냅샷과 비교하고 수정된 Entity를 찾는다. 수정된 Entity는 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송한다.