How to map sql native query result into DTO in spring jpa repository?
Solution 1:
You can define the following named native query with appropriate sql result set mapping:
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.ConstructorResult;
import javax.persistence.ColumnResult;
@Entity
@NamedNativeQuery(
name = "find_stock_akhir_dto",
query =
"SELECT " +
" stock_akhir.product_id AS productId, " +
" stock_akhir.product_code AS productCode, " +
" SUM(stock_akhir.qty) as stockAkhir " +
"FROM book_stock stock_akhir " +
"where warehouse_code = :warehouseCode " +
" AND product_code IN :productCodes " +
"GROUP BY product_id, product_code, warehouse_id, warehouse_code",
resultSetMapping = "stock_akhir_dto"
)
@SqlResultSetMapping(
name = "stock_akhir_dto",
classes = @ConstructorResult(
targetClass = StockAkhirDto.class,
columns = {
@ColumnResult(name = "productId", type = Long.class),
@ColumnResult(name = "productCode", type = String.class),
@ColumnResult(name = "stockAkhir", type = Integer.class)
}
)
)
public class SomeEntity
{
}
and then use it:
@Repository
public interface StockRepository extends RevisionRepository<Stock, Long, Integer>, JpaRepository<Stock, Long> {
@Query(name = "find_stock_akhir_dto", nativeQuery = true)
List<StockAkhirDto> findStockAkhirPerProductIn(
@Param("warehouseCode") String warehouseCode,
@Param("productCodes") Set<String> productCode
);
}
Solution 2:
i find a way which is not usual, but i find data type called "Tuple" when i try to use QueryDsl to solved this problem, but i won't recommend QueryDsl if you are just getting started just like me. Lets focus on how i do it with "Tuple"
i changed my return type to Tuple, here is how my repository looked like :
@Repository
public interface StockRepository extends RevisionRepository<Stock, Long, Integer>, JpaRepository<Stock, Long> {
@Query(value = "SELECT stock_akhir.product_id AS productId, stock_akhir.product_code AS productCode, SUM(stock_akhir.qty) as stockAkhir "
+ "FROM book_stock stock_akhir "
+ "where warehouse_code = (:warehouseCode) "
+ "AND product_code IN (:productCodes) "
+ "GROUP BY product_id, product_code, warehouse_id, warehouse_code", nativeQuery = true)
List<Tuple> findStockAkhirPerProductIn(@Param("warehouseCode") String warehouseCode, @Param("productCodes") Set<String> productCode);
}
and then here in my service class, since it's returned as Tuple, i have to map the column one by one manually, here is my service function looked like :
public List<StockTotalResponseDto> findStocktotal() {
List<Tuple> stockTotalTuples = stockRepository.findStocktotal();
List<StockTotalResponseDto> stockTotalDto = stockTotalTuples.stream()
.map(t -> new StockTotalResponseDto(
t.get(0, String.class),
t.get(1, String.class),
t.get(2, BigInteger.class)
))
.collect(Collectors.toList());
return stockTotalDto;
}
the column field start with 0, in this way i can keep my query neat at Repository level. But i will accept SternK answer as the accepted answer because that way worked too, i will keep my answer here if someone need something like this