BE/SPRING

[Spring] JPA 지연로딩 예외 LazyInitializationException

hyu_na 2025. 3. 27. 01:33

Bookstore [1:N] BookstoreReview

Member    [1:N] BookstoreReview

 

-> BookstoreReview.java

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bookstore_id", nullable = false)
private Bookstore bookstore;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id",nullable = false)
private Member member;

 

-> Member.java

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookstoreReview> bookstoreReviews;
더보기

- mappedBy : BookstoreReview 엔티티의 member 필드와 연결된다.

- cascade = CascadeType.ALL : Member에서 수행한 작업(persist, merge, remove, refresh, detach)을 BookstoreReview에도 전파

- orphanRemoval = true : Member와 연관관계가 끊어진 BookstoreReview를 자동 삭제 

-> Bookstore.java

@OneToMany(mappedBy = "bookstore", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookstoreReview> bookstoreReviews;

 

 

Q) LazyInitializationException 

: FetchType.LAZY 로 연관관계가 설정된 필드- 지연 로딩으로 조회 =해당 필드의 엔티티를 프록시 객체로 조회

프록시 객체로 조회된 엔티티는 초기화 되어있지 않음 ->해당 엔티티를 조회하려 할 때 LazyInitializationException 발생

 

  "detail": "failed to lazily initialize a collection of role: com.capstone.bszip.Member.domain.Member.bookstoreReviews: could not initialize proxy - no Session"

 

 

  • 연관관계 어노테이션
    • @ManyToOne, @OneToOne 의 fetch 타입의 기본값 -  FetchType.EAGER
    • @OneToMany, @ManyToMany 의 fetch 타입의 기본값 - FetchType.LAZY 
  • 즉시로딩 
    • 엔티티를 조회할때 연관된 엔티티도 함께 조회
    • JPA에서는 즉시로딩을 최적화하기 위해 연관된 엔티티에 대한 추가 쿼리를 실행하기 보다는 가능하면 조인쿼리를 사용
  • 지연 로딩
    • 엔티티를 조회할때 사용하지 않는 연관관계도 함께 조회하는 건 비효율적
    • 연관된 객체의 멤버 변수에 프록시 객체를 넣어둠
    • 실제 사용할때 프록시 객체를 초기화하면서 데이터베이스 조회함
더보기

권장되지 않는 해결법

- OSIV open session in view

spring.jpa.open-in-view : true 

응답이 완료되거나 뷰가 렌더링 될 때까지 영속성 컨텍스트를 유지함

@Transactional 어노테이션이 사용된 메서드가 종료되도 컨트롤러 리턴 시점까지 세션을 유지해줌

-> 데이터베이스 커넥션을 이어서 사용하고 세션의 수명이 길어짐에 따라 성능과 확장성 측면에서 좋지 않음

 

- hibernate.enable_lazy_load_no_trans 활성화

spring.jpa.properties.hibernate.enable_lazy_load_no_trans:true

세션이 종료된 이후에도 다른 세션을 사용하여 데이터를 조회함

-> 여러 개의 지연로딩이 있는 경우에 대해 각각 새로운 데이터베이스 커넥션을 획득하여 조회하기 때문에 성능 측면에서 좋지 않고 커넥션 풀을 고갈시키는 장애를 유발할 수 있음

 

- 지연로딩을 즉시로딩으로 바꾸기

연관관계가 설정됨 엔티티의 FetchType.EAGER을 사용하여 즉시 로딩으로 엔티티를 조회하면 연관된 객체가 모두 영속성 컨텍스트에 생성되어 해결이 가능하지만 연관된 엔티티가 컬렉션과 같은 경우엔 성능 문제가 발생할 수 있으므로 권장되지 않음

 

해결 방법

1. @Transactional 사용하기

서비스 계층에서 @Transactional 어노테이션을 읽기전용 (readOnly = true) 로 사용하여 해결하는 방법

트랜잭션을 읽기 전용으로 사용하면 Dirty Checking을 하지 않아 성능을 향상시키고 데이터의 의도하지 않은 변경을 방지해줌 

JPA의 영속성 컨텍스트가 종료된 후에 연관관계가 설정된 엔티티를 조회하려고 할때 발생하기 때문에 세션이 유지되도록 트랜잭션을 설정해주면, 서비스 계층에서 트랜잭션을 시작하면 Repository 까지 해당 트랜잭션이 전파되어 사용됨

-> 지연로딩 시점까지 세션을 유지하여 사용할 수 있음

 

2.Transactional 내부에서 연관된 엔티티 조회하기

3.@EntityGraph 사용하기

 

 

 

 

 

레전드 .. 어이 없는 부분...

service 계층에 @Transactional추가했지만 에러 동일

+ controller 계층에도 @Transactional 추가했지만 에러 동일 +

controller의 메서드 시작부분에 출력코드 확인했으나 출력되지 않음

-> 

public ResponseEntity<?> createReview(
                                      @AuthenticationPrincipal Member member,
                                      @RequestBody BookstoreReviewRequest request
                                       ){..}

 

Member.domain.Member.bookstoreReviews: could not initialize proxy - no Session

즉, 파라미터의 Member member에서 지연로딩 예외가 발생한다고 추정

 

확인을 위해서

System.out.println("인증된 사용자 ID: " + member.getMemberId());

 

추가 .. 

난 member의 값에 접근할 수 있나 확인하고 싶었을 뿐인데 

이게 실행되면서 지연로딩이 일어나서 자동으로 영속성 컨텍스트를 통해 객체 초기화가 이루어지면서 오류가 해결됨 ....

이 코드를 지워도, 서버를 재실행해도, 

지연로딩 예외는 다신 일어나지 않고 있음 ........

정확한 해결책을 알지 못함 .. 다시 에러가 나길 바래야하는건가 ;;;

 

 

Reference)https://freestrokes.tistory.com/176