wonwonk
477
2021-05-20 17:44:20 작성 2021-05-20 18:25:00 수정됨
10
266

JPA 연관관계 질문드립니다.


안녕하세요 JPA로 조회를 하는중에 불필요한 쿼리들이 날라가는게 해결이 안되 질문드립니다.


A entity B entity C entity가 있습니다.


A entity에서 B entity를 사용하고 one To one 단뱡향입니다. 

그리고 B entity에서 C entity를 사용하고 one To one 단방향 입니다.


원하는 쿼리는 

A entity를 조회하면 A + B 테이블을 조회를 하는건데요

A entity를 findByid를 통해 조회를 하면 

A+B 쿼리가 날라가고 그 다음에 B+C 쿼리도 한번더 날라갑니다.


B+C 쿼리는 안날라가게 하고싶은데 방법에 대한 조언부탁드립니다.

좀더 추가하면 A entity 를 조회할때 A+B만 하고싶은데

A+B쿼리 이후에 B내부에 있는 연관 entity를 한번더 조회해서 B+C 쿼리가 날라가는 상황입니다

좀더 여러 관계가 있지만 질문내용만 추려 아래 소스입니다.

public class A {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;


    @OneToOne

    @JoinColumn(name = "b_id")

    private B b;

public class B {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;


    @OneToOne

    @JoinColumn(name = "c_id")

    private C c;

public class C {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;



0
  • 답변 10

  • 신입개발자444호
    193
    2021-05-20 17:49:05
    B클래스에서 @OneToOne 어노테이션에 페치 추가하면  되나요? A 클래스처럼요
  • ReuNyo
    296
    2021-05-20 17:49:26

    B와 C의 연관 관계의 fetchType을 LAZY로 줘보세요. OneToOne default가 EAGER이므로 즉시 로딩이 되어 B+C쿼리도 날라가는 것으로 보여지네요.

  • 신입개발자444호
    193
    2021-05-20 17:54:16

    ReuNyo 

    저도 같은생각했는데 맞았으면 좋겠네요. 저래도 안되면 프록시 써야할까요?

  • wonwonk
    477
    2021-05-20 18:01:55

    위에 답변대로 처리를 했는데 잘 되지는 않습니다. 

    원하지 않는 연관관계에 fetch = FetchType.LAZY 를 이용해봤지만 죄회를 하고있네요

    지연로딩으로만 해결이 안되나 봅니다. 뭔가 연관이 되있는것 같은데 찾기가 힘드네요 



  • shirohoo
    1k
    2021-05-20 18:29:11 작성 2021-05-20 18:30:06 수정됨
    원투원은 지연로딩이되려면 몇가지 조건이 필요합니다.
    우선 단방향이어야하고요 optional=false를 줘보세요.
    조건이 몇개더있긴한데 애지간하면 이정도선에서 지연로딩이 됩니다. 퇴근길이라 자세히쓰기가 어렵네요
  • ReuNyo
    296
    2021-05-20 18:31:17
    신입개발자444호
    저같은 경우는 예상치 못한 N+1 쿼리 호출 때문에 프록시를 사용하며 필요한 경우에만 즉시로딩을 하고 있습니다.

    wonwonk
    혹시 C 엔티티를 사용하고 있는 라인이 있는지 한번 확인해보시는 것도 추천드립니다.
    그리고 혹시 eager일 때와 lazy일 때 동일 쿼리가 나가는지요? 쿼리가 조인되어 나가는지 따로따로 나가는지 궁금하네요
  • wonwonk
    477
    2021-05-20 18:44:58

    답변 감사드립니다 다시 해봐야겠습니다

    지연로딩을 하기위해 one to one은 전부 단방향 사용을 하고 있습니다

    A entity를 조회할때 A+B만 쿼리가 날라가길 원하는데 

    B 내부에 있는 C entity의 쿼리가 추가로 날라가는게 이해가 되지를 않네요 

  • 제타건담
    7k
    2021-05-20 19:05:46

    findById 메소드를 사용하지 마시고..@Query annotation을 사용해서 직접 JPQL을 작성해서 조회해보시면 어떨까요..?

    https://www.baeldung.com/spring-data-jpa-query

    JPQL는 조회해야 할 클래스의 범위가 한계가 주어지기 때문에 연관관계만 믿고 접근하지는 않을듯 한데..



  • shirohoo
    1k
    2021-05-20 19:12:39 작성 2021-05-20 19:14:55 수정됨

    @OneToOne의 optional속성은 기본적으로 true입니다.

    optional = true는 nullable과 같습니다.

    이렇게되면 @OneToOne에 매핑된 엔티티가 null일수도 있기때문에 프록시를 채워줄수 없습니다.

    왜냐하면 프록시를 채워버리면 반대로 null인 경우를 해결할 수 없기때문입니다.


    그렇기때문에 optional=false라는 속성을 명시하여 엔티티가 notnull임을 보장해준다면

    하이버네이트는 @OneToOne에 null이 참조될 가능성을 완벽히 배제할수 있게되어 프록시를 채워주게됩니다.

    이렇게되면 fetch=FetchType.LAZY 속성이 무시되지 않으며 정상적으로 지연로딩이 동작하게됩니다.


    B내부에 있는 C 엔티티의 쿼리가 추가로 날아가는게 이해가 되지 않으신다고 하셨는데 위와 같은맥락입니다.


    아무런 명시를 하지 않으셨기에 optional이 기본값인 true이므로 하이버네이트는 쿼리를 날려보지 않고서는 null을 채워야할지 프록시를 채워줘야할지 알 수 없기때문에 EAGER로 동작하게 되는겁니다.

    OneToOne 관계에 대해서는 무조건적이라고 해도 될만큼 이런방식으로 동작하기때문에 JPA에서는 OneToOne사용을 절대 지양하며, OneToOne을 쓸바에 차라리 OneToMany 사용을 권장합니다.

    가장 좋은건 설계를 제대로해서 ManyToOne을 사용하는것이고요.


    boolean optional() default true;


    (Optional) Whether the association is optional. If set to false then a non-null relationship must always exist.



    @OneToOne(fetch = FetchType.LAZY, optional = false) // Not Null
  • wonwonk
    477
    2021-05-20 20:14:38

    친절한 답변 감사합니다 더 해봐야겠습니다

  • 로그인을 하시면 답변을 등록할 수 있습니다.