select order0_.order_id as order_id1_6_0_, member1_.member_id as member_i1_4_1_, delivery2_.delivery_id as delivery1_2_2_, order0_.delivery_id as delivery4_6_0_, order0_.member_id as member_i5_6_0_, order0_.order_date as order_da2_6_0_, order0_.status as status3_6_0_, member1_.city as city2_4_1_, member1_.street as street3_4_1_, member1_.zipcode as zipcode4_4_1_, member1_.name as name5_4_1_, delivery2_.city as city2_2_2_, delivery2_.street as street3_2_2_, delivery2_.zipcode as zipcode4_2_2_, delivery2_.status as status5_2_2_from orders order0_inner join member member1_ on order0_.member_id = member1_.member_idinner join delivery delivery2_ on order0_.delivery_id = delivery2_.delivery_idselect orderitems0_.order_id as order_id5_5_0_, orderitems0_.order_item_id as order_it1_5_0_, orderitems0_.order_item_id as order_it1_5_1_, orderitems0_.count as count2_5_1_, orderitems0_.item_id as item_id4_5_1_, orderitems0_.order_id as order_id5_5_1_, orderitems0_.order_price as order_pr3_5_1_from order_item orderitems0_where orderitems0_.order_id = ?select item0_.item_id as item_id2_3_0_, item0_.name as name3_3_0_, item0_.price as price4_3_0_, item0_.stock_quantity as stock_qu5_3_0_, item0_.artist as artist6_3_0_, item0_.etc as etc7_3_0_, item0_.author as author8_3_0_, item0_.isbn as isbn9_3_0_, item0_.actor as actor10_3_0_, item0_.director as directo11_3_0_, item0_.dtype as dtype1_3_0_from item item0_where item0_.item_id = ?select item0_.item_id as item_id2_3_0_, item0_.name as name3_3_0_, item0_.price as price4_3_0_, item0_.stock_quantity as stock_qu5_3_0_, item0_.artist as artist6_3_0_, item0_.etc as etc7_3_0_, item0_.author as author8_3_0_, item0_.isbn as isbn9_3_0_, item0_.actor as actor10_3_0_, item0_.director as directo11_3_0_, item0_.dtype as dtype1_3_0_from item item0_where item0_.item_id = ? ...반복
@RepositorypublicclassOrderRepository {publicList<Order> findAllWithMemberDelivery(int offset,int limit) {returnem.createQuery("select o from Order o"+" join fetch o.member m"+" join fetch o.delivery d",Order.class)// 페이징을 적용한다..setFirstResult(offset).setMaxResults(limit).getResultList(); }}
spring:jpa:properties:hibernate:# 미리 in 절로 땡겨 올 데이터 개수default_batch_fetch_size:1000
select order0_.order_id as order_id1_6_0_, member1_.member_id as member_i1_4_1_, delivery2_.delivery_id as delivery1_2_2_, order0_.delivery_id as delivery4_6_0_, order0_.member_id as member_i5_6_0_, order0_.order_date as order_da2_6_0_, order0_.status as status3_6_0_, member1_.city as city2_4_1_, member1_.street as street3_4_1_, member1_.zipcode as zipcode4_4_1_, member1_.name as name5_4_1_, delivery2_.city as city2_2_2_, delivery2_.street as street3_2_2_, delivery2_.zipcode as zipcode4_2_2_, delivery2_.status as status5_2_2_from orders order0_inner join member member1_ on order0_.member_id = member1_.member_idinner join-- 페이징이 적용된다. delivery delivery2_ on order0_.delivery_id = delivery2_.delivery_id limit ?offset ?select orderitems0_.order_id as order_id5_5_1_, orderitems0_.order_item_id as order_it1_5_1_, orderitems0_.order_item_id as order_it1_5_0_, orderitems0_.count as count2_5_0_, orderitems0_.item_id as item_id4_5_0_, orderitems0_.order_id as order_id5_5_0_, orderitems0_.order_price as order_pr3_5_0_from order_item orderitems0_-- in 절로 땡겨온다.where orderitems0_.order_id in ( ?, ? )select item0_.item_id as item_id2_3_0_, item0_.name as name3_3_0_, item0_.price as price4_3_0_, item0_.stock_quantity as stock_qu5_3_0_, item0_.artist as artist6_3_0_, item0_.etc as etc7_3_0_, item0_.author as author8_3_0_, item0_.isbn as isbn9_3_0_, item0_.actor as actor10_3_0_, item0_.director as directo11_3_0_, item0_.dtype as dtype1_3_0_from item item0_-- in 절로 땡겨온다.where item0_.item_id in ( ?, ? )
페이징이 잘 적용되었다.
default_batch_fetch_size
order 2개와 그 아래의 데이터를 가져오는 데 쿼리가 3개만 나갔다.
이전에는 order 마다 item 쿼리 2개씩 총 4개가 나갔는데 확 줄었다.
pk 기준으로 in 절을 날리기 때문에 쿼리 최적화로 빠르게 가져온다.
fetch_size를 100으로 정했는데 데이터가 1000개면 쿼리는 10개가 나간다.
비교
V3
진짜 한 방 쿼리로 모든 걸 가져온다.
컬렉션 때문에 중복 데이터가 많아져 부하 이슈가 있다.
V3.1
중복 없이 최적화 되어서 나온다.
데이터를 몇 천개씩 퍼올릴 때 사용하면 좋다.
장점
쿼리 호출 수가 1+N에서 1+1로 최적화된다.
fetch join과 비교해 쿼리 호출 수가 약간 증가하지만 중복이 제거되어 DB 데이터 전송량이 감소한다.
컬렉션 fetch join은 페이징이 불가능하지만 이 방법은 페이징이 가능하다.
ToOne 관계는 fetch join 해도 페이징에 영향을 주지 않는다.
따라서 ToOne 관계는 fetch join으로 쿼리 수를 줄이고, 나머지는 default_batch_fetch_size로 최적화 한다.