You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Criteria Builder中结合Group By与Order By解决重复预订问题

解决方案:通过Criteria Builder实现Group By与Order By并解决重复Booking问题

你的问题核心是多表关联导致的笛卡尔积,即使使用distinct,由于你同时查询了Booking实体和不同的出发日期(DEP_DATE_STR),同一个Booking会因为关联多个TrainJourney而生成多条不同的结果行,最终表现为重复的Booking记录。以下是具体的解决步骤和代码示例:

核心思路

  1. 用join替代fetch:fetch用于实体关联的懒加载初始化,而分组查询需要明确的关联路径来构建聚合逻辑。
  2. 按bookingId分组:确保每个Booking只返回一行结果。
  3. 聚合出发日期:因为一个Booking可能对应多个TrainJourney,需要通过聚合函数(如min/max)确定要展示的出发日期(比如最早的出发日期)。
  4. 结合聚合后的日期实现排序。

代码实现

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<Booking> bookingRoot = query.from(Booking.class);

// 替换fetch为join,构建明确的关联路径
Join<Booking, Product> productJoin = bookingRoot.join("products", JoinType.LEFT);
Join<Product, TrainBooking> trainBookingJoin = productJoin.join("trainBooking", JoinType.LEFT);
productJoin.join("provider", JoinType.LEFT);
Join<TrainBooking, TrainJourney> trainJourneyJoin = trainBookingJoin.join("trainJourneys", JoinType.LEFT);
Join<TrainJourney, Journey> journeyJoin = trainJourneyJoin.join("journey", JoinType.LEFT);

// 聚合出发日期:这里以取最早的出发日期为例,可根据业务需求替换为max
Expression<Date> aggregatedDepDate = cb.min(journeyJoin.get(DEP_DATE_STR));

// 按bookingId分组,确保每个Booking只返回一行
query.groupBy(bookingRoot.get("bookingId"));

// 选择Booking实体和聚合后的出发日期,同时配置排序
query.multiselect(bookingRoot, aggregatedDepDate)
     // 先按出发日期排序,再结合原有的排序规则
     .orderBy(cb.asc(aggregatedDepDate), getOrderParam(request, cb, bookingRoot))
     .where(createPredicates(request, otaUser, cb, bookingRoot));

关键说明

  • 为什么distinct失效?:你之前的查询返回的是Booking+出发日期的组合,同一个Booking对应多个不同的出发日期时,这些组合都是唯一的,distinct无法对Booking单独去重。
  • 聚合函数的选择:如果业务需要展示最晚的出发日期,将cb.min替换为cb.max即可;如果需要其他逻辑,可根据JPA支持的聚合函数调整。
  • 排序逻辑:示例中先按聚合后的出发日期排序,再保留你原有的orderParam排序规则,可根据业务需求调整排序顺序。

内容的提问来源于stack exchange,提问作者Fatih Enes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:03:13