JPA : Collection Join 시 페이징 불가능

    Colletion Join이란?

    Collection Join은 일대다 관계에서의 Join으로 편리하게 이해하면 된다. 위의 그림을 예로 들어보자. Member Table과 Order Table은 One To Many의 연관관계를 가진다. 이 연관관계에서 Member를 기준으로 Order를 Join 한다고 생각해보자. Member는 Many인 Order를 객체 상태로 가지고 있기 때문에 Collection 형태로 보관하고 있다. 따라서 One을 기준으로 Many를 Join하기 때문에 Collection Join이라고 한다. 

     


    Collection Join 시 페이징 불가능

    Collection Join 시, 치명적인 단점이 발생한다. 바로 페이징이 불가능하거나, 가능하더라도 정합성이 떨어진다는 것이다. 위의 상황을 가정해보자. 이 때, Collection Join이 되기 때문에 Order를 기준으로 Member의 Row가 뻥튀기 된다. 실제 DB에 Member는 2개 밖에 없지만, 테이블을 Join하고 보니 4줄이 된다. 

    테이블의 Join이 완료된 후에 페이징 기능인 offset, limit 값이 설정되어 값을 가져온다. 이 때, Row가 이미 뻥튀기 되었기 때문에 offset, limit으로 값을 정한다고 하더라도 정합성이 있는 값이 나올 수 없다. 

    Distinct를 하더라도 소용이 없다. 왜냐하면 Application 단에서 PK 값을 기준으로 없애주는 것이기 때문에 DB단에서 이미 정합성이 떨어지는 값이 와서 정합성을 보장할 수 없다. 

     


    Collection Join, 테스트 코드 확인 

    List<Member> findMember = queryFactory.selectFrom(member).distinct()
            .join(member.orderList, order).fetchJoin()
            .offset(0)
            .limit(2)
            .fetch();

    Member와 OrderList를 Collection Join 한 후 페이징 쿼리를 작성했다. Collection Join 상태에서 페이징 쿼리를 보내면 다음과 같은 문구가 나온다. 

    WARN : firstResult/maxResults specified with collection fetch; applying in memory!

    문구는 Collection Join을 했기 때문에 DB의 값을 메모리로 가져와서 메모리에서 페이징 처리를 하고 있는 것으로 이해할 수 있다. 

    쿼리를 확인해보면 이는 더 명확해진다. 쿼리가 나갈 때, offset, limit의 값이 쿼리에 들어가지 않는 것을 확인할 수 있다. 결론은 Collection Join에서 페이징 처리를 하면, DB 단에서는 페이징 처리가 안되고 메모리에 값을 다 불러와서 메모리 상태에서 페이징을 처리한다.

     


    결론

    1. Collection Join을 하면 "One" 엔티티는 "Many" 기준으로 Row가 증가한다
    2. 1번에 의해서 One에 대한 페이징 기능이 정상적으로 동작할 수 없다.
    3. 페이징 처리 시, 실제 SQL 쿼리에는 페이징이 적용되지 않는다. 대신 메모리로 불러와서 페이징 처리를 해주는데, 1번에 의해서 페이징의 정합성은 매우 떨어진다. 

    댓글

    Designed by JB FACTORY