[JPA] Persistence Context

Gajun Choi·2022년 9월 19일
0

JPA

목록 보기
3/4

요약

  1. Persistence Management
  2. Flush

1. 영속성 관리

  • JPA에서 가장 중요한 2가지
    • 객체와 관계형 DB 매핑하기(Object Relational Mapping)
    • Persistence Context - JPA 내부 동작

1.1 Persistence Context

  • JPA의 핵심으로 논리적인 개념으로 보이지는 않지만 Entity를 영구 저장하는 환경을 의미

    	EntityManager.persist(entity);
  • DB가 아닌 Persistence Context를 통해서 Entity를 영속화 한다.

  • persist() 시점에서 Entity를 영속성 컨텍스트에 저장함

  • EntityManager가 생성되면 1:1로 Persistence Context가 생성

1.2 EntityManagerFactory / EntityManager

  • 고객의 요청에 의해 EntityManagerFactory는 EntityManager를 생성한다 (thread 생성시)
  • EntityManager는 내부적으로 DB connection pool을 사용해서 DB에 접근
  • JPA는 EntityManagerFactory를 만들어야 함
  • EntityManagerFactory
    • application loading시 DB당 하나의 EntityMagerFactory 생성

      EntityManagerFactory emf = Persistence.createEntityManagerFactory("name");
    • 서버가 종료되는 시점에는 EntityManagerFactory를 닫는다

      emf.close();
  • EntityManager
    • 실제 Tracsaction 단위를 수행할 때마다 생성
    • 즉, 고객의 요청이 올 때마다 사용했다가 닫는다.
    • thread 간에 공유하면 안된다. (사용하고 버려야 한다.)
    • Transaction 수행 후에는 닫아야함 (내부적으로 DB Connection을 반환하기 위해)
  • EntityTracsaction
    • Data를 변경하는 모든 작업은 반드시 Transaction 안에서 이루어짐 (조회 x)

      EntityTransaction tx = entityManager.getTransaction();
    • tx.begin(); : Transaction 시작

    • tx.commit(); : Transaction 수행

    • tx.rollback(); : 작업에 문제가 생겼을 시

1.3 Entity 생명주기

1.3.1 비영속성

  • Persistence Context와 전혀 관계가 없는 상태
  • 객체를 생성만 한 생태
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("멤버1");

1.3.2 영속

  • Persistence Context에 저장된 상태
  • Entity가 영속성 컨텍스트에 의해 관리되는 상태
  • EntityManager.persist() : 영속 상태가 된다고 바로 DB에 쿼리가 가지는 않는다 (DB 저장 x)
  • transaction.commit() : 트렌젝션의 commit 시점에 Persistence Context의 정보가 DB에 쿼리로 날라감
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("멤버1");
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// 객체를 저장한 상태 (영속)
entityManager.persist(member);

1.3.3 준영속성

  • Persistence Context에 저장되어 있다가 분리된 상태
  • Persistence Context에서 지운 상태
// 멤버 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
entityManager.detach(member);

1.3.4 삭제

  • 실제 DB 삭제를 요청한 상태
// 객체를 삭제한 상태
entityManager.remove(member);

1.4 Persistence Context의 이점

1.4.1 1차 캐시

  • Persitence Context 내부에 1차 캐시가 존재(1차 캐시 = 영속성 컨텍스트)
  • Map 형식으로 1차 캐시에 저장(key : @Id로 선언한 필드 값 - PK, value : 해당 Entity 전체)
  • 1차 캐시에 Entity가 있으면 좋은 점
    • entityManager.find() 를 하면 DB보다 먼저, 1차 캐시를 조회하기 때문에 빠르다
    • 1차 캐시에 해당 Entity가 존재하면 바로 반환한다.
  • 1차 캐시에 조회하고자하는 Entity가 없다면
    • DB에서 조회한 후 Entity를 1차 캐시에 저장하고나서 Entity를 반환한다.

1.4.2 동일성 보장

Member a = entityManager.find(Member.class, "member1");
Member b = entityManager.find(Member.class, "member1");
System.out.println(a == b); // 동일성 비교 true
  • 영속 Entity의 동일성(== 비교)을 보장한다.
    • 즉, “==” 비교가 true 임을 보장한다.
    • member1에 해당하는 Entity를 2번 조회하면 1차 캐시에 의해 같은 Reference 로 인식된다.
    • 하나의 Transaction 안에서 같은 Entity 비교 시 true
  • 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 DB가 아닌 아닌 애플리케이션 차원에서 제공한다.

1.4.3 Transactional Write-Behid (트랜잭션 쓰기 지연)

EntityManager entityManager = emf.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
// EntityManager는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin(); // Transaction 시작

entityManager.persist(memberA);
entityManager.persist(memberB);
// 이때까지 INSERT SQL을 DB에 보내지 않는다.

