• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

16:10 페이징 쿼리, mvc 관련 질문드립니다.

22.05.04 01:24 작성 조회수 175

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

안녕하세요 영한님 jpa 강의 정말 재미있게 잘 듣고 있습니다. 이번에 페이징 한계돌파 강의를 들으면서 실습을 하던 중 16:10쯤에 저의 개발환경에서 발생한 쿼리가 영한님의 개발환경에서 발생한 쿼리와 다른 것 같아서 질문드립니다. where __row__ >= 2 and __row__ < 102; 쿼리로 페이징이 적용되어 결과는 원하는 대로 나왔지만, 예상과는 조금 다르게 아래와 같은 서브 쿼리가 나갔습니다. 데이터베이스 방언 문제는 아닌 것 같고 jpa 버전 문제일까요?
 
2022-05-04 01:14:49.015 DEBUG 21484 --- [nio-8081-exec-6] org.hibernate.SQL : with query as (select inner_query.*, row_number() over ( order by current_timestamp) as __row__ from ( 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_.order_status as order_st3_6_0_, member1_.city as city2_4_1_, member1_.street as street3_4_1_, member1_.zip_code as zip_code4_4_1_, member1_.name as name5_4_1_, delivery2_.city as city2_2_2_, delivery2_.street as street3_2_2_, delivery2_.zip_code as zip_code4_2_2_, delivery2_.status as status5_2_2_ from orders order0_ inner join member member1_ on order0_.member_id=member1_.member_id inner join delivery delivery2_ on order0_.delivery_id=delivery2_.delivery_id ) inner_query ) select order_id1_6_0_, member_i1_4_1_, delivery1_2_2_, delivery4_6_0_, member_i5_6_0_, order_da2_6_0_, order_st3_6_0_, city2_4_1_, street3_4_1_, zip_code4_4_1_, name5_4_1_, city2_2_2_, street3_2_2_, zip_code4_2_2_, status5_2_2_ from query where __row__ >= ? and __row__ < ? 2022-05-04 01:14:49.035 INFO 21484 --- [nio-8081-exec-6] p6spy : #1651594489035 | took 3ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/jpashopping with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( 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_.order_status as order_st3_6_0_, member1_.city as city2_4_1_, member1_.street as street3_4_1_, member1_.zip_code as zip_code4_4_1_, member1_.name as name5_4_1_, delivery2_.city as city2_2_2_, delivery2_.street as street3_2_2_, delivery2_.zip_code as zip_code4_2_2_, delivery2_.status as status5_2_2_ from orders order0_ inner join member member1_ on order0_.member_id=member1_.member_id inner join delivery delivery2_ on order0_.delivery_id=delivery2_.delivery_id ) inner_query ) select order_id1_6_0_, member_i1_4_1_, delivery1_2_2_, delivery4_6_0_, member_i5_6_0_, order_da2_6_0_, order_st3_6_0_, city2_4_1_, street3_4_1_, zip_code4_4_1_, name5_4_1_, city2_2_2_, street3_2_2_, zip_code4_2_2_, status5_2_2_ from query where __row__ >= ? and __row__ < ? with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( 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_.order_status as order_st3_6_0_, member1_.city as city2_4_1_, member1_.street as street3_4_1_, member1_.zip_code as zip_code4_4_1_, member1_.name as name5_4_1_, delivery2_.city as city2_2_2_, delivery2_.street as street3_2_2_, delivery2_.zip_code as zip_code4_2_2_, delivery2_.status as status5_2_2_ from orders order0_ inner join member member1_ on order0_.member_id=member1_.member_id inner join delivery delivery2_ on order0_.delivery_id=delivery2_.delivery_id ) inner_query ) select order_id1_6_0_, member_i1_4_1_, delivery1_2_2_, delivery4_6_0_, member_i5_6_0_, order_da2_6_0_, order_st3_6_0_, city2_4_1_, street3_4_1_, zip_code4_4_1_, name5_4_1_, city2_2_2_, street3_2_2_, zip_code4_2_2_, status5_2_2_ from query where __row__ >= 2 and __row__ < 102; 2022-05-04 01:14:49.038 DEBUG 21484 --- [nio-8081-exec-6] org.hibernate.SQL : select 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=? 2022-05-04 01:14:49.040 INFO 21484 --- [nio-8081-exec-6] p6spy : #1651594489040 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/jpashopping select 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 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=11; 2022-05-04 01:14:49.044 DEBUG 21484 --- [nio-8081-exec-6] org.hibernate.SQL : 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=? 2022-05-04 01:14:49.046 INFO 21484 --- [nio-8081-exec-6] p6spy : #1651594489046 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/jpashopping 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=9; 2022-05-04 01:14:49.048 DEBUG 21484 --- [nio-8081-exec-6] org.hibernate.SQL : 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=? 2022-05-04 01:14:49.050 INFO 21484 --- [nio-8081-exec-6] p6spy : #1651594489050 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/jpashopping 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=10;

