❌ 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해서 불러 와 졌으며, 오류가 사라지고 어플리케이션이 잘 실행 되었습니다.
하지만 다음부턴 Fetch Join이나 Batch Size를 통해 해결할 생각입니다.