Spring Data JPA를 사용하다 보면 fetch = FetchType.LAZY
을 설정했음에도 불구하고 Lazy Loading
이 적용되지 않는 상황이 발생 할 수 있다. 이 포스팅에서는 해당 문제가 발생하는 경우 2가지를 적어놔 이 글을 본 다른이가 나처럼 개고생하는 일이 없었으면 좋겠다...
우선 해당 포스팅에서는 다음과 같은 @ManyToOne
, @OneToOne
의 기본값이 Eager Loading
이니까 fetch = FetchType.LAZY
을 작성해 주고 fetch join
을 하라든가 Lazy Loading
된 값을 접근해서 추가적인 Query를 발생시켜서 그렇다는 얘기는 제외하고 하겠다.
Lazy Loading
자체가 Proxy 객체
를 이용한 것이고 이런 Proxy 객체
는 연관관계가 존재하는 경우에 할당해주는 것이다.
그렇다면 JPA 구현체 입장에서 주인이 아닌 쪽에서 조회하는 경우, DB에 관련 Column이 없는데 연관관계가 존재하는지 안하는지 알 수 있을까?(null인지 아닌지)
아니, 절대 알수 없다. 직접 DB 뒤져 찾아보긴 전까지는 null
을 넣어야하는지 Proxy 객체
를 넣어야하는지 판단할 수 없다. 그렇기 때문에 이 경우, 자동적으로Eager Loading
이 되는 것이다.
이 Stack Overflow의 답변을 보면 native Hibernate XML 매핑에서는 constrained 속성을 true로 설정한 one-to-one 매핑을 선언하여 이를 수행할 수 있다고한다.
그러나 이는 연관관계의 주인이 아닌 쪽이 @OneToOne(optional=false,fetch=FetchMode.LAZY)
를 써야하며 XML 직접 수정해야된다. 그 방법에 대한 링크는 여기에 남겨두겠다.
연관관계를 끊고 비즈니스 로직에서 연관관계가 있는 것 처럼 처리하는 방법
즉 DB 상으로는 실제 연관관계가 없지만, 엔티티에는 서로를 확인할 수 있는 key를 추가해 비즈니스 로직 단에서 관리하고 필요한 경우에만 join 하여 받아오는 방법이다.
이 방법은 DB가 제공하는 데이터 중복 방지나 일관성 유지등의 기능이 사라진다는 것을 알고 있어야한다.
One-to-one을 포기하고 One-to-Many & Many-to-One으로 변경하는 방법
이 방법은 경우에 따라 다르겠지만 억지로 변경하는 경우 설계 상 좋지않을 경우가 많을 것이다.
그냥 fetch join을 통해 Query라도 줄여보는 방법
위 예시와 같은 상황에서
select B
를 하는 경우 이후 C
에 대한 추가적인 select
문이 발생한다. 그 이유를 알아보려 노력했으나 결국 찾는데 실패했으나 같은 질문을 많이 만나볼 수는 있었다. (위에 참코링크)
놀랍게도 관련링크가 8년 전 질문이고, 1년 전에 수정되었으나 명쾌한 답이 나와 있지않다.
Hibernate
의 버그라는 답변도 달려있지만 지금까지 수정이 안되있는 것을 보면 어떤 이유가 있어 의도된 스펙이라는 생각이 들기도 한다.
PK가 아닌 FK를 PK로 변경하는 방법
PK가 아닌 FK를 사용해서 Lazy Loading
이 불가능하니 가능하다면 PK를 FK로 변경해 가능하게하는 방법이다.
이런 경우 ERD를 좀 수정 할 필요가 있다.
연관관계를 끊고 비즈니스 로직에서 연관관계가 있는 것 처럼 처리하는 방법
즉 DB 상으로는 실제 연관관계가 없지만, 엔티티에는 서로를 확인할 수 있는 key를 추가해 비즈니스 로직 단에서 관리하고 필요한 경우에만 join 하여 받아오는 방법이다.
이 방법은 DB가 제공하는 데이터 중복 방지나 일관성 유지등의 기능이 사라진다는 것을 알고 있어야한다.
백엔드 프로그래머가 제일 나타내기 쉬운 역량 중 하나인 데이터 효율을 쫓다가 여기까지 와버렸으나 결국 명확한 이유를 찾지는 못했다... 좀 더 알아보기 위해서는 Hibernate
공식 사이트를 뒤져봐야 알것같다 ㅋㅋ..
이와 같은 문제를 겪고 사람이 있다면 이 글을 본고 빠르게 문제를 해결 할 수 있었으면 좋겠다.