JPA) 내부구조 - 영속성 컨텍스트의 이점

Date:    Updated:

카테고리:

저번 포스팅에 이어 영속성 컨텍스트가 주는 이점들에 대해 알아보자.

영속성 컨텍스트의 이점

영속성 컨텍스트는 다음과 같은 이점으로 application과 DB 사이에 중간계층으로 존재한다.

1차 캐시

  • image
  • 영속성 컨텍스트 내부에는 1차 캐시가 존재한다. (1차 캐시 자체를 영속성 컨텍스트 라고 이해해도 된다.)
  • Entity가 영속성 컨텍스트에 의해 영속 상태가 되면 1차 캐시에 저장된다.
  • 1차 캐시 내부에는 Entity@Id로 등록된 필드가 식별자가 되고 Entity 객체가 해당 식별자의 값이 된다. (Key-Value)
  • 1차 캐시에 저장된 Entity를 조회할 경우 DB Connection pool을 거치지 않고 캐시에 있는 값을 조회하기 때문에 성능상의 이점이 있다.
    • 만약에 1차 캐시에 Entity가 없다면?
      • image
      • Entity 객체를 DB에서 조회한 후 1차 캐시에 저장하여 1차 캐시에서 조회 한 후 반환한다.
  • 영속성 컨텍스트는 트랜잭션이 끝날 때 같이 종료됨.
    • 즉, 트랜잭션이 종료될 때 1차 캐시도 같이 소멸되기 때문에 찰나의 순간에서만 이득이 있다. 그렇게 큰 이득은 아니다.
    • 그러나 비즈니스 로직이 매우 복잡한 경우에는 효과가 있다.

영속 Entity의 동일성(identity) 보장

  • 영속 상태의 Entity에 대해서 동일성을 보장한다. (== 비교시)
    • 예를 들어, 다음과 같이 member1이라는 영속 상태의 Entity를 여러번 호출하여 비교해도 결국 1차 캐시에 있는 동일한 Entity를 호출한 것임으로 true를 반환한다.
      Member a = entityManager.find(Member.class, "member1");
      Member b = entityManager.find(Member.class, "member1");
      System.out.println(a == b); // true
      
    • a와 b 객체 둘다 같은 reference다.
  • 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.

트랜잭션을 지원하는 쓰기 지연 (Transactional write-behind)

  • 영속성 컨텍스트가 영속화 할때마다 일일이 SQL을 데이터베이스에 보내지 않고 트랜잭션 커밋 시점에 한번에 보낸다.
    EntityManager em = emf.createEntityManager();
    EntityTransaction transaction = em.getTransaction();
    
    //엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
    transaction.begin(); // [트랜잭션] 시작
    
    em.persist(memberA);
    em.persist(memberB);
    //여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
    
    //커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
    transaction.commit(); // [트랜잭션] 커밋
    
    • EntityManager가 SQL을 생성하여 쓰기 지연 SQL 저장소에 저장했다가 트랜잭션 커밋 시점에 한번에 보내게 된다. (flush가 이루어짐)
      • image
      • 일종의 버퍼링 기능이며 hibernate.jdbc.batch_size 옵션을 통해 설정할 수 있다. (한 번에 몇 건까지 모았다가 보낼건지)
        • 여러 번의 network를 통하지 않고 한 번의 network를 통해 전부 보낼 수 있기 때문에 성능을 개선할 수 있다.

변경 감지(Dirty checking)

  • 영속 상태의 Entity를 마치 객체의 속성을 수정하듯 setter를 통해 수정한 뒤 커밋을 하면 알아서 DB에 반영된다.

    EntityManager em = emf.createEntityManager();
    EntityTransaction transaction = em.getTransaction();
    
    transaction.begin(); // [트랜잭션] 시작
    
    // 영속 엔티티 조회
    Member memberA = em.find(Member.class, "memberA");
    
    // 영속 엔티티 데이터 수정
    memberA.setUsername("hi");
    memberA.setAge(10);
    
    //em.update(member) 이런 코드가 있어야 하지 않을까? setter만으로 수정이 가능하다?
    
    transaction.commit(); // [트랜잭션] 커밋
    
  • image
  • 영속성 컨텍스트의 스냅샷 활용
    • 영속성 컨텍스트는 1차 캐시에 Entity가 들어오는 순간 해당 시점의 스냅샷을 저장해놓는다.
    • 트랜잭션 커밋 이후 flush()가 일어날 때 1차 캐시에 저장된 스냅샷과 Entity의 상태를 비교한다.
    • 만일 저장된 스냅샷과 Entity의 상태가 다르다면 UPDATE SQL을 생성한다 (변경 감지 발생!)
    • 그 후 flush -> commit을 통해 DB에 반영된다.

마무리

다음 포스팅에선 영속성 컨텍스트의 변경내용을 DB에 반영하는 flush()에 대해 알아보자

📣 Reference

본 포스팅은 김영한님의 강의를 듣고 스스로 정리 및 추가한 내용입니다.

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

댓글남기기