从存储库接口检索列表到DTO列表类的最佳方法是什么

时间:2018-09-01 06:07:24

标签: spring-boot spring-data-jpa spring-data dto

DTO包中有一个类名DailyElectricity,其中包含 max,min,sum,getter和setter的平均值

public class DailyElectricity implements Serializable {

  private static final long serialVersionUID = 3605549122072628877L;


  private LocalDate date;

  private Long sum;

  private Double average;

  private Long min;

  private Long max;


}

有一个接口,其作用是从数据库获取数据

@RestResource(exported = false)
public interface HourlyElectricityRepository 
    extends PagingAndSortingRepository<HourlyElectricity,Long> {
  Page<HourlyElectricity> findAllByPanelIdOrderByReadingAtDesc(Long panelId,Pageable pageable);


  @Query("SELECT max(a.generatedElectricity), sum(a.generatedElectricity),min(a.generatedElectricity),max(a.generatedElectricity)  from HourlyElectricity a where DATE_FORMAT(reading_at,'%Y-%m-%d')=DATE_FORMAT(CURDATE()-1,'%Y-%m-%d') and  panel_id=:panelId")
  List<DailyElectricity> getStaticsDailyElectricity(@Param("panelId")Long panelId);

}

它没有任何异常,但是当我调用API时,它会给出

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.techtrial.dto.DailyElectricity]

它不能隐藏到dto类

2 个答案:

答案 0 :(得分:1)

问题在于,Spring无法解决如何将查询结果转换为期望的自定义​​对象 DailyElectricity ;为了使这种映射成为可能,您需要做两件事:

  1. 创建一个构造函数,以便您可以创建一个新对象并通过查询的每一行得出的值对其进行初始化:

    public DailyElectricity (Long max,Long sum,Long min,Double average){    
        this.sum=sum;
        this.average=average;
        this.min=min;
        this.max=max;   
    }
    
  2. 然后在HourlyElectricityRepository

    中对您的查询使用以下结构
    @Query("SELECT new com.example.DailyElectricity( max(a.generatedElectricity), sum(a.generatedElectricity),min(a.generatedElectricity),avg(a.generatedElectricity))  from HourlyElectricity a where DATE_FORMAT(reading_at,'%Y-%m-%d')=DATE_FORMAT(CURDATE()-1,'%Y-%m-%d') and  panel_id=:panelId")
       List<DailyElectricity> getStaticsDailyElectricity(@Param("panelId")Long panelId);
    
    • 请注意我在查询中使用的程序包名称( com.example.DailyElectricity ),并确保在测试前使用与项目相对应的正确程序包名称。

答案 1 :(得分:0)

如果您使用的是class-based projections(DTO),则它们必须包含一个构造函数。尝试将其添加到您的DTO中。

但是最好使用Lombok来避免样板代码:

@Value
public class DailyElectricity {
  private LocalDate date;
  private Long sum;
  private Double average;
  private Long min;
  private Long max;
}

另一种方法是使用interface-based projections

public interface DailyElectricity {
  LocalDate getDate();
  Long getSum();
  Double getAverage();
  Long getMin();
  Long getMax();
}

IMO最好使用它们,因为它们更简单并且具有一些优点(请参阅提供的手册)。

请注意,一种好的做法是在使用投影时在查询中使用别名。它们必须与投影中的对应字段名称/获取器匹配,例如:

"select max(a.generatedElectricity) as max, ..."

已更新

不幸的是,在Spring Boot 2.0+中,基于类的投影无法像expected一样工作(与SB 1.5+不同-请参见可以工作的demo)。 在修复此错误之前,我们可以使用DTO constructor in the query

更新2

我错了-当我们在自定义查询中使用基于类的投影时,无论Spring Boot的版本如何,都必须使用其构造函数