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

What is the best way to Retrieve List from repository interface to DTO list Class

DTO 包中有 class 名称 DailyElectricity,它包含 最大值、最小值、总和、平均值 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 class

问题是 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) 并确保在测试前使用与您的项目对应的正确包名称。

如果您使用 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 最好使用它们,因为它们更简单并且有一些优点(请参阅提供的手册)。

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

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

已更新

不幸的是,在 Spring Boot 2.0+ 中,基于 class 的投影无法像 expected (unlike SB 1.5+ - see may working demo). Until this bug will be fixed we can use DTO constructor in the query 那样工作。

已更新 2

我错了 - 当我们将基于 class 的投影与自定义查询一起使用时,我们必须使用其构造函数 无论 Spring Boot[=38= 的版本如何].