728x90

JPA는 개발하는 입장에서 매우 편리하고 설정만 잘하면 편할거라고 생각하는데  사용하기 편하고 쉬운만큼 성능적인 측면에서 생각하지 못한 이슈가 발생할 수 있다.이번에 정리할 이슈는 즉시로딩(EAGER Loading)과 지연로딩(LAZY Loading)이다.

@OneToOne 매핑시에 지연로딩(LAZY Loading)로 설정해도 즉시로딩(EAGER Loading)로 작동하는 경우가 있다.이번 포스팅에서는 발생하는 이슈에 대해서 해결책을 적어보려한다.


일단 정상적으로 작동하는 예제이다. 일대일 단방향 매핑으로 외래키를 가지고 있는 USER가 연관관계 주인이다. 여기서 유저를 조회하면 어떻게 쿼리가 작동하는지 보자 

 

이처럼 이름으로 조회하면 

 

 

  select
        user0_.user_id as user_id1_1_,
        user0_.name as name2_1_,
        user0_.studygroup_id as studygro3_1_ 
    from
        user user0_ 
    where
        user0_.name=?
 
 

지연 로딩이 정상적으로 작동한다. 그러면 도대체 어디에서 이슈가 발생할까?

이제 이슈에 대해서 다뤄보겠다.

 

위 예시는 USER와 STUDYGROUP이 일대일 양방향 관계를 가지고 있다.여기에서 또한 연관관계의 주인은 외래키를 가지고있는 USER이다.

여기서 USER를 조회하면 정상적으로 지연로딩이 작동한다.

그러나 STUDYGROUP을 조회하면 아래처럼 지연로딩이 작동하지 않고 즉시로딩이 작동한다.

 

 

select
        studygroup0_.id as id1_4_0_ 
    from
        studygroup studygroup0_ 
    where
        studygroup0_.id=?

  select
        user0_.user_id as user_id1_1_,
        user0_.name as name2_1_,
        user0_.studygroup_id as studygro3_1_ 
    from
        user user0_ 
    where
        user0_.studygroup_id=?

 

 

fetch전략을 지연로딩으로 설정했음에도 불구하고 즉시로딩으로 유저를 조회하는 비효율적이고 불필요한 쿼리가 발생한다.

 

이러한 이슈의 원인은 지연로딩은 로딩되는 시점에 엔티티를 프록시객체로 가져온다.여기서는 문제가 발생하지 않지만 이후에는 StudyGroup 객체를 가져오는 시점에는 실제 객체가 사용된다.

지연로딩으로 설정이 되어있는 엔티티를 조회할 때에는 프록시로 감싸서 동작한다.그러나 프록시의 한계로 null을 감싸지못한다. 그래서 이러한 문제가 발생한다.

 

StudyGroup라는 테이블에는 USER를 참조할 수 있는 컬럼이 존재하지 않는다. 따라서 StudyGroup는 어떤 USER에 의해 참조되고 있는지 알 수 없다.

StudyGroup가 어떤 USER에 의해 참조되고 있는지 알 수 없다는 뜻은 만약 USER가 null이더라도 StudyGroup는 이 사실을 알지 못한다는 것이다.

만약 USER가 null이 아니라고 해도, StudyGroup의 입장에서는 USER가null인지 null이 아닌지 확인할 방법이 없다.

따라서 USER의 존재 여부를 확인하는 쿼리를 실행하기 때문에 지연 로딩으로 동작하지 않는 것이다.

 


해결책 

  • 양방향 매핑이 반드시 필요한지 검토해본다.
  • 다대일이나 일대다로 관계를 변경할 수 있는지 고민해본다.
  • StudyGroup을 조회할 때 User도 같이 조회한다.(fetch join)

해결 방법이 명확하게 존재하는 것은 아니다.그래서 상황에 맞게 적용해서 사용하는것이 가장 좋다고 생각한다.

 

728x90