JPA - 페이징과 한계 돌파

희운·2025년 7월 30일

JPA

목록 보기
5/5

페이징 + 컬렉션 엔티티를 조회하려면 어떻게 해야할까

데이터가 뻥튀기 되지 않게 할수 없을까???

[
    {
        "orderId": 1,
        "name": "userA",
        "orderDate": "2025-07-30T18:23:28.899788",
        "orderStatus": "ORDER",
        "address": {
            "city": "서울",
            "street": "1",
            "zipcode": "1111"
        },
        "orderItems": [
            {
                "itemName": "JPA1 BOOK",
                "orderPrice": 10000,
                "count": 1
            },
            {
                "itemName": "JPA2 BOOK",
                "orderPrice": 20000,
                "count": 2
            }
        ]
    },
    {
        "orderId": 2,
        "name": "userB",
        "orderDate": "2025-07-30T18:23:28.945129",
        "orderStatus": "ORDER",
        "address": {
            "city": "진주",
            "street": "2",
            "zipcode": "2222"
        },
        "orderItems": [
            {
                "itemName": "SPRING1 BOOK",
                "orderPrice": 20000,
                "count": 3
            },
            {
                "itemName": "SPRING2 BOOK",
                "orderPrice": 40000,
                "count": 4
            }
        ]
    }
]

이렇게 order 를 기준으로 페이징을 할 경우는 어떻게 해야할까
쉽게 말해서 Order 엔티티는 OrderItem 과 일대다 관계이기 때문에
fetch join 을하고 페이징이 불가능하다.

  • 먼저 ToOne(OneToOne, ManyToOne) 관계를 모두 페치조인 한다. ToOne 관계는 row수를 증가시키지 않 으므로 페이징 쿼리에 영향을 주지 않는다.

  • 컬렉션은 지연 로딩으로 조회한다

  • 지연 로딩 성능 최적화를 위해 hibernate.default_batch_fetch_size , @BatchSize 를 적용한다.
    -hibernate.default_batch_fetch_size: 글로벌 설정
    - @BatchSize: 개별 최적화
    - 이 옵션을 사용하면 컬렉션이나, 프록시 객체를 한꺼번에 설정한 size 만큼 IN 쿼리로 조회한다

    spring:
      jpa:
        properties:
          hibernate:
            default_batch_fetch_size: 1000
    

    장점

  • 쿼리 호출 수가 1 + N 1 + 1 로 최적화 된다

  • 조인보다 DB 데이터 전송량이 최적화 된다. (Order와 OrderItem을 조인하면 Order가 OrderItem 만큼 중복해서 조회된다. 이 방법은 각각 조회하므로 전송해야할 중복 데이터가 없다.)

  • 페치 조인 방식과 비교해서 쿼리 호출 수가 약간 증가하지만, DB 데이터 전송량이 감소한다.

  • 컬렉션 페치 조인은 페이징이 불가능 하지만 이 방법은 페이징이 가능하다.

    결론

  • ToOne 관계는 페치 조인해도 페이징에 영향을 주지 않는다. 따라서 ToOne 관계는 페치조인으로 쿼리 수 를 줄이고 해결하고, 나머지는 hibernate.default_batch_fetch_size 로 최적화 하자.
    객체 그래프 탐색을 하다보면 알아서 batch fetch size 에 따라서 쿼리가 in 쿼리로 한꺼번에 가져올 것이다.

    XXToOne -> DTO로 바로 조회, Fetch Join
    XXToMany -> 페이징 필요없고 하나만 하는거면 컬렉션 페치조인 (최소한개만), 페이징할꺼면 다 XXXToOne Fetch 로 다가져오고, 나머지는 그냥 객체 그래프 탐색으로 지연로딩 + batch fetch size

참고로 컬렉션 페치 조인일때는 DTO 로 해결하지말자.
그리고 DTO 로 조회역시 SQL 을 짜는것과 유사하기 때문에 select 절에 컬렉션이 오는것 자체가 불가능해서 다른 방법을 해야한다.

대부분의 문제는 위에서 말한 엔티티를 직접 조회해서 DTO 로 변환하자.
그리고 컬렉션 fetch join 은 페이징을 안할꺼면 상관이 없지만 , 1개만 사용하자.
그리고 2개 이상사용해야하거나, 페이징을 해야되는 상황이면
ToOne 만 fetch join 하고 ToMany 는 default_batch_fetch_size 로 해결하자.

profile
기록하는 공간

0개의 댓글