【问题标题】:How to map sql native query result into DTO in spring jpa repository?如何将sql本机查询结果映射到spring jpa存储库中的DTO?
【发布时间】:2021-02-21 23:43:36
【问题描述】:

嗨,我想要实现的是在 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);
}

一旦我执行了这个函数,我得到了这个错误:

没有找到能够从类型转换的转换器 [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 [不适用];嵌套异常是 org.hibernate.exception.SQLGrammarException:无法提取 结果集

下面是我的 DTO:

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

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

【问题讨论】:

  • AFAIK,当nativeQuery=true 时,所有这些方法都不起作用

标签: java spring hibernate jpa spring-data-jpa


【解决方案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
   );
}

【讨论】:

  • 嗨,我似乎无法导入 "SqlResultSetMapping","ConstructorResult" 注释我应该添加什么 pom 来导入该注释?
  • @KeVin 这是标准的 jpa 注释(@SqlResultSetMapping 在 jpa 1.0 中添加,@ConstructorResult 在 jpa 2.1 中添加)我已将导入添加到代码 sn-p。
【解决方案2】:

我找到了一种不常见的方法,但是当我尝试使用 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);
}

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

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 的答案作为接受的答案,因为这种方式也很有效,如果有人需要这样的东西,我会在这里保留我的答案

【讨论】:

  • 这是处理一些更复杂的 Spring 注解时的简单版本。
  • 相当整洁!感谢您的建议!
  • 谢谢!一整天都在寻找这样一个简单的解决方案。 JPA 存储库可能会让人头疼
【解决方案3】:

创建标准原生@Query

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

还有一个界面

public interface PersonView {
    Long getId();
    Integer getAge();
    String getName();
}

列按顺序匹配(而不是按名称)。 这样,您就有了一个原生查询,没有实体,也没有太多样板代码(也就是很多注释)。

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

SELECT new com.my_project.myDTO(p.id, p.age, p.name) .....

【讨论】:

  • Spring 可以弄清楚如何读取接口来填充投影,但不是 POJO。事情不可能很简单吧? :)
【解决方案4】:

第二个变体非常接近。您只需删除构造函数表达式的别名:

new com.b2bwarehouse.Dto.RequestDto.StockDto.StockAkhirDto(
    stock_akhir.product_id, 
    stock_akhir.product_code, 
    SUM(stock_akhir.qty)
)

应该可以。

【讨论】:

  • 您确定可以在 Native Query 中混合使用“new Entity(...)”吗?
  • 我没有意识到这是一个原生查询。它不适用于本机查询。
  • 我添加了对我有用的答案(本机查询和 DTO 接口 - 不是实体)
【解决方案5】:

基于 Sternk 答案的另一个有效选项如下

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

资源/META-INF/orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
                 version="2.0">
    <named-native-query name="find_stock_akhir_dto" result-class="com.fullyqualified.name.SomeEntity"
                        result-set-mapping="stock_akhir_dto">
        <query><![CDATA[
            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]]></query>
    </named-native-query>
</entity-mappings>
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.ConstructorResult;
import javax.persistence.ColumnResult;

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

【讨论】:

    猜你喜欢
    • 2019-07-25
    • 2021-08-13
    • 2022-01-11
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 2018-03-30
    相关资源
    最近更新 更多