如何使用 java 流对 JPA 存储库中的字段进行分组和平均并放入新集合中

How to group and average fields from JPA repo and put into new collection using java streams

我需要计算一周中所选日期的平均入住率(例如,所有星期五 - 每分钟)。由于缺少 Date/Time 函数,我没有找到解决此问题的任何 JPQL/Querydsl 方法。所以我正在尝试使用 Java 流。我的(简化)对象:

class Occupancy {
   private LocalDateTime timeStamp;
   private int occupied;
}

我的回购:

@Query("select o from Occupancy o")
public Stream<Occupancy> streamAllOccupancies();

样本:

try ( Stream<Occupancy> stream = repository.streamAllOccupancies()) {

   Function<Occupancy,LocalTime> OccupancyMinutesGrouping = (Occupancy o) -> {
        return o.getDateTime().toLocalTime().truncatedTo(ChronoUnit.MINUTES);
   };


   Map<LocalTime,Double> avgMap = stream
      .filter( o -> o.getDateTime().getDayOfWeek() == DayOfWeek.MONDAY) //example
         .collect(
            Collectors.groupingBy(
               OccupancyMinutesGrouping, 
               Collectors.averagingInt(Occupancy::getOccupied)
            )
         );
}

有效 - 但是否可以将此地图更改为我的占用对象列表:

new Occupancy( localTime, averagedOccupancy );

我也担心流效率 - 它必须处理数据库中的所有记录。流如何与 jpa repo 一起工作?首先 SQL 获取所有记录 - 然后流处理它?或者它们是在每条记录上按顺序处理的?也许最好的解决方案是使用 Native SQL query insted of Stream?任何想法都会很有帮助...

至于转换为 List<Occupancy>,请注意 occupied 字段是 int 类型,而平均值可以是非整数。所以我假设 Occupancy class 是这样定义的:

class Occupancy {
   private LocalDateTime timeStamp;
   private double occupied;

   public Occupancy(LocalDateTime ts, double occ) {
       this.timeStamp = ts;
       this.occupied = occ;
   }
}

现在您可以从生成的地图中再创建一个流:

List<Occupancy> occupancies = avgMap.entrySet().stream()
    .map(e -> new Occupancy(e.getKey(), e.getValue()))
    .collect(Collectors.toList());

似乎中间 Map 是不可避免的(至少如果您的流尚未按 LocalTime 排序)。

至于内存使用:它取决于底层 JDBC 驱动程序。结果流确实逐行读取底层 ResultSet,但它是 JDBC 特定的,一次预缓冲了多少行。例如,众所周知 MySQL 驱动程序默认将完整的 ResultSet 检索到内存中,因此您可能需要这样的查询提示:

@QueryHints(value = @QueryHint(name = HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE))

详情见this blog post

另请注意,如果您的 JDBC 驱动程序实际上是从服务器逐行获取数据(没有缓冲),这实际上可能会降低性能,因为您可能需要在 DBMS 和您的应用程序(如果 DBMS 服务器位于不同的机器上,这可能尤其重要)。因此,请参阅您的 JDBC 驱动程序文档以获取更多详细信息。