자바 ORM 표준 JPA 프로그래밍 서적을 학습하면서, 정리하고 있는 내용입니다.

 

 

EntityManagerFactory

사용자의 요청이 들어옴에 따라 EntityManager를 생성하는 책임을 가지고 있다.

 

JPA를 동작시키기 위한 내부 객체들이 이 Factory를 통해 생성되고, 구현체에 따라선 커낵션 풀도 별도로 구성하므로 생성 비용이 크다고 한다.

 

여러 EntityManagerFactory를 생성하여 공유할 수 있으나. 하나의 객체만 생성해서 전 영역에 공유하는 것이 권장된다. (configuration lite mode를 사용하면 여러 개를 생성하고 공유할 수 있을 것 같다.)

 

 

 

EntityManager (Persistence Context)

엔티티 정보를 저장하고 있는 환경. 요청 결과가 DB에 반영되기 이전에 위치하는 논리적인 영역을 의미한다. 가상의 데이터베이스 영역이라고 봐도 무방하다.

 

사용자의 요청을 받아 Connection을 통해 DB와 로컬 트랜잭션을 맺으며, 데이터를 관리하고 영속적으로 반영하는 혹은 삭제하는 책임을 가지는 객체이다.

 

JPA의 대부분의 기능은 해당 객체가 지원한다. 커넥션과 밀접한 관계를 가지기에 스레드 간의 공유, 재사용이 어렵다.

멀티스레드에 안전하지 않은 상태가 된다는 것 아닐까?

 

 

 

Entity의 Life Cycle

  • 비영속 (new, transient) : 영속성 컨텍스트와 관계가 없는 새로 생성된 상태

      // 비영속 상태의 Entity. 즉 entityManager가 모르고 있는 Entity이다.
      Entity entity = new Entity();
      entity.setXxxA("test");
      entity.setXxxB("value");
  • 영속 (managed) : 영속성 컨텍스트에게 관리되는 상태 (환경 안에 정보가 있는 상태)

    JPA는 기본적으로 트랜잭션 안에서 데이터를 변경하여야 한다.

      // 요청에 따른 entityManager(Persistence Context) 생성
      EntityManager entityManager = entityManagerFactory.createEntityManager();
    
      // 영속성 컨택스트에 대한 글로벌 트랜잭션 관리 시작
      EntityTransaction transaction = entityManager.getTransaction();
      transaction.begin();
    
      try {
    
              // 비영속 상태의 Entity.
              Entity entity = new Entity();
              entity.setXxxA("test");
              entity.setXxxB("value");
    
              // 영속성 컨택스트에 Entity 정보를 저장. 즉 entityManager가 알고있는 Entity이다.
              entityManager.persist(entity);
    
              // transcation 결과 반영 - (DB 저장 X, 영속성 컨텍스트에만 반영)
              transaction.commit();
      } catch (RuntimeException exception) {
              // 위 로직 실패 시 결과 반영 X -> 원복
              transaction.rollback(); 
      } finally {
              // 성공, 실패 상관없이 transaction 종료
              transaction.close();
      }
  • 준영속 (detached) : 영속성 컨텍스트에 관리되다가 이후 분리된 상태

    영속성 컨텍스트가 지원하는 기능을 사용할 수 없기에 비영속 상태와 유사하다. 다른 점은 식별자 값(id)이 존재한다는 점이다.

      // 특정 회원 엔티티를 영속성 컨텍스트에서 분리한다.
      // 쉽게 말하면 해당 엔티티를 영속성 컨텍스트에 존재하는 1차 캐시에서 삭제한다.
      entityManager.detach(entity);
    
      // 해당 영속성 컨텍스트에서 관리되는 모든 엔티티를 준영속화한다.
      // 쉽게 말하면 영속성 컨텍스트의 1차 캐시를 초기화한다.
      emtityManager.clear();
    
      // 영속성 컨택스트를 종료시킨다.
      entityManager.close();
  • 삭제 (removed) : 엔티티가 삭제된 상태

      // 해당 객체를 삭제. 즉 DB에 저장된 정보를 삭제하는 요청을 전송한다. 
      entityManager.remove(entity);

    준영속과 삭제의 다른 점은 Entity가 DB에 반영되기 전이냐 이후이냐의 차이이다.

 

 

 

JPA Internal Flow

 

 

 

Persistence Context의 특징

  • 영속성 컨텍스트와 식별자 값

    영속성 컨텍스트는 @ID로 매핑된 값을 통해 엔티티를 구분한다. 즉 해당 값이 무조건 존재해야 하며, 없는 경우 예외가 발생한다.

  • 데이터베이스 저장 시점

    영속성 컨텍스트에 저장, 관리되는 엔티티는 flush()가 호출된 시점에 실제 DB에 반영된다.

  • 엔티티 관리 방식의 장점 - 아래

 

 

 

