如何将sql本机查询结果映射到spring jpa存储库中的DTO?

Ke *_*Vin 7 java spring hibernate jpa spring-data-jpa

嗨,我想要实现的是在 java spring jpa 存储库中将 SQL 本机查询结果映射到我的 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);
}
Run Code Online (Sandbox Code Playgroud)

执行该函数后,出现此错误:

没有找到能够从类型 [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);
}
Run Code Online (Sandbox Code Playgroud)

第二个是错误:

无法提取结果集;SQL [不适用];嵌套异常是 org.hibernate.exception.SQLGrammarException: 无法提取 ResultSet

下面是我的 DTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StockAkhirDto {
   private Long productId;
   private String productCode;
   private Integer stockAkhir;
}
Run Code Online (Sandbox Code Playgroud)

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

Ke *_*Vin 33

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

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

@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);
}
Run Code Online (Sandbox Code Playgroud)

然后在我的服务类中,由于它作为元组返回,我必须手动一一映射列,这是我的服务函数,如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)

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


raz*_*zor 20

创建标准原生@Query

@Query(value = "select id, age, name FROM Person WHERE age=?1", nativeQuery=true)
List<PersonView> getPersonsByAge(int age);
Run Code Online (Sandbox Code Playgroud)

和一个接口

public interface PersonView {
    Long getId();
    Integer getAge();
    String getName();
}
Run Code Online (Sandbox Code Playgroud)

列按顺序(而不是名称)匹配。通过这种方式,您将拥有一个本机查询,没有实体,也没有太多的样板代码(也称为许多注释)。

然而,生成的视图(Jdk 代理等)的访问速度非常慢,我有一些代码对流进行了一些分组,它是 10 倍!比标准 DTO/Pojos 慢!所以最后我不再使用nativeQuery,而是使用JPQL:

SELECT new com.my_project.myDTO(p.id, p.age, p.name) .....
Run Code Online (Sandbox Code Playgroud)

  • Spring 可以弄清楚如何读取接口来填充投影,但不能读取 POJO。事情不可能这么简单吧?:) (6认同)

Ste*_*rnK 12

您可以使用适当的 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
{
}
Run Code Online (Sandbox Code Playgroud)

然后使用它:

@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
   );
}
Run Code Online (Sandbox Code Playgroud)