또한 api가 아닌 mvc 패턴으로 컬렉션을 find할 때에는 dto가 아닌 엔티티를 그대로 repository에서 찾아서 view 화면에 넘기는데, 이 때는 쿼리 성능 최적화를 어떻게 할 수 있을까요? (타임리프로 order, orderItem의 결과를 보여준다고 가정할 때) 

 

답변 1

답변을 작성해보세요.

1

안녕하세요. 딸기초콜릿님

데이터베이스 방언 때문에 페이징 쿼리 최적화를 위해 해당 구문이 사용됩니다^^ 데이터베이스 방언을 변경해보시면 결과가 달라지는 것을 확인하실 수 있을거에요.

 

추가로 API가 아니어도 fetch join, batch size 등을 활용해서 최적화가 가능합니다.

감사합니다.

방언 문제였군요... 답변 정말 감사합니다 영한님 ~ 

영한님의 강의에서 배운 것들을 토대로 간단하게 게시판을 만들고 있는데 게시물(1)과 댓글(다)을 일대다 관계로 설정했습니다. 홈 화면에서는 일반적인 카페 게시판처럼 페이징을 통해서 [ 게시판 pk, 게시판 제목, 해당 게시판 댓글 수, 게시판 조회수 ] x 10개를 띄우는데 아무래도 댓글은 게시물의 입장에서 일대다 관계이므로 페치 조인으로 하면 페이징이 안 되는 것으로 알고 있습니다. batch size를 글로벌 설정으로 넣어두고 실행했는데도 in 쿼리가 안 나가네요... ㅜ 

그래서 하단처럼 강제 초기화를 하거나 api 강의에서 배운 것처럼 dto로 가져와서 해결은 했지만 뭔가 이게 맞는 방식인지는 잘 모르겠네요 ㅎ...

 

강제 초기화

List<Article> articles = articleService.findArticlesByPageDesc();

articles.forEach(article -> article.getReplies().forEach(
reply -> reply.getId()
));

 

딸기초콜릿님

API에 엔티티를 반환하셨을까요?

강의에서 설명드린 것 처럼 API에 엔티티를 직접 반환하면 안되고, DTO로 변환해서 반환하셔야 합니다.

감사합니다.

안녕하세요 영한님 mvc 패턴에서 dto로 만들거나 하는게 문제가 아니였네요...

 

 @GetMapping("/")
public String home(@SessionAttribute(name = "memberId", required = false) Long memberId,
Model model) {

List<Article> articles = articleService.findArticlesByPageDesc();

// articles.forEach(article -> article.getReplies().forEach(
// reply -> reply.getId()
// ));

model.addAttribute("articles", articles);

if (memberId == null) {
return "home";
}

Member member = memberService.findById(memberId);
model.addAttribute("member", member);

return "loginHome";
}

이렇게 실행하면 home 화면에서는 댓글을 불러오는 쿼리가 batch_fetch_size로 in 쿼리로 최적화되지만, loginHome 화면에서는 이상하게 최적화가 되지 않네요... 

Member member = memberService.findById(memberId);
model.addAttribute("member", member);

이 코드가 articleService.findArticles한 결과의 영속성 컨텍스트에 영향을 미치는 것인가요?

물론 주석을 해제해서 게시물의 댓글 불러오는 쿼리를 강제로 초기화하면 모두 in 쿼리로 최적화 됩니다. 

 

안녕하세요 영한님 혹시나 해서 memberServiceImpl단에서 findById(Long memberId)의 트랜잭션을 읽기전용으로 변경했더니 바로 최적화가 되네요~

안녕하세요. 딸기초콜릿님

다음 이슈인 것 같아요.

https://www.inflearn.com/questions/427216

감사합니다.