개발을 하다 보면 JPA와 Hibernate를 사용하는 환경에서 LazyInitializationException을 마주치게 될 때가 있습니다. 특히 연관 데이터를 지연 로딩(Lazy Loading)으로 설정했을 때 트랜잭션 종료 이후 데이터를 로드하려 하면 이 문제가 발생합니다. 이번 글에서는 LazyInitializationException이 무엇인지, 왜 발생하는지, 그리고 이를 해결하기 위해 필요한 트랜잭션, 영속성 컨텍스트, 준영속성에 대해 설명합니다.
1. LazyInitializationException이란?
LazyInitializationException은 JPA에서 지연 로딩(Lazy Loading)으로 설정된 연관 데이터를 트랜잭션 종료 이후 로드하려고 할 때 발생하는 예외입니다. Lazy Loading은 실제로 데이터가 필요할 때만 쿼리를 실행하여 데이터를 가져오는 방식이지만, 트랜잭션이 종료된 후에는 영속성 컨텍스트가 닫혀 데이터베이스에 접근할 수 없으므로 예외가 발생합니다.
2. 예제 코드
Circle 엔티티와 CircleSchedule 엔티티를 사용한 예제를 통해 설명합니다.
@Entity
class Circle(
@Id
@GeneratedValue
val id: UUID = UUID.randomUUID(),
val title: String,
@OneToMany(mappedBy = "circle", fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
val circleSchedules: MutableList<CircleSchedule> = mutableListOf()
)
CircleSchedule 엔티티
@Entity
class CircleSchedule(
@Id
@GeneratedValue
val id: UUID = UUID.randomUUID(),
@ManyToOne(fetch = FetchType.LAZY)
val circle: Circle,
val dayOfWeek: String,
val startTime: String,
val endTime: String
)
3. 문제 상황: LazyInitializationException 발생
아래 코드에서 circle.circleSchedules는 Lazy Loading으로 설정되어 있습니다. 트랜잭션이 종료된 후 circleSchedules에 접근하려 하면 LazyInitializationException이 발생합니다.
문제 코드
fun getCircleSchedules(circleId: UUID): List<CircleSchedule> {
val circle = circleRepository.findById(circleId)
.orElseThrow { NotExistException("Circle not found") }
return circle.circleSchedules // LazyInitializationException 발생 가능
}
발생 이유
- circle.circleSchedules는 FetchType.LAZY로 설정되어 있어, 기본적으로 데이터베이스 쿼리를 실행하지 않습니다.
- 트랜잭션이 종료되면 영속성 컨텍스트도 종료되므로, 지연 로딩된 데이터를 가져올 수 없습니다.
4. LazyInitializationException 해결 방법
1) @Transactional 사용
트랜잭션 내에서 지연 로딩 데이터에 접근하도록 @Transactional을 적용합니다.
@Transactional(readOnly = true)
fun getCircleSchedules(circleId: UUID): List<CircleSchedule> {
val circle = circleRepository.findById(circleId)
.orElseThrow { NotExistException("Circle not found") }
return circle.circleSchedules // Lazy Loading 가능
}
2) Open-Session-In-View 설정
Spring Boot에서 spring.jpa.open-in-view 설정을 활성화하면 트랜잭션이 종료된 이후에도 Lazy Loading이 가능합니다. 하지만 이 방법은 권장되지 않습니다.
application.yml 설정:
spring:
jpa:
open-in-view: true
5. 영속성 컨텍스트와 트랜잭션
영속성 컨텍스트(Persistence Context)
영속성 컨텍스트는 JPA 엔티티를 관리하며 데이터베이스와의 상호작용을 처리하는 캐시 역할을 합니다.
- 트랜잭션이 시작되면 영속성 컨텍스트가 활성화됩니다.
- 트랜잭션이 종료되면 영속성 컨텍스트도 종료됩니다.
- Lazy Loading은 영속성 컨텍스트 내에서만 작동합니다.
트랜잭션 종료 이후의 Lazy Loading
트랜잭션이 종료되면 영속성 컨텍스트가 종료되고, Lazy Loading 데이터는 로드할 수 없습니다. 이를 방지하려면 트랜잭션 내부에서 데이터를 로드해야 합니다.
6. 결론
- LazyInitializationException은 트랜잭션 종료 이후 Lazy Loading 데이터를 로드하려 할 때 발생합니다.
- @Transactional 등을 활용하여 LazyInitializationException을 방지할 수 있습니다.
- 영속성 컨텍스트와 트랜잭션의 생명주기를 이해하면 Lazy Loading 문제를 더 잘 해결할 수 있습니다.
'JPA' 카테고리의 다른 글
Kotlin, JPA 환경에서 Entity 설계에 대한 고민 (0) | 2024.09.08 |
---|---|
자바 ORM 표준 JPA 프로그래밍 기본편 정리 1탄 (0) | 2022.08.07 |
@Enumerated (0) | 2022.07.25 |