네이티브 쿼리에서 DTO를 매핑하는 가장 쉬운 방법
복잡한 통계 쿼리를 사용할 때는 네이티브 쿼리를 사용하고는 한다. 그리고 그 결과를 DTO에 매핑하기 위해 JPA에서 제공하는 natvieQuery를 사용해도 되는 데 DTO매핑이 쉽지 않다. 그래서 QLRM을 사용하는 방법을 소개하겠다. QLRM을 사용하면 DTO 매핑이 매우 쉬워진다.
일단 해당 라이브러리를 추가한다.
//https://github.com/72services/qlrm
implementation 'org.qlrm:qlrm:4.0.1'
그리고는 해당 repository에서 바로 사용하면 된다.
QLRM은 JapResultMapper
를 제공하는데 두개의 메소드를 제공한다.
- uniqueResult : 단일 ResultSet를 처리한다.
- list : 리스트형 ResultSet를 처리한다.
@PersistenceContext
private EntityManager em;
public OrderStatisticDto getOrderStatisticsDto(String startDate, String endDate) {
String sql = new StringBuilder()
.append(" SELECT")
.append(" COUNT(O.ORDER_ID) AS totalCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'WAITING' THEN 1 ELSE 0 END ) AS waitingCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'DOING' THEN 1 ELSE 0 END ) AS doingCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'DONE' THEN 1 ELSE 0 END ) AS doneCount")
.append(" FROM ORDERS AS O")
.append(" WHERE O.ORDER_DATE >= :startDate AND O.ORDER_DATE <= :endDate")
.toString();
JpaResultMapper jpaResultMapper = new JpaResultMapper();
Query nativeQuery = em.createNativeQuery(sql)
.setParameter("startDate", startDate)
.setParameter("endDate", endDate);
return jpaResultMapper.uniqueResult(nativeQuery, OrderStatisticDto.class);
}
네이티비 쿼리에서 얻으려는 결과를 dto와 바로 매핑처리하면 된다.
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class OrderStatisticsDto {
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd")
private Date orderDate;
private Long totalCount;
private BigDecimal waitingCount;
private BigDecimal doingCount;
private BigDecimal doneCount;
}
주의할 점은 JpaResultMapper에서 쿼리에 사용된 COUNT 등은 DTO에서 Long으로 처리되지만, SUM, AVG 등과 같은 데이터베이스 함수를 사용하는 경우 BigDecimal로 가져와야 한다.
public List<OrderStatisticsDto> getOrderStatisticsDtoList(String startDate, String endDate) {
String sql = new StringBuilder()
.append(" SELECT")
.append(" O.ORDER_DATE AS orderDate")
.append(" ,COUNT(O.ORDER_ID) AS totalCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'WAITING' THEN 1 ELSE 0 END ) AS waitingCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'DOING' THEN 1 ELSE 0 END ) AS doingCount")
.append(" ,SUM(CASE WHEN O.ORDER_STATUS = 'DONE' THEN 1 ELSE 0 END ) AS doneCount")
.append(" FROM ORDERS AS O")
.append(" WHERE O.ORDER_DATE >= :startDate AND O.ORDER_DATE <= :endDate")
.append(" GROUP BY O.ORDER_DATE")
.toString();
JpaResultMapper jpaResultMapper = new JpaResultMapper();
Query nativeQuery = em.createNativeQuery(sql)
.setParameter("startDate", startDate)
.setParameter("endDate",endDate);
return jpaResultMapper.list(nativeQuery, OrderStatisticsDto.class);
}
위에서 처럼 여러개의 resultSet를 가져올 경우 jpaResultMapper.list로 처리하면 된다.
이렇게 하면 Native 쿼리를 DTO에 매핑하는 것이 매우 간단해 진다.