• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

복잡한 통계쿼리도 JPA로 가능한가요?

21.09.02 10:25 작성 조회수 4.41k

2

기선님 쉽게 설명해주시는 강의를 보며 참 많은 도움이 되고있습니다.

실제 제가 만들고 있는 모델관 다르지만 질문하기 위해 예시를든다면 , 쇼핑몰을 예로  어떤 상품들이 있고 그것에 대한 판매가 이뤄진다고 가정할때 엔티티는 상품,  주문,  결제등이 있을수 있겠죠. 

이때 '상품들의 월별 판매량'을 통계 내야 한다고 치고 

---------------------------------------

상품명    1월  2월   3월  4월  5월 ... 합

--------------------------------------

상품A      20   15      4    7        8      104

상품B      1       1       10   2      1       25

----------------------------------------

이런식의 통계 쿼리를 짜야 할경우 JPA로 가능한지?

QueryDSL 등을 써서라도 가능한지?

감이 안잡힙니다.

답변 1

답변을 작성해보세요.

0

JPA에서 쿼리를 만드는 방법이 꽤 다양한데요. QueryDSL이 그중에서도 꽤나 SQL에 가깝우면서, 타입세이프하게 JPQL 쿼리를 만들어 낼 수 있습니다. 최악의 경우엔 JPA를 통해 네이티브 쿼리를 실행할 수도 있으니 JPA 못한다고 말하긴 어렵지만, 우선은 저런 뷰를 만들 수 있는 쿼리를 SQL로 먼저 작성해 보시고 그 다음에 QueryDSL, JPQL, Criteria 등으로 옮겨보길 시도해 보시면 어떨까요?

빠른 답변 감사합니다.

그럼 스프링 코드상으로는 저 통계 데이터를 담기위한 DTO? VO? (뭐가 정확한 표현인지 모르겠습니다. 스프링도 초급이라) 를 만들어서 거기에 받으면 되는건가요?

쿼리는 이미 mybatis를 통해 운영중인걸 스프링부트 + JPA로 옮기려하는것이기 때문에 준비되어있는데 쿼리가  

SELECT IFNULL(a.frstt_nm,'-') frstt_nm
, IFNULL(a.first_name,'-') first_name
, IFNULL(a.second_name,'-') second_name
, IFNULL(a.third_name,'-') third_name
, COUNT(*) tcnt
, SUM(a.mon_01) mon01
, SUM(a.mon_02) mon02
, SUM(a.mon_03) mon03
, SUM(a.mon_04) mon04
, SUM(a.mon_05) mon05
, SUM(a.mon_06) mon06
, SUM(a.mon_07) mon07
, SUM(a.mon_08) mon08
, SUM(a.mon_09) mon09
, SUM(a.mon_10) mon10
, SUM(a.mon_11) mon11
, SUM(a.mon_12) mon12
FROM (
SELECT
<choose>
<when test='srchWardType != null and srchWardType == "2"'>
wi2.first_name
, wi2.second_name
, wi2.third_name
, wi2.frstt_nm
</when>
<otherwise>
wi.first_name
, wi.second_name
, wi.third_name
, wi.frstt_nm
</otherwise>
</choose>
, IF(MONTH(REGIST_DT)='01',1,0) mon_01
, IF(MONTH(REGIST_DT)='02',1,0) mon_02
, IF(MONTH(REGIST_DT)='03',1,0) mon_03
, IF(MONTH(REGIST_DT)='04',1,0) mon_04
, IF(MONTH(REGIST_DT)='05',1,0) mon_05
, IF(MONTH(REGIST_DT)='06',1,0) mon_06
, IF(MONTH(REGIST_DT)='07',1,0) mon_07
, IF(MONTH(REGIST_DT)='08',1,0) mon_08
, IF(MONTH(REGIST_DT)='09',1,0) mon_09
, IF(MONTH(REGIST_DT)='10',1,0) mon_10
, IF(MONTH(REGIST_DT)='11',1,0) mon_11
, IF(MONTH(REGIST_DT)='12',1,0) mon_12
FROM sfm_emergency_rescue_info r
INNER JOIN mcm_pat_info p ON p.EGNCR_NO = r.EGNCR_NO
LEFT JOIN ward_view wi ON wi.SMRT_INSTT_ID = r.SMRT_INSTT_ID
LEFT JOIN ward_view wi2 ON p.SMRT_INSTT_ID = wi2.SMRT_INSTT_ID
WHERE R.EGNCR_PROCESS_SE_CODE = '2'
<include refid="statsMgmtListSearch"></include>
) a
GROUP BY a.first_name, a.second_name, a.third_name
ORDER BY a.first_name, a.second_name, a.third_name

이런느낌입니다.  이정도는 그 최악의 경우인 네이티브 쿼리를 안쓰고 

JPQL이나 QueryDSL로 만들어 낼수 있나요?

완강을 하고  대부분의 기능들은 어떻게 하면 되겠다 감이 잡혔는데 통계만큼은 도통 감이 안잡히네요 ㅜㅜ

특히 이해가 안가는부분은  제가 이해한대로라면  JPA로 쿼리가 돌아가고 그 결과를 엔티티객체( Account, Post등) 이 받아서 

퍼시스턴트 상태가 된다인데

통계같은 경우는 여러 테이블들의 조인의 결과로  엔티티가 아닌 일반 Data객체 (VO, DTO 등) 으로 받아야 된다는 점에서 멘붕이  오는겁니다.  혹시 강좌중에 관련 부분 설명을 해주신게 있는데 제가 놓친걸까요 ( 다른강의 들까지 전부 완강한 상태입니다. )

JPA가 제공하는 Projection이라는 기능을 사용하면 DTO로 결과를 맵핑받을 수 있습니다. 

그리고 Month()와 같은 DB 함수를 호출해서 쓰고 계신데, 제 강의에서 그 부분은 다루지 않은것 같은데 JPA 스팩상 DB 함수를 쓸 수도 있긴합니다. https://docs.oracle.com/middleware/1221/toplink/jpa-extensions-reference/jpql.htm#TLJPA615

분기 분은 코드상에서 if 문을 쓰시면 될것 같고 나머지 group by나 order by 그리고 subselect 등은 모두 지원하니까 조금 더 공부하시다보면 옮길 수 있을 것 같기도 하네요.

다만! 이런 쿼리는 네이티브 쿼리를 쓰시는게 적절한 경우로 보이네요. 결코 최악의 선택이 아니에요. 오히려 JPQL이나 QueryDSL로 옮긴 코드가 SQL 보다 이해하기 어렵게 될 수도 있고 성능에서 큰 장점이 없는 경우도 있습니다.

아 Projection 파트는 테이블의 모든 컬럼 결과를 다 가져오지 않아도되고 특정 컬럼만 가져올수도 있다 정도로만 이해하고 대강넘겼는데 지금 다시 보고 오니 결과 값을 매핑하여  별도의 DTO 클래스 또는 인터페이스로 결과를 가져와 연산시킬수 있다는것도 알려주셨네요.

이러면 저같은경우는 서브쿼리의 결과를 가져와 SUM 부분은 자바코드로 더해주는식으로 하면 쿼리가 더 줄수도 있지 않을까라는 생각을 해보게됩니다

친절한 답변 감사합니다ㅜㅜ 짱짱!