如何在spring jpa仓库中将sql native查询结果Map到DTO?

eagi6jfj  于 12个月前  发布在  Spring
关注(0)|答案(6)|浏览(114)

嗨,我试图实现的是将SQL原生查询结果Map到我的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. hib.exception。SQL语法异常:无法提取结果集
下面是我的DTO:

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

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

omtl5h9j

omtl5h9j1#

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

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

hm2xizp92#

我找到了一种不常见的方法,但是当我尝试使用QueryDsl来解决这个问题时,我发现了一种名为**“Tuple”的数据类型,但是如果你和我一样刚刚开始使用QueryDsl,我不会推荐你使用它。让我们专注于如何使用“元组”**
我将返回类型改为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);
}

然后在我的服务类中,由于它以元组的形式返回,我必须手动逐个Map列,下面是我的服务函数:

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开头,这样我就可以在仓库级别保持查询的整洁。但我会接受斯特恩克的答案作为公认的答案,因为这种方式也工作,我会把我的答案在这里,如果有人需要这样的东西

v9tzhpje

v9tzhpje3#

创建标准原生@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) .....
qgelzfjb

qgelzfjb4#

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

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

应该可以。

6jjcrrmo

6jjcrrmo5#

基于Sternk答案的另一个有效选项如下所示
您可以使用适当的sql结果集Map定义以下命名的本机查询:
resources/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
   );
}
rjee0c15

rjee0c156#

Sping Boot 框架可以非常简化地将原生SQL查询的结果Map到Spring JPA存储库中基于接口的DTO中,您可以遵循以下步骤

1.定义DTO接口:

创建一个接口,为要从本机SQL查询结果Map的属性定义getter方法。接口方法应该与SQL查询中使用的列别名匹配。

public interface MyDTO {
    Long getId();
    String getName();
    // Other getter methods for other properties
}

2.定义原生SQL查询:

在Spring JPA存储库接口中,使用带有原生SQL查询的@Query注解,该查询返回DTO接口所需的列。

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

    @Query(value = "SELECT id as id, name as name FROM my_table WHERE ...", nativeQuery = true)
    List<MyDTO> findDataUsingNativeQuery();
}

现在可以使用服务层的findDataUsingNativeQuery()方法检索Map到DTO接口的数据。

相关问题