본문 바로가기
SpringBoot/JPA

JPA로 객체를 가져올 때 LazyLoading을 만난 썰

by devebucks 2020. 9. 27.
728x90

안녕하세요. 

 저는 단위테스트를 작성하고 있었습니다. 양방향 관계를 가지는 주인 객체에서 종속 객체의 데이터가 들어가 있는지 확인하는 과정에서 테스트코드가 통과되지 않는 현상이 발생해서 이 부분을 공부를 해보려고 합니다.

 

저의 개발 환경은 다음과 같았습니다.

  • spring-boot-starter-web:2.3.1
  • spring-boot-starter-data-jpa:2.3.1
  • spring-boot-postgresql:42.2.14
  • junit:junit:4.13

 

에러 내용

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.studyolle.domain.Event.enrollments, could not initialize proxy - no Session

 

테스트 코드의 작성 내용은 다음과 같습니다.

1. 사용 객체

  • Event 주인객체 (모임) @OneToMany(mappedBy = "event") - 주ㅏ
  • Enrollment 종속 객체 (모임에 등록한 멤버의 등록 정보 객체) @ManyToOne
  • 양방향 관계

2. 테스트 내용

  • 모임에 참가를 신청한 Enrollment객체가 정상적으로 등록이 되는지 확인. 

 

두 가지 케이스로 Event 객체를 가져왔을 때, 하나는 에러가 발생하였고, 다른 하나는 테스트를 성공하였습니다.

[테스트 실패] JPA를 통해서 Event 객체를 받아올 경우.

테스트코드에서 새로운 Event객체를 생성하는 부분.

createEvent1()이 동작하고 id값을 받아오면, Transaction이 종료된 상태가 됩니다. findById()로 event를 가져오면 그 event는 Detached상태의 객체입니다. Detached여서 안가져오는지... LazyLoading때문인지는 의문입니다. -> 생각해본 결과, 데이터베이스에서 JPA를 통해서 가져오는 객체이기 때문에 LazyLoading으로 Event객체의 종속 객체인 enrollments객체의 데이터를 가져오지 않은 것이었습니다.

 

[테스트 성공] 자바로 생성한 Event객체를 사용할 경우.

테스트코드에서 새로운 Event객체를 생성하는 부분.

createEvent()로 Event객체를 반환합니다. 이때, 반환되는 부분을 보시면 JpaRepository.save()메서드로 event를 저장합니다. 

즉,

createEvent()메서드의 return으로 반환되는 event는 JPA를 통해서 데이터베이스로부터 event을 가져온 것이 아닌, PersistentContext에 1차 캐시로 저장된 event를 반환해주는 것입니다. 

테스트 클래스에 정의된 createEvent()메서드

그래서 LazyLoading에 상관없이 Event객체의 종속객체인 enrollments객체의 데이터가 초기화 값으로 ArrayList() 인스턴스로를가질 수 있었습니다.

Event객체에서 enrollments가 정의된 부분.
종속객체 enrollments의 크기가 0

 

이번 이슈를 고민하면서 JPA의 LazyLoading에 대해서 더 잘 이해가 되었습니다.

JPA를 통해서 객체를 받아오면 해당 객체와 관계를 가지는(@OneToMany) 객체 값은 LazyLoading으로 기본 설정되어 있으면 값을 가져오지 않는다는 것을 잘 이해할 수 있었습니다.

728x90

댓글