개발하는 자몽

[JPA] 지연로딩과 프록시 객체 조회 본문

Java/Spring

[JPA] 지연로딩과 프록시 객체 조회

jaamong 2023. 7. 21. 14:54

findById() 메서드로 L엔티티를 조회하고 가져온 엔티티의 getter 메서드에 접근했더니 아래와 같은 에러가 발생했다.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role

 

위 에러는 엔티티는 잘 가져왔지만, 이후 해당 엔티티를 통해 어떤 메소드를 호출하려고 할 때 이미 영속성 컨텍스트가 닫혀서 지연로딩을 할 수 없을 때 발생한다.

 

 

 

상황

application.yml

...

spring.jpa.open-in-view=false

...

spring.jpa.open-in-view 옵션에 관하여는 이 글을 참고하자 ( https://velog.io/@dnwlsrla40/JPA-Open-In-View )

 

 

Team 클래스

...
@Getter
@Entity
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "members")
    private List<Member> members;

  	...
}

 

Member 클래스

...
@Entity
@Getter
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
        
    @ManyToOne
    private Team team;

    ...
}

 

TeamController 클래스

...
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("teams")
public class TeamControlelr {

    private final TeamRepository teamRepository;

    @GetMapping("{id}/members")
    public void readTeamMembers(@PathVariable("id") Long id) {

        Team team = teamRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));

        for (Member membmer : team.getMember()) {
            log.info("team: {} - membmer: {}", team.getName(), membmer.getName()); //에러 발생
        }
    }
    
    ...

}

Team 엔티티를 가져오고 Team 엔티티와 연관관계가 매핑된 Member를 getMembers()로 조회할 때 에러가 발생한다.

 

원인 분석

...
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("teams")
public class TeamControlelr {

    private final TeamRepository teamRepository;

    @GetMapping("{id}/members")
    public void readTeamMembers(@PathVariable("id") Long id) {
    	
           	1. findById로 Team 조회 : repository에서 조회했을 땐 영속 상태(지연 로딩이므로 proxy 객체), open-in-view=false 이므로 controller에서 영속성 컨텍스트는 닫힌 상태(즉, 준영속 상태)

    		2. 조회한 Team (proxy) 객체의 getMembers() 요청
      			2-1. 요청 시 proxy 객체의 타겟은 null 이므로 getXxx() 메소드 없음, 존재하는 건 실제 객체의 참조.
      			2-2. 영속성 컨텍스트에 실제 Entity로 초기화를 요청
      			2-3. 영속성 컨텍스트가 DB에서 실제 객체를 찾는다
      			2-4. proxy 객체와 실제 객체 연결
      			2-5. 이제 proxy 객체는 getMembers()를 실제 객체의 getMembers()로 처리 가능

    		3. 그런데 여기서 proxy 객체는 준영속 상태이므로 초기화 요청 시 오류가 난다(즉, 2-2 에서 에러)

    		4. 해당 에러 발생!
        
    }
    
    ...

}

 

해결

영속성 컨텍스트를 유지하기 위해 해당 메서드에 @Transactional을 적용시키거나 spring.jpa.open-in-view 옵션을 true로 설정하면 된다.

 

SUMMARY

지연 로딩일 때 'spring.jpa.open-in-view=false'면 해당 에러가 발생

 

 

Comments