JPA 엔티티 관리 방식의 장점

  • 1차 캐시

    영속성 컨텍스트에서 관리되는 데이터를 우선적으로 조회하는 것을 의미한다.

    사용자의 요청이 종료된다면 해당 영역도 제거되기에 하나의 비즈니스 로직에서만 이점을 제공한다.

     

    전역적으로 관리되는 캐시는 2차 캐시라고 부른다.

     

     

    해당 정보가 컨텍스트 안에 존재하지 않는다면,

    • DB에서 해당 정보를 조회한 후 (SELECT)

    • 쿼리 결과를 이용하여 엔티티를 생성한 다음

    • 컨텍스트 내부에 엔티티를 저장하고 반환한다.

    • 1차 캐시는 내부적으로 Map을 통해 관리되며, @Id로 매핑된 값을 Key로 가진다. 이 값은 DB의 PK 값인 경우도 존재한다.*

       

  • 동일성 보장

    1차 캐시를 이용하여 반복 가능한 읽기 등급의 격리 수준을 Application 수준에서 제공한다. 즉 같은 트랜잭션 내에 여러 번 조회 쿼리를 진행하더라도 동일한 결과를 반환받는 것을 이야기한다.

     

    이때에는 동일성 비교가 가능하다. → Map에서 동일한 엔티티를 제공한다.

     

  • 트랜잭션을 지원하는 쓰기 지연

    한 트랜잭션 내에서 여러 엔티티를 등록할 때 각각의 엔티티에 대한 Insert Query를 DB에 바로 요청하지 않고, 일종의 버퍼처럼 컨택스트 내부에 존재하는 SQL 저장소에 Query를 쌓아놓다가 1번에 반영할 수 있다.

     

    entityManager.commit();

     

    큰 오버헤드를 가지는 i/o 처리를 최소화할 수 있다.

     

  • 변경 감지

    영속성 컨텍스트에게 관리되고 있는 엔티티 정보에 대해서 Service logic에서 setter 등을 통해 수정을 가할 경우, 그 변경 결과가 트랜잭션을 커밋하는 시점에 영속성 컨텍스트에게 감지되고 최종적으로 Update Query가 DB로 전송되어 최신 정보로 변경하게 된다.

     

    엔티티 정보와 엔티티 스냅샷 정보를 비교하여 감지한다.

     

    스냅샷 정보는 DB에서 값을 읽어와 캐시에 저장한 시점의 엔티티 정보를 복사하여 저장해둔 것이다. 이것을 트랜잭션이 커밋된 시점에서 엔티티 정보와 비교하여 변경된 사항들에 대해서 Update Query를 생성하는 것이다.

     

    엔티티 삭제도 동일 메커니즘을 공유하고 트랜잭션 커밋 시 Delete Query를 전송한다.

     

     

     

Flush?

영속성 컨텍스트의 변경 내용들을 데이터베이스에 반영시키는 기능이다.

 

영속성 컨텍스트에 저장된 정보를 비우는 것은 아니다.

 

 

Flush 발생 시 Flow는

  • 엔티티에 대한 변경을 감지하고
  • 추가, 수정, 삭제된 엔티티 정보에 대한 Query를 내부 SQL 저장소에 등록한 뒤
  • 해당 정보를 DB에 전송한다.

이렇게 이루어진다.

 

 

Flush를 발생시키는 방법으로는

  • em.flush()를 통한 직접 호출

  • 트랜잭션 commit() 을 통한 자동 호출

  • JPQL으로 만들어진 Query 실행 시 자동 호출

    영속성 컨텍스트에만 관리되고 있는 영속 상태의 엔티티들은 사실상 DB에 존재하지 않으므로 JPQL을 통한 Query 요청 시 결과에서 이 것들을 확인할 수 없게 된다. 이를 방지하기 위해 미리 Flush()를 호출하여 DB에 반영한 뒤 정상적인 결과를 제공하게 된다.

가 있다.

 

 

Flush 모드 옵션

  • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 Flush를 실행한다.
  • FlushModeType.COMMIT : 커밋할 때만 Flush를 실행한다.

'Programming' 카테고리의 다른 글

Open Authorization Framework 2.0?  (0) 2021.03.01
Live Study_Week 14. Generic  (0) 2021.02.28
객체 지향 디자인의 핵심 개념 : 책임 [ GRASP ]  (4) 2021.02.11
Java의 Reflection API와 성능 이슈?  (0) 2021.02.02
G1 GC  (0) 2021.01.26

+ Recent posts