이번 시간에는 JPA 영속성 컨텍스트에 대해 알아보고자 합니다.
Hibernate와 같은 영속성 공급자는 영속성 컨텍스트를 사용하여 애플리케이션의 엔티티 수명 주기를 관리합니다.
이글에서는 영속성 컨텍스트의 소개부터 왜 중요한 지를 알아보겠습니다. 영속성 컨텍스트에 종류와 그 차이점을 예제와 함께 살펴보겠습니다.
- 영속성 컨텍스트 (Persistence context)
먼저 영속성 컨텍스트의 공식 정의에 대해 알아 보겠습니다.
EntityManager인스턴스는 영속성 컨텍스트와 연결됩니다. 영속성 컨텍스트는 영구 엔티티ID에 대해
고유한 엔티티 인스턴스가 있는 엔티티 인스턴스 집합입니다. 영속성 컨텍스트 내에서 엔티티 인스턴스와 해당 수명 주기가 관리됩니다. EntityManager API는 영구 엔티티 인스턴스를 생성 및 제거하고 기본 키로 엔티티를 찾고 쿼리하는 데 사용됩니다.
위 설명이 복잡해 보일 수 있지만 진행하면서 완전히 이해 되실겁니다. 영속성 컨텍스트는 모든 엔티티가 데이터베이스에서 가져오거나 데이터베이스에 저장되는 첫 번째 수준 캐시입니다. 애플리케이션과 영구 스토리지 사이에 위치하게 됩니다.
영속성 컨텍스트는 관리되는 엔티티에 적용된 모든 변경 사항을 추적합니다. 트랜잭션 중에 변경 사항이 있으면 엔티티가 더티(상태의 변화가 생긴)로 표시됩니다. 트랜잭션이 완료되면 이런한 변경 사항은 영구 저장소로 플러시됩니다.
EntityManager는 영속성 컨텍스트와 상호 작용할 수 있게 해주는 인터페이스입니다. EntityManager를 사용할 때마다 우리는 실제로 영속성 컨텍스트와 상호 작용합니다.
엔티티의 모든 변경 사항이 영구 저장소를 호출하면 얼나나 많은 호출이 이루어질지 상상할 수 있습니다. 영구 저장소 호출은 비용이 많이 들기 때문에 성능에 영향을 끼칩니다.
- 영속성 컨텍스트 유형
- 트랜잭션 범위 영속성 컨텍스트
- 확장 범위 영속성 컨텍스트
트랜잭션 범위 영속성 컨텍스트
트랜잭션 영속성 컨텍스트는 트랜잭션에 바인딩됩니다. 트랜잭션이완료되는 즉시 영속성 컨텍스트에 있는 엔티티는 영구 저장소로 플러시됩니다.
트랜잭션 내에서 작업을 수행할 때 EntityManager는 영속성 컨텍스트를 확인합니다. 컨턱스트가 존재하면 그걸 사용하고 그렇지 않으면 영속성 컨텍스트가 생성됩니다.
기본 영속성 컨텍스트 유형은 PersistenceContextType.TRANSACTION입니다. 트랜잭션 영속성 컨텍스트를 사용하도록 EntityManager에 설정하려면 @PersistenceContext 애노테이션을 추가해줍니다.
@Configuration
class QuerydslConfig(
@PersistenceContext
val entityManager: EntityManager
) {
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(entityManager)
}
}
확장된 범위의 영속성 컨텍스트
확장된 영속성 컨텍스트는 여러 트랜잭션에 걸쳐 있을 수 있습니다. 트랜잭션 없이 엔티티를 유지할 수 있지만 트랜잭션 없이 엔티티를 플러시 할 수는 없습니다.
EntityManager에게 확장 범위 지속성 컨텍스트를 사용하도록 하려면 @PersistenceContext의 type속성을 적용해야 합니다.
@Configuration
class QuerydslConfig(
@PersistenceContext(type=PersistenceContextType.EXTENDED)
val entityManager: EntityManager
) {
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(entityManager)
}
}
무상태 세션 빈에서 한 구성 요소의 확장된 영속성 컨텍스트는 다른 구성 요소의 영속성 컨텍스트를 완전히 인식하지 못합니다. 둘 다 같은 트랜잭션에 있는 경우에도 마찬가지입니다.
트랜잭션에서 실행 중인 회원정보를 담당하는 Service Component의 메서드에 일부 엔티티를 유지한다고 가정해 보겠습니다. 그런 다음
상품정보를 담당하는 Service Component 일부 메서드를 호출합니다. 상품정보를 담당하는 Service Component의 메소드 영속성 컨텍스트에서 이전에 회원정보를 담당하는 Service Component의 메소드에서 지속했던 엔티티를 찾을 수 없습니다.
- 영속성 컨텍스트 예제
영속성 컨텍스트에 조금 알았 보았으니 이제 예제를 살펴보겠습니다. 트랜잭션 영속성 컨텍스트와 확장된 영속성 컨텍스트를 사용하여 다양한 사용 예제를 알아 보겠습니다.
// 트랜잭션 SellerService.kt
@Service
class AdminSellerService {
@Transactional
fun goodbye(
sellerId: Long,
) {
val seller = sellerService.getSignedUpSellerById(sellerId)
seller.isGoodbye = true
sellerRepository.save(seller)
}
fun noTransactionGoodbye(
sellerId: Long,
) {
val seller = sellerService.getSignedUpSellerById(sellerId)
seller.isGoodbye = true
sellerRepository.save(seller)
}
fun getSellerAll(
query: String,
pageable: Pageable
): Page<Seller> = adminSellerRepository
.getSellerAll(query, pageable)
}
// 확장된 Configuration.kt
@Configuration
class QuerydslConfig(
@PersistenceContext(type=PersistenceContextType.EXTENDED)
val entityManager: EntityManager
) {
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(entityManager)
}
}
- 테스트
이제 서비스 클래스를 설정했으므로 트랜잭션 영속성 컨텍스트와 확장된 영속성 컨텍스트를 사용하여 다양한 사용 예제를 만들어 보겠습니다.
트랜잭션 영속성 컨텍스트 테스트
트랜잭션 범위 영속성 컨텍스트를 사용하여 Seller엔티티를 유지합시다. 엔티티는 영구 저장소에 저장됩니다. 그런 다음 확장된 영속성 컨텍스트의 EntityManager를 사용하여 getSellerAll로 확인합니다.
val seller:Seller = Seller(1L, "부산", "hong");
AdminSellerService.goodbye(seller);
sellerFromTransctionPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller.getId());
assertNotNull(sellerFromTransctionPersistenceContext);
//# 확장된 영속성 컨텍스트 configration파일 수정
sellerFromExtendedPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller.getId());
assertNotNull(sellerFromExtendedPersistenceContext);
트랜잭션 없이 User엔티티를 삽입하려고 하면 TransactionRequiredException 에러가 발생합니다.
@Test(expected = TransactionRequiredException.class)
fun test() {
val seller:Seller = Seller(1L, "부산", "hong");
AdminSellerService.noTransactionGoodbye(seller);
}
확장 영속성 컨텍스트 테스트
다음으로 트랜잭션 없이 확장된 영속성 컨텍스트를 사용하여 Seller를 유지해 보겠습니다. Seller엔티티는 영속성 컨텍스트(캐시)에 저장되지만 영구 저장소에는 저장되지 않습니다.
val seller:Seller = Seller(1L, "부산", "hong");
AdminSellerService.noTransactionGoodbye(seller);
sellerFromTransctionPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller.getId());
assertNotNull(sellerFromTransctionPersistenceContext);
sellerFromExtendedPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller.getId());
assertNotNull(sellerFromExtendedPersistenceContext);
영구 엔티티 ID의 영속성 컨텍스트에는 고유한 엔티티 인스턴스가 있습니다. 동일한 식별자를 가진 다른 엔티티를 유지하려는 경우:
@Test(expected = EntityExistsException.class)
fun test() {
val seller1:Seller = Seller(1L, "부산", "hong");
val seller2:Seller = Seller(1L, "부산", "hong");
AdminSellerService.noTransactionGoodbye(seller1);
AdminSellerService.noTransactionGoodbye(seller2);
}
EntityExistsException이 발생합니다.
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session
트랜잭션 내의 확장된 영속성 컨텍스트는 트랜잭션이 끝날 때 엔티티를 영구 저장소에 저장합니다.
val seller1:Seller = Seller(1L, "부산", "hong");
AdminSellerService.goodbye(seller1);
sellerDB:Seller = AdminSellerService
.getSellerAll(seller.getId());
assertNotNull(sellerDB);
확장된 영속성 컨텍스트는 트랜잭션 내에서 사용될 때 캐시된 엔티티를 영구 저장소로 플러시합니다. 먼저 트랜잭션없이 엔티티를 유지합니다. 다음으로 트랜잭션에서 다른 엔티티를 유지합니다.
val seller1:Seller = Seller(1L, "부산", "hong");
AdminSellerService.noTransactionGoodbye(seller1);
val seller2:Seller = Seller(2L, "부산", "hong");
AdminSellerService.noTransactionGoodbye(seller1);
seller1FromTransctionPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller1.getId());
assertNotNull(sellerFromTransctionPersistenceContext);
seller2FromTransctionPersistenceContext:Seller = AdminSellerService
.getSellerAll(seller2.getId());
assertNotNull(seller2FromTransctionPersistenceContext);
정리
이상으로 영속성 컨텍스트에 대해 알아 보았습니다.
먼저 트랜잭션 수명 동안 존재하는 트랜잭션 영속성 컨텍스트를 살펴보았고 다음으로 여러 트랜잭션에 걸쳐 있을 수 있는 확장된 영속성 컨텍스트를 살펴보았습니다.
영속성 컨텍스트에 대해서는 차후에 다시 한번 글을 작성할 생각입니다.
'IT > JPA' 카테고리의 다른 글
JPA 상속 전략에 대해 알아보자 (0) | 2023.04.06 |
---|---|
JPA Entity Manager를 알아보자 (0) | 2023.04.05 |
JPQL – JPA 및 Hibernate에서 쿼리를 정의하는 방법 (0) | 2023.04.03 |
JPA @Embedded 및 @Embeddable에 대해 알아보자 (0) | 2023.04.01 |
JPA 연관관계 방향성 (이론편) (0) | 2023.03.31 |