[Spring Data JPA] fetch join에서 Lazy Loading이 작동하지 않는 문제

허석진·2023년 3월 9일
0
post-thumbnail

Spring Data JPA를 사용하다 보면 fetch = FetchType.LAZY을 설정했음에도 불구하고 Lazy Loading이 적용되지 않는 상황이 발생 할 수 있다. 이 포스팅에서는 해당 문제가 발생하는 경우 2가지를 적어놔 이 글을 본 다른이가 나처럼 개고생하는 일이 없었으면 좋겠다...

우선 해당 포스팅에서는 다음과 같은 @ManyToOne, @OneToOne의 기본값이 Eager Loading이니까 fetch = FetchType.LAZY을 작성해 주고 fetch join을 하라든가 Lazy Loading된 값을 접근해서 추가적인 Query를 발생시켜서 그렇다는 얘기는 제외하고 하겠다.

Lazy Loading이 동작하지 않는 2가지 경우

  1. @OneToOne 양방향 연관관계에서 주인이 아닌 쪽 entity를 조회할 때 참고 링크
  2. @ManyToOne 연관관계를 PK가 아닌 Column으로 FK로 설정한 경우 참고 링크

1번의 경우

Lazy Loading 자체가 Proxy 객체를 이용한 것이고 이런 Proxy 객체는 연관관계가 존재하는 경우에 할당해주는 것이다.
그렇다면 JPA 구현체 입장에서 주인이 아닌 쪽에서 조회하는 경우, DB에 관련 Column이 없는데 연관관계가 존재하는지 안하는지 알 수 있을까?(null인지 아닌지)

아니, 절대 알수 없다. 직접 DB 뒤져 찾아보긴 전까지는 null을 넣어야하는지 Proxy 객체를 넣어야하는지 판단할 수 없다. 그렇기 때문에 이 경우, 자동적으로Eager Loading이 되는 것이다.

해결책

1

Stack Overflow의 답변을 보면 native Hibernate XML 매핑에서는 constrained 속성을 true로 설정한 one-to-one 매핑을 선언하여 이를 수행할 수 있다고한다.
그러나 이는 연관관계의 주인이 아닌 쪽이 @OneToOne(optional=false,fetch=FetchMode.LAZY)를 써야하며 XML 직접 수정해야된다. 그 방법에 대한 링크는 여기에 남겨두겠다.

2

연관관계를 끊고 비즈니스 로직에서 연관관계가 있는 것 처럼 처리하는 방법
즉 DB 상으로는 실제 연관관계가 없지만, 엔티티에는 서로를 확인할 수 있는 key를 추가해 비즈니스 로직 단에서 관리하고 필요한 경우에만 join 하여 받아오는 방법이다.
이 방법은 DB가 제공하는 데이터 중복 방지나 일관성 유지등의 기능이 사라진다는 것을 알고 있어야한다.

3

One-to-one을 포기하고 One-to-Many & Many-to-One으로 변경하는 방법
이 방법은 경우에 따라 다르겠지만 억지로 변경하는 경우 설계 상 좋지않을 경우가 많을 것이다.

4

그냥 fetch join을 통해 Query라도 줄여보는 방법

2번의 경우

예시

위 예시와 같은 상황에서 select B를 하는 경우 이후 C에 대한 추가적인 select문이 발생한다. 그 이유를 알아보려 노력했으나 결국 찾는데 실패했으나 같은 질문을 많이 만나볼 수는 있었다. (위에 참코링크)
놀랍게도 관련링크가 8년 전 질문이고, 1년 전에 수정되었으나 명쾌한 답이 나와 있지않다.
Hibernate의 버그라는 답변도 달려있지만 지금까지 수정이 안되있는 것을 보면 어떤 이유가 있어 의도된 스펙이라는 생각이 들기도 한다.

해결책

1

PK가 아닌 FK를 PK로 변경하는 방법
PK가 아닌 FK를 사용해서 Lazy Loading이 불가능하니 가능하다면 PK를 FK로 변경해 가능하게하는 방법이다.
이런 경우 ERD를 좀 수정 할 필요가 있다.

2 (위에 1 - 2 해결법과 동일)

연관관계를 끊고 비즈니스 로직에서 연관관계가 있는 것 처럼 처리하는 방법
즉 DB 상으로는 실제 연관관계가 없지만, 엔티티에는 서로를 확인할 수 있는 key를 추가해 비즈니스 로직 단에서 관리하고 필요한 경우에만 join 하여 받아오는 방법이다.
이 방법은 DB가 제공하는 데이터 중복 방지나 일관성 유지등의 기능이 사라진다는 것을 알고 있어야한다.


마치며

백엔드 프로그래머가 제일 나타내기 쉬운 역량 중 하나인 데이터 효율을 쫓다가 여기까지 와버렸으나 결국 명확한 이유를 찾지는 못했다... 좀 더 알아보기 위해서는 Hibernate 공식 사이트를 뒤져봐야 알것같다 ㅋㅋ..
이와 같은 문제를 겪고 사람이 있다면 이 글을 본고 빠르게 문제를 해결 할 수 있었으면 좋겠다.

0개의 댓글