JPA의 동작 방식
- EntityManagerFactory에서 고객 요청에 대한 EntityManager 생성
Request1 ➡️ EntityManagerFactory에서 EntityManager1 생성
Request2 ➡️ EntityManagerFactory에서 EntityManager2 생성
영속성 컨텍스트란?
- Entity Manager = 영속성 컨텍스트
- Entity 객체들을 관리 및 영구 저장하는 환경(논리적인 영역)
- 영속성 컨텍스트의 역할
- Entity 객체의 생명주기 관리
- 데이터베이스와의 상호작용을 추상화 ➡️ 개발자가 DB와 직접 상호작용 할 필요가 없음
- Entity의 영속성을 보장
- Entity 객체의 변경을 감지
- DB와 동기화 담당
- EntityManager를 통해 영속성 컨텍스트에 접근
- EntityManager.persist(ENTITY)를 통해 Entity 객체를 영속성 컨텍스트에 저장
영속성 컨텍스트의 내부 구조
영속성 컨텍스트(EntityManager)는 아래의 내부 공간을 가진다.
- 쓰기 지연 SQL 저장소
- 1차 캐시 (key-value+value2)
- key = id
- value1 = Entity Object (변경이 일어나면 요 값을 변경)
- value2 = Entity Object의 스냅샷 (영속성 컨텍스트에 들어온 최초 시점의 상태 저장)
-
id(key) entity(value1) snapshot(value2) "memberA" memberA Obj memberA Obj'snapshot
Entity의 생명주기
- 비영속 상태 (New / Transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 객체를 생성만 한 상태
-
Member member = new Member().builder() .setId("member1") .setUserName("회원1")
- 영속 (Managed)
- 영속성 컨텍스트에 관리되는 상태
- DB에는 반영 ❌ (Transection이 commit되는 시점에 반영)
- EntityManager.persist()
-
EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); em.persist(member); //Member 객체가 영속화됨
- 준영속 (Detached)
- 영속성 컨텍스트에 저장되었다 분리된 상태
- EntityManager.detach()
-
... em.persist(member); em.detach(member);//영속화된 Member를 영속성 컨텍스트에서 지움
- 삭제 (removed)
- DB에서 영구 저장된 Entity가 삭제됨
-
em.remove(member); //DB에서 삭제됨
영속성 컨텍스트에 대한 이점(2023.05.12)
- 1차 캐시
- 영속성 컨텍스트 **내부에 1차 캐시가 존재**
- 내부 1차 캐시에 Id(key) - Entity(Value)로 Id값-객체로 매핑되어 저장됨 ➡️ em.find()로 객체로 꺼낼 수 있다!
- em.persist 후 객체를 조회하면 1차캐시에서 바로 꺼내옴
- em.persist(member); //1차 캐시에 저장됨 em.find(Member.class, "member1"); //DB에 접근할 필요없이, 1차 캐시에서 바로 Member1 꺼내옴
- 1차 캐시에 없을 경우에만 DB에서 직접 조회한다.
- 동일성(Identity)을 보장
-
Member find1 = em.find(Member.class, 101L); Member find2 = em.find(Member.class, 101L); System.out.println("result = " + (find1 == find 2)); // output : "result = True"
-
- 트랜잭션을 지원하는 쓰기 지연 Transactional Write-Behind
- em.persist(obj) 하는 시점에 DB에 Insert SQL 실행 ❌
**tx.commit() 하는 시점에 DB에 Insert SQL 실행**! - ... em.persist(member1); //DB에 반영(Insert SQL)❌
tx.commit(); //DB에 반영(Insert SQL) - em.persist() ➡️ **1차 캐시에 Entity 적재** + **쓰지 지연 SQL 저장소에 Insert SQL 생성해 적재**
- tx.commit() ➡️ 쓰지 지연 SQL에 적재되어 있던, Insert SQL이 실행됨(flush).
- 쓰기 지연 SQL에 적재되는 양 ➡️ hibernate 설정으로 정함
- <property name = "hibernate.jdbc.batch_size" value = "10"> </property> //10만큼 적재
- 장점
- 버퍼링 효과를 얻을 수 있다. Commit 될 때마다 Insert SQL을 실행할 경우, 계속 DB와 트랜잭션이 생겨 비효율적인데, 모아서 한번에 배치 실행할 경우, 한번의 연결로 여러 Insert SQL을 실행할 수 있어 성능이 향상된다!
- em.persist(obj) 하는 시점에 DB에 Insert SQL 실행 ❌
- **변경 감지 Dirty Checking**
- 아래와 같은 상황에서 바뀐 member의 이름을 DB에 반영하기 위해, em.persist(member)를 실행시켜야 할까?정답은 NO!
즉, 자바의 컬렉션처럼 사용하면 된다! - JPA에서 Entity의 데이터 변경을 감지하여 반영한다.
- 아래와 같은 상황에서 바뀐 member의 이름을 DB에 반영하기 위해, em.persist(member)를 실행시켜야 할까?정답은 NO!
Member member = em.find(Member.class, 101L);
member.setName("ZXC"); //member의 이름 수정
//em.persist(member); //해야하나? NO!
- 작동 방식
- flush()
- 영속성 컨텍스트의 1차 캐시에서 Entity와 Entity의 스냅샷을 비교.
- 변경에 대한 UPDATE SQL 생성 후 "쓰기 지연 SQL 저장소"에 적재
- DB에 반영(flush)
- 중요‼️ JPA를 쓰는 이유 = DB를 자바 컬렉션처럼 다루기 위해...
- 지연 로딩 Lazy Loading
- Member member = em.find(Member.class, 20L); em.remove(member); // 20L member 삭제됨
플러시 Flsuh
영속성 컨텍스트의 변경 내용과 DB의 내용을 맞추는 작업(영속성 컨텍스트 ➡️ DB에 동기화)
❇️ **트랜잭션이라는 작업 단위가 중요하다**
플러시가 발생할 때 일어나는 일들
- 변경 감지 (Dirty Checking)
- 수정된 Entity를 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 DB에 반영(등록, 수정, 삭제 SQL)
- **영속성 컨텍스트를 비우지는 않는다.**
영속성 컨텍슽트를 플러시 하는 법
- 자동 호출
- 트랜잭션 커밋
- JPQL 쿼리 실행
- ex) **em.createQuery**("select m from Member ...");
- 직접 호출
- em.flush() : 자주 사용하지 않음 (보통 테스트할 때 사용함)
플러시 관련 모드 옵션
굳이 변경할 필요는 없다...
em.setFlushMode(원하는 모드 옵션);
1. 커밋이나 쿼리를 실행할 때, 플러시 (Default)
em.setFlushMode(FlushModeType.AUTO);
2. 커밋할 때만 플러시
em.setFlushMode(FlushModeType.COMMIT);
준영속 상태
영속 상태의 Entity가 영속성 컨텍스트에서 분리된 상태
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못함.
- Update 관련 기능, dirty checking 등...
준영속 상태로 전환 방법
- em.detach(entity);
- **특정 엔티티**만 준영속 상태로 전환
Member member = em.find(Member.class, 1L); member.setName("A"); em.detach(member); //member만 영속성 컨텍스트에서 제거 tx.commit(); //member db 반영❌
- em.clear();
- 영속성 컨텍스트를 완전히 **모두 초기화**
- Member member1 = em.find(Member.class, 1L); Member member2 = em.find(Member.class, 2L); member1.setName("A"); member2.setName("B"); em.clear(); //영속 컨텍스트를 모두 초기화. tx.commit(); //member1, member2 모두 db 반영❌
- em.close();
- 영속성 컨텍스트를 종료
강의 출처
https://www.inflearn.com/course/lecture?courseSlug=ORM-JPA-Basic
'JPA' 카테고리의 다른 글
프록시 (0) | 2023.05.23 |
---|---|
JPA - 연관관계 주인(feat. mappedBy) (0) | 2023.05.19 |
자바의 트랜잭션 (0) | 2023.05.09 |
엔티티 매니저와 영속성 컨텍스트 정리 (0) | 2023.05.01 |
1. JPA의 등장 (0) | 2023.04.26 |