[JPA] 영속성 컨텍스트(Persistence Context) (ft. 1차 캐시, 2차 캐시)

 

 

 

JPA의 영속성 컨텍스트

영속성 컨텍스트는 JPA가 Entity 객체를 상태관리하는 공간 및 매커니즘이다.

자세히는 엔티티의 상태를 메모리 상에 올려 놓고, Datsbase와의 동기화를 관리하는 공간으로 볼 수 있다. 이 공간에 올라간 객체는 변경 사항을 추적할 수 있게 되고 트랜잭션이 끝나는 시점에 Database에 반영된다.

 

 

1. 영속성 상태

JPA의 영속성 컨텍스트 내에서 관리되는 Entity는 네 가지의 상태를 갖는다

비영속(Transient) Database와 전혀 관련이 없는, 단순 메모리 상에만 존재하는 상태
영속(Persistent) 영속성 컨테스트에 의해 관리되고 있는 상태
준영속(Detached) 영속 상태에서 벗어나 분리된 상태
삭제(Removed) Database에서 삭제 예정인 상태(트랜잭션이 커밋되면 데이터베이스에서 삭제됨)

 

 

2. 영속성 특징

더 자세한 특징을 보면 4가지로 정리할 수 있다

1차 캐시

Entity를 Database에서 조회할 때, 영속성 컨텍스트 내 1차캐시에서 먼저 찾게되어있다. 여기에서 없으면 그때 Database에서 조회하고 그 결과가 다시 1차 캐시에 저장된다

🤔2차 캐시도 있나요? 🍊:넹. 이따 알려줄게요

변경 감지

흔히 Dirty Checking이라고 하는데, 영속성 컨텍스트에 속한 Entity는 Transaction 내에서 변경 사항이 있으면 자동으로 감지된다. 이후 Transaction이 Commit될 때 변경 사항이 Databse에 반영된다.

 

쓰기 지연

 JPA가 성능을 최적화하기 위해 사용하는 기법으로, 여러 변경 작업이 Transaction에서 이루어질지언정 Transaction이 Commit될 때 한꺼번에 변경 사항을 Database에 반영한다.

 

 

동일성 보장

영속성 컨텍스트 내에 있는 Entity를 조회한다면 이는 동일한 객체임을 보장하게 되어있다. 즉, 동일한 Entity를 두 번 조회한다면 메모리 상의 동일한 객체를 반환받는다. 코드레벨에서 보자면 다음과 같다

public void selectTest(){
    User user1 = entityManager.find(User.class, 1L); //ID가 1인 사용자 조회, DB에서 가져옴
    User user2 = entityManager.find(User.class, 1L); // ID가 1인 사용자 조회, 영속성 컨텍스트에서 가져옴

    System.out.println(user1 == user2); // true가 찍힌다
}


public void changeTest(){
    User user = entityManger.find(User.class, 1L);
    System.out.println(user.getName()); // 출력결과가 "origin" 이라고 하자
    user.setName("change"); // 변경 진행

    Order order = entityManager.find(Order.class, 1L);
    User sameUser = entityManager.find(User.class, 1L);

    System.out.println(sameuser.getName()); // Commit 전이지만 "chagne" 가 출력됨
}

 

 

🤔: 그러면 한 트랜잭션 실행 중에 다른 사용자가 접근한다면 어떤 정보를 뱉나요?

앞선 Transaction 이 사용자 A의 것이라고 치자. 이후 사용자B가 접근하여 user_id = 1L인 데이터를 조회를 한다. 그때는 별개의 Transaction 이 생성되어 사용자A의 Transaction이 수행되기 전의 (commit 되지 않은) 상태를 조회한다 (name = "origin"인 데이터를 보게 되는 것)

 

 

3. 1차 캐시, 그리고 2차 캐시

1차 캐시를 다시 떠올리면 1차 캐시는 영속성 컨텍스트에서만 유지되는 데이터다. 즉 한 트랜잭션 범위 내에서만 사용되며 해당 Transaction이 종료되면 1차 캐시는 사라진다. 이에 비해 2차 캐시는 아래와 같다.

 

2차 캐시

2차 캐시는 Application 전역에서 사용된다. 즉 트랜잭션과 영속성 컨텍스트를 넘어서도 캐시된 Entity를 재사용할 수 있다. 동작과정은 이렇다.

1. 2차 캐시를 활성화한다 (ft. persistence.xml, application.properties 등)

2. 캐시 구현체를 선택한다 ( ex. Hibernate와 호환되는 Ehcache, HazelCast 라이브러리 등)

3. 2차 캐시가 활성화됨에 따라, Application에서 여러 Transaction과 여러 영속성 컨텍스트가 존재할지라도 Database에 접근하는 대신 캐시에 존재하는 Entity를 가져온다.

 

1차캐시와 2차캐시의 비교

2차캐시는 일관성 측면에서 주의가 필요하다. 이와 관련하여 비교하면 다음과 같다.

  1차 캐시 2차 캐시
적용범위 영속성 컨텍스트 내 (한 트랜잭션 내)  애플리케이션 전역(다중 트랜잭션 간)
캐시 유지 기간 Transaction 종료 시 소멸 애플리케이션 실행 중 지속
일관성 Transaction 내에서 완전한 일관성 보장 Entity 변경 시 수동 갱신 필요
데이터 추적 Entity 상태 변경 추적 Entity 상태 변경 추적 불가
데이터베이스 접근 캐시된 Entity인 경우 Database에 접근하지 않음 캐시된 Entity에 접근
설정 기본적으로 활성화 명시적 설정 필요

 

Application에서 자주 조회되는 데이터라면 2차 캐시로 저장함으로써 Application 종료 전까지 여러 사용자가 동일 데이터를 조회하도록 하는 데에 효과적일 것이다. 또 필요에 따라 캐싱전략을 설정할 수도....,.,.,.