// 커밋하는 순간 DB에 INSERT SQL을 보낸다.
transaction.commit(); // Transaction 커밋 

  • entityManager.persist(memberA)
    • memberA가 1차 캐시에 저장됨
    • 저장됨가 동시에 JPA가 Entity를 분석하여 insert Query를 만든다
    • insert Query를 쓰기 지연 SQL저장소에 쌓는다
    • DB에 바로 넣지 않고 기다린다.
    • memberB도 동일
  • transaction.commit()
    • 쓰기 지연 SQL 저장소에 쌓아여있는 쿼리들을 DB로 날린다. (flush())
    • flush()는 1차 캐시를 지우지 않는다. 쿼리를 DB에 날려서 DB와 캐시의 싱크를 맞추는 역할
    • flush() 후에 실제 DB transaction이 커밋됨 (commit())
  • 버버링 기능
    • DB에 쿼리를 날리는 것에 대한 옵션 설정을 통한 최적화 기능 (성능 개선 역할)
    • JDBC 일괄 처리 옵션

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

업로드중..

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // Transaction 시작

// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

// em.update(member) 또는 em.persist(member) 이런 코드가 있어야 하지 않을까?

transaction.commit(); // Transaction 커밋
  • Entity 데이터 수정 시 그냥 데이터만 수정하고 commit하면 알아서 반영 됨
    => (데이터 set하면 변경을 감지하여 UPDATE Queury가 나감)
  • 1차 캐시
    • @ID, Entity, Snapshot (영속성 컨텍스트에 최초로 들어간 값을 읽어온 최초의 상태)
  • 변경감지 매커니즘
    • transaction.commit() 을 하면 flush()가 일어날 때 Entity와 Snapshot을 비교한 다음 변경사항이 있으면 UPDATE Query를 만들고 해당 쿼리를 쓰기 지연 SQL 저장소에 저장한다. 이후 UPDATE Query를 DB에 반영한 후 commit()

1.4.6 엔티티 삭제

Member memberA = em.find(Member.class, "memberA");

em.remove(memberA); // 엔티티 삭제
  • 위의 Entity 수정에서의 원리와 동일
  • Transaction의 commit시점에 DELETE Query가 나감

2. Flush

2.1 Flush 개념

  • Persistence Context의 변경 내용을 DB에 반영하는 것
  • Transaction commit이 일어날 때 flush가 동작하는데, 이때 쓰기 지연 SQL 저장소에 쌓여있던 SQL 쿼리들이 날라간다.
  • Flush 를 통해 Persistence Context를 비우는 것은 아님 (DB와 싱크를 맞춤)

2.2 Flush 동작원리

  1. 변경을 감지(Dirty Checking)를 한다.
  2. 수정된 Entity를 쓰기 지연 SQL 저장소에 등록
  3. 쓰기 지연 SQL 저장소의 Query들을 DB에 전송 (insert, update, delete)
  • flush가 발생한다고 commit이 된 것이 아니라 flush 다음에 commit이 발생
  • flush가 동작할 수 있는 이유는 DB Transaction(작업 단위)라는 개념이 있기 때문임
  • JPA는 기본적으로 데이터의 싱크나 동시성에 관련된 것은 Transaction에 위임

2.3 Persistence Context를 flush 하기

  1. em.flush()를 통한 직접 호출
// 영속 상태 (Persistence Context 에 의해 Entity 가 관리되는 상태)
Member member = new Member(200L, "A");
entityManager.persist(member);

entityManager.flush(); // 강제 호출 (쿼리가 DB 에 반영됨)

System.out.println("DB INSERT Query 가 즉시 나감. -- flush() 호출 후 --  Transaction commit 됨.");
tx.commit(); // DB에 insert query 가 날라가는 시점 (Transaction commit)
  1. Transaction commit 발생 시 flush 자동 호출(flush 후 commit)

  2. JPQL 쿼리 실행 시 flush 자동 호출

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

// 중간에 JPQL 실행
query = entityManager.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
  • memberA,B,C를 Persistence Context에 저장한 상태에서 바로 조회하면 조회가 될까?
    = NO! DB에 Query로 날라가야 반영이 될텐데 INSERT 가 되지 않은 상태이기 때문에 조회 X
  • JPQL은 SQL로 번역이 되어서 실행된다.
  • JPA는 JPQL 쿼리 실행 시 flush()를 자동으로 날린다.
    = JPQL 쿼리 실행 시 flush를 자동 호출하여 위 코드는 조회가 가능

2.4 Flush Mode

 em.setFlushMode(FlushModeType.COMMIT);
  • FlushModeType.AUTO
  • flush 수행
    • (1) Transaction을 commit 하기 전
    • (2) Query를 실행 한 후
  • FlushModeType.COMMIT

참조

https://gmlwjd9405.github.io/2019/08/06/persistence-context.html

https://velog.io/@kwg527/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8

profile
개발하는헬창

0개의 댓글