如何将sql原生查询结果映射到春季jpa存储库中的DTO中?

2022-09-03 16:40:40

嗨,我试图实现的是将SQL本机查询结果映射到Java Spring jpa存储库中的DTO中,我如何正确地做到这一点?我尝试了几个代码,但它不起作用,这是我尝试过的:

第一次尝试 :

@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<StockAkhirDto> findStockAkhirPerProductIn(@Param("warehouseCode") String warehouseCode, @Param("productCodes") Set<String> productCode);
}

执行函数后,我得到这个错误:

找不到能够从类型 [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] 转换为类型 [com.b2bwarehouse.Dto.RequestDto.StockDto.StockAkhirDto] 的转换器

第二次尝试 :

@Repository
public interface StockRepository extends RevisionRepository<Stock, Long, Integer>, JpaRepository<Stock, Long> {
    
   @Query(value = "SELECT new com.b2bwarehouse.Dto.RequestDto.StockDto.StockAkhirDto(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<StockAkhirDto> findStockAkhirPerProductIn(@Param("warehouseCode") String warehouseCode, @Param("productCodes") Set<String> productCode);
}

第二个是错误:

无法提取结果集;SQL [n/a];嵌套异常是 org.hibernate.exception.SQLGrammarException: 无法提取 ResultSet

以下是我的DTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StockAkhirDto {
   private Long productId;
   private String productCode;
   private Integer stockAkhir;
}

我应该如何更正我的代码?那么,我可以将结果输入到我的 DTO 中吗?


答案 1

可以使用适当的 sql 结果集映射定义以下命名的本机查询:

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
{
}

然后使用它:

@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
   );
}

答案 2

我找到了一种不常见的方法,但是当我尝试使用QueryDsl来解决这个问题时,我发现数据类型称为“元组”,但是如果您像我一样刚刚入门,我不会推荐QueryDsl。让我们专注于如何使用“元组”来做到这一点

我将我的返回类型更改为元组,这是我的存储库的样子:

@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);
}

然后在我的服务类中,由于它作为元组返回,我必须手动逐个映射列,这是我的服务函数看起来像这样:

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;
}

列字段以0开头,这样我就可以在存储库级别保持查询整洁。但是我会接受SternK的答案作为接受的答案,因为这种方式也有效,如果有人需要这样的东西,我会把我的答案留在这里。


推荐