[JPA] 영속성 컨텍스트를 왜 사용하는가?

Intro

앞 게시물에서 영속성 컨텍스트를 정리했다. 정리를 하면서 왜 영속성 컨텍스트라는 공간을 사용하는 가? 라는 생각이 들었고 공부를하였기에 이를 정리한다.

영속성 컨텍스트란

영속성 컨텍스트 개념이 궁금하다면 여기로!

영속성 컨텍스트 사용 이유(이점)

  1. 첫째로는 1차 캐시 사용이다. 영속성 컨텍스트에 있는 1차 캐시라는 공간이 있는데 영속화 된 엔티티가 존재할 경우 DB에 쿼리를 보내지 않고도 엔티티를 가져올 수 있기에 DB를 거치지 않고도 엔티티를 얻을 수 있는 장점이 있다.
  2. 두번째로는 동일성 보장이다. 1차 캐시 공간에 Id(식별자 값)가 같은 엔티티에 대해서 동일성을 보장한다.
  3. 세번째로는 변경감지(Dirty Checking)이다. 1차 캐시에 들어온 데이터에서 변경이 됐을 때 쓰기 지연 SQL 저장소에 쌓아둔 후 flush를 하면서 변경된 내용을 반영한다는 장점이 있다.
  4. 마지막으로는 쓰기지연(Dirty Checking)이다. 트랜잭션 커밋하기 전까지 SQL을 DB에 보내지 않고 쓰기 지연 SQL 저장소에 보관했다가 한 번에 커밋했을 때 보낼 수 있다.

영속성 컨텍스트 이점

1. 1차 캐시

1차 캐시란, 영속성 컨텍스트에 있는 공간으로 영속화 된 엔티티를 저장하는 공간이다.

  • 엔티티가 영속화 될 때 key : value 형태로 저장된다.
  • key에는 @id로 선언한 필드값이 저장되고, value에는 Entity가 저장되어 @Id로 선언한 필드값으로 1차 캐시 내에서 구분한다.
  • 1차 캐시는 조회 할 때 DB로 바로 접근하지 않고 1차 캐시에서 먼저 조회하여 해당 엔티티를 먼저 찾을 수 있는 공간이다. img

  • 영속화 된 엔티티가 있을 경우 DB를 거치지 않고 엔티티를 반환하지만, 없는 경우에는 DB를 검색하고 해당 엔티티를 1차 캐시에 저장하고 반환한다. img

2. 동일성 보장

  • 동일성이란, 자바에서 두 객체가 갖고 있는 값이 일치하고 메모리 상에서 같은 주소에 위치할 때의 성질을 의미한다.
  • 1차 캐시에서 @Id가 같은 엔티티를 반복하여 호출하여도 같은 인스턴스를 반환하여 동일성을 보장할 수 있다.
    1
    2
    3
    4
    5
    6
    
    // Id가 1인 User 엔티티를 조회
    User user1 = em.find(User.class, 1L);
    User user2 = em.find(User.class, 1L);
    
    System.out.println(user1 == user2);//true가 반환
    
    
  • 1차 캐시로 반복 가능한 읽기(REPETABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.

3. Dirty Checking(변경 감지)

  • JPA에서 별도의 update 코드가 필요하지 않은데 Dirty Checking을 통해 변경된 내용을 DB에 반영하기 때문이다.
  • 원리
    • 1차 캐시에 저장될 때(영속화 될 때) 스냅샷을 찍어두게 되는데, 엔티티가 변경되면 변경된 내용을 스냅샷과 비교하여 감지한 후 트랜잭션 커밋 시점에 변경된 내용을 DB에 반영하는 것이다.
    • 따라서, 커밋하기 전에 원상복구 시키면 update 쿼리가 실행되지 않는다.

4. flush(플러시)

flush(플러시)란, 영속성 컨텍스트와 데이터베이스를 동기화하는 것을 의미한다.

  • 트랜잭션이 커밋되는 순간에 flush가 발생한다. flush가 발생하게 되면 쓰기 지연 SQL 저장소에 있는 SQL 등이 데이터베이스에 전달되어 변경된 내용을 반영하여 DB와 영속성 컨텍스트를 동기화하는 것이다.
  • 플러시를 한다고 해서 영속성 컨텍스트를 비우는 것이 아니다.
  • flush 방법
    • 트랜잭션 커밋(자동 flush)
    • JPQL 쿼리 실행(자동 flush)
    • em.flush(직접 flush, EntityManager)
  • 순서 img
    1. Dirty Checking(변경감지)
    2. 변경된 내용 존재할 경우 쓰기 지연 SQL 저장소에 등록
    3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송하여 동기화함.

5. 쓰기 지연

  1. em.persist(user) 를 통해 영속화 했을 때 Insert 쿼리문을 DB에 바로 보내는 것이 아니라, 1차 캐시 공간에 저장하면서 동시에 쓰기 지연 SQL 저장소에 쌓아둔다.
  2. 이후 transaction.commit()을 하게 되면 쓰기 지연 SQL에 있는 INSERT 쿼리를 DB에 보낸다.

요약(commit ~ flush)

  1. commit 하는 시점에 Dirty Checking(변경 감지)를 하여 변경 내용을 쓰기 지연 SQL 저장소에 저장한다.
  2. 그리고 flush가 발생하는데, 변경된 내용을 DB와 동기화하는 작업을 진행한다.

결론

영속성 컨텍스트를 사용했을 때 1차 캐시에 저장하여 쓰기 지연을 통해 바로바로 DB에 커밋되지 않도록, 커밋 전까지 변경을 자유롭게 할 수 있다는 이점을 갖고 있다.
그리고 변경되는 내용을 스냅샷을 통해 Dirty Checking이 이뤄진다.


참고 사이트

엔티티 매니저, 엔티티 참고 게시물
영속성 컨텍스트 참고 게시물
영속성 컨텍스트 참고 게시물2