Study/Spring

영속성 컨텍스트

Jedo0224 2024. 10. 23. 15:51

강의 링크

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런





영속성 컨텍스트

  • JPA를 이해하는데 가장 중요한 용어, 논리적인 개념이다. (눈에 보이지 않는다)
  • 엔티티를 영구 저장하는 환경.
  • EntityManager를 통해 영속성 컨텍스트에 접근한다
    • EntityManager와 영속성 컨텍스트가 1:1 매핑을 한다.
    • EntityManager.persist(entity)
    • persist = 영속(지속하다)
      • 엔티티를 DB가 아니라 “영속성 컨텍스트”에 저장한다는 의미.





엔티티의 생명주기

  • 비영속 (new/transient)
    • transient - 과도 현상 : 정상 상태가 될 때까지의 사이에 생기는 현상.
    • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태



  • 영속 (managed)
    Untitled
    • em.persist(member); 이때 바로 데이터 베이스에 저장되는 것이 아니다!
    • Untitled 1
  • EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); System.out.println("====BEFORE====") em.persist(member); // System.out.println("====AFTER====")
  • 영속성 컨텍스트에 관리되는 상태



  • 준영속 (detached)
      em.detach(member)
  • 영속성 컨텍스트에 저장되었다가 분리된 상태. (아무 관계가 없어지는 상태)



  • 삭제 (remove)
  • 삭제된 상태

영속성 컨텍스트의 이점

  1. 1차 캐시
  2. 동일성(identiy) 보장
  3. 트랜잭션을 지원하는 쓰기 지연 (transcational write-behind)
  4. 변경 감지 (Dirty Checking)
  5. 지연 로딩 (Lazy Loading)





1차 캐시에서 조회

  • find(조회)시 DB 접근으로 가져오는 것이 아니라 1차로 캐시에서 가져온다.
    • 1차 캐시에 없다면 → DB에서 조회 후 → 1차 캐시에 저장한다.
      System.out.println("====BEFORE====");
            em.persist(member); //
            System.out.println("====AFTER====");
      
            Member findMember = em.find(Member.class , 101L);
      
            System.out.println("findMember.id = " + findMember.getId());
            System.out.println("findMember.namw = " + findMember.getName());
      Untitled 3
    • Untitled 2
    • 먼저 1차 캐시에서 값을 찾고 쿼리문이 나가는 것을 확인할 수 있음.
      Untitled 4
    • Member findMember1 = em.find(Member.class , 101L); Member findMember2 = em.find(Member.class , 101L);
    • 똑같은 엔티티 조회시 쿼리문이 1번만 작용하게 된다.
  • 영속성 컨텍스트는 하나의 트랜잭션안에서 작용된다. 따라서 트랜잭션이 끝나면 1차 캐시도 삭제되게 되므로 사실 큰 성능 향상을 주지는 않는다.(여러명이 공유하는 캐시 - 2차 캐시)





영속 엔티티의 동일성 보장

Member findMember1 = em.find(Member.class , 101L);
Member findMember2 = em.find(Member.class , 101L);

System.out.println(findMember1 == findMember2); // true
  • 1차 캐시로 Repeatable read 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.





엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연

  • persist가 동작하면 쓰기 지연 데이터베이스와 1차 캐시에 데이터를 저장한다.
  • 이후 commit이 동작할 때 그때 DB에 query를 날려서 저장한다!

Untitled 5

  • 이를 통해 persistence에 코드를 추가하면 버퍼링 기능을 수행할 수 있게 된다.
  • <property name="hibernate.jdbc.batch_size" value="10"/>





엔티티 수정 - 변경감지(Dirty Checking)

Member member1 = em.find(Member.class, 150L);
member1.setName("ZZZZZZ");
  • persist를 사용할 필요는 없다 → Java collection 다루듯이 값만 변경해도 JPA가 이를 감지하고 UPDATE 쿼리를 보내게된다.
  • 따라서 if문을 사용하려고 해도 JPA는 이미 setName을 통한 변경감지로 UPDATE 쿼리가 전송될 것이다.
    • 데이터 수정 시 if문을 사용하는 것은 무용지물.
  • 변경 감지 과정
    • 1차 캐시에 SnapShot을 만든다.
      • SnapShot : 값을 읽어온 최초 시점의 상태를 저장하는것
    • SnapShot과 변경된 값이 다르면 이를 감지하고 (변경 감지) Update 쿼리를 전송한다.

Untitled 6





플러쉬 - flush

  • 영속성 컨택스트의 변경내용을 데이터베이스에 적용.
  • 사용 방법
    • em.flush() - 수동
    • 트랜잭션 커밋 - 자동
    • JPQL 쿼리 실행 - 자동
  • 과정
    1. 변경 감지
    2. 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
    3. 쓰기 지연 SQL 저장소의 쿼리들 flush가 작동하면 데이터 베이스의 쿼리를 데이터 베이스에 전송한다.
  • 대체적으로 자동으로 JPA에서 호출하게된다.
    • 수동으로 사용할 경우
      1. 쿼리를 중간에 확인하고 싶거나.
      2. 강제적으로 중간에 쿼리를 보내고 싶거나.
  • flush를 해도 1차 캐시가 유지된다.
  • JPQL 쿼리 실행하면 왜 자동으로 실행될까?

Untitled 7

쿼리가 날라가지 않은 상태에서 JPQL을 사용해 조회하면 아무것도 조회할 수 없을 것이기 때문에 JPQL을 사용하면 자동적으로 flush가 실행된다.

  • 영속성 컨텍스트를 비우지 않는다!!!
  • 트랜잭션의 작업 단위가 중요 → 커밋 직전에만 동기화 하면 된다.





준영속 상태

  • 영속 → 준영속이 된다면…
    • em.persist() (영속 상태가 됨) , em.find(class,id) (1차 캐시를 업데이트하면서 영속상태가됨)
    • 준영속 상태가 되면 해당 컨텍스트에서의 영속상태를 해제한다
      • 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
      • commit해도 아무런 변화가 일어나지 않는다.



  • 사용 방법
    • em.detach(entity) - 특정 엔티티만 준영속 상태로 전환
    • em.clear() - 영속성 컨텍스트를 완전히 초기화
    • em.close() - 영속성 컨텍스트를 종료