❌ Error Handling

Spring - Lazy Initialization Exception

신건우 2023. 4. 15. 13:41

❌ Spring - Lazy Initialization Exception


원인

Hibernate가 제공하는 Lazy Loading 전략을 사용할 때 발생할 수 있습니다. 
Lazy Loading은 연관된 엔티티를 실제로 사용하는 시점에 로딩하는 전략입니다. 
그러나, 트랜잭션이 끝나고 세션이 닫힌 후에 Lazy Loading을 시도하면,
Hibernate는 연관된 엔티티를 로딩할 수 없으므로 'Lazy Initialization Exception'을 발생시킵니다.
  • Proxy 객체로 채워진 Entity의 타입캐스팅을 시도할 때
  • 트랜잭션 내부에서 연관관계가 설정된 엔티티의 프로퍼티 접근이 안됬을때
  • 영속성 컨텍스트가 Transaction 범위 밖인 Controller에서 Lazy Loading을 시도할 때

N:1 관계 예시

  • 1쪽에서의 Fetch 전략은 Lazy
  • N쪽에서의 Cascade 전이 범위는 Persist,Remove 설정

N의 엔티티를 단건 조회했을때 Lazy Loading으로 연관된 1은 바로 초기화가 되지않고, 필요할때 정보가 채워지는 Proxy 객체로 채워집니다.


그럼 N의 Request가 Entity로 변환되고 Entity -> Response로 변환이 될때 정상적으로 변환이 될까?

  • 안됩니다.
  • 왜냐하면 1의 값을 써서 DTO를 채워야하는데 1의 값이 초기화가 되지 않은 상태여서 DTO 생성이 안됩니다.

해결

방법 1

  • Controller의 Entity -> Response 변환 로직을 트랜잭션 범위안에 있는 Service로 가져오기
  • 변환 시 쿼리를 날려 1의 값을 채우면 ResponseDTO가 정상적으로 만들어집니다.

방법 2

  • application.yml 파일 내부 JPA 속성 중 open-in-view 옵션을 true로 변경
  • 위 방법은 권장하지 않습니다. DB 성능 저하 및 장애 발생 가능성이 있고 임시 해결책입니다.

open-in-view옵션을 true로 설정 시, 영속성 컨텍스트가 트랜잭션 범위를 넘어선 레이어까지 살아있게 됩니다.

open-in-view'옵션을 false로 설정 시, 트랜잭션이 종료될 경우 영속성 컨텍스트도 닫히게 됩니다.

트랜잭션 내부(Service)에서 프록시 객체의 프로퍼티를 건드려 프록시 객체를 실제 사용을 하려고 하니,

연관된 데이터를 Lazy Fetch해서 불러 와 졌으며, 오류가 사라지고 어플리케이션이 잘 실행 되었습니다.

img

img


하지만 다음부턴 Fetch Join이나 Batch Size를 통해 해결할 생각입니다.