본문 바로가기

JPA

영속성 컨텍스트!

JPA의 동작 방식

  1. EntityManagerFactory에서 고객 요청에 대한 EntityManager 생성

Request1 ➡️ EntityManagerFactory에서 EntityManager1 생성

Request2 ➡️ EntityManagerFactory에서 EntityManager2 생성

영속성 컨텍스트란?

  • Entity Manager = 영속성 컨텍스트
  • Entity 객체들을 관리 및 영구 저장하는 환경(논리적인 영역)
  • 영속성 컨텍스트의 역할
    1. Entity 객체의 생명주기 관리
    2. 데이터베이스와의 상호작용을 추상화 ➡️ 개발자가 DB와 직접 상호작용 할 필요가 없음
    3. Entity의 영속성을 보장
    4. Entity 객체의 변경을 감지
    5. DB와 동기화 담당
  • EntityManager를 통해 영속성 컨텍스트에 접근
    • EntityManager.persist(ENTITY)를 통해 Entity 객체를 영속성 컨텍스트에 저장

영속성 컨텍스트의 내부 구조

영속성 컨텍스트(EntityManager)는 아래의 내부 공간을 가진다.

  1. 쓰기 지연 SQL 저장소
  2. 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차 캐시가 존재**
    • 내부 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에서 직접 조회한다.
  2. 동일성(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"
  3. 트랜잭션을 지원하는 쓰기 지연 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을 실행할 수 있어 성능이 향상된다!
  4. **변경 감지 Dirty Checking**
    • 아래와 같은 상황에서 바뀐 member의 이름을 DB에 반영하기 위해, em.persist(member)를 실행시켜야 할까?정답은 NO!
      즉, 자바의 컬렉션처럼 사용하면 된다!
    • JPA에서 Entity의 데이터 변경을 감지하여 반영한다.
Member member = em.find(Member.class, 101L);
member.setName("ZXC"); //member의 이름 수정

//em.persist(member); //해야하나? NO!
  • 작동 방식
    1. flush()
    2. 영속성 컨텍스트의 1차 캐시에서 Entity와 Entity의 스냅샷을 비교.
    3. 변경에 대한 UPDATE SQL 생성 후 "쓰기 지연 SQL 저장소"에 적재
    4. DB에 반영(flush)
  1. 중요‼️ JPA를 쓰는 이유 = DB를 자바 컬렉션처럼 다루기 위해...
  2. 지연 로딩 Lazy Loading
  3. Member member = em.find(Member.class, 20L); em.remove(member); // 20L member 삭제됨

플러시 Flsuh

영속성 컨텍스트의 변경 내용과 DB의 내용을 맞추는 작업(영속성 컨텍스트 ➡️ DB에 동기화)

❇️ **트랜잭션이라는 작업 단위가 중요하다**

플러시가 발생할 때 일어나는 일들

  1. 변경 감지 (Dirty Checking)
  2. 수정된 Entity를 쓰기 지연 SQL 저장소에 등록
  3. 쓰기 지연 SQL 저장소의 쿼리를 DB에 반영(등록, 수정, 삭제 SQL)
  4. **영속성 컨텍스트를 비우지는 않는다.**

영속성 컨텍슽트를 플러시 하는 법

  1. 자동 호출
    1. 트랜잭션 커밋
    2. JPQL 쿼리 실행
    3. ex) **em.createQuery**("select m from Member ...");
  2. 직접 호출
    1. em.flush() : 자주 사용하지 않음 (보통 테스트할 때 사용함)

플러시 관련 모드 옵션

굳이 변경할 필요는 없다...

em.setFlushMode(원하는 모드 옵션);

1. 커밋이나 쿼리를 실행할 때, 플러시 (Default)
em.setFlushMode(FlushModeType.AUTO);

2. 커밋할 때만 플러시
em.setFlushMode(FlushModeType.COMMIT);

준영속 상태

영속 상태의 Entity가 영속성 컨텍스트에서 분리된 상태

  • 영속성 컨텍스트가 제공하는 기능을 사용하지 못함.
    • Update 관련 기능, dirty checking 등...

준영속 상태로 전환 방법

  1. em.detach(entity);
    • **특정 엔티티**만 준영속 상태로 전환
    Member member = em.find(Member.class, 1L);
    member.setName("A");
    
    em.detach(member); //member만 영속성 컨텍스트에서 제거
    
    tx.commit(); //member db 반영❌
  2. 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 반영❌
  3. 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