【问题标题】:JPA CriteriaBuilder - sort by the number of associated entities in a one-to-many relationshipJPA CriteriaBuilder - 按一对多关系中关联实体的数量排序
【发布时间】:2013-01-03 17:49:05
【问题描述】:

我有两个实体 Customer 和 Order 处于一对多关系中。 对于每个客户,我需要计算关联订单的数量并按此数字对结果进行排序。 在本机 postgres 查询中,它看起来像这样:

select cust.id, count(order.id) from customers cust
left outer join orders order
on cust.id = order.customer_id
where .... conditions ...
group by cust.id
order by count desc;

但我必须使用 CriteriaBuilder 执行此操作,因为此查询是使用 CriteriaBuilder 放入附加条件的较大代码段的一部分。在 Hibernate 中我可能会使用 Projections,但在 JPA 中找不到类似的东西。

非常感谢您在使用 CriteraBuilder 编写查询方面提供任何帮助。

提前谢谢你。

【问题讨论】:

  • 看看一个很好的教程。我个人建议this one

标签: java hibernate jpa criteria-api


【解决方案1】:

假设实体 Customer 具有这样的 OneToMany 属性:

@OneToMany(mappedBy = "customerId")
private Collection<Order> orders;

您可以使用以下查询:

EntityManager em;  // to be built or injected
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Customer> customer = cq.from(Customer.class);
CollectionJoin<Customer, Order> orders = customer.join(Customer_.orders, JoinType.LEFT);
cq.select(cb.tuple(customer, cb.count(orders)));
cq.where(... add some predicates here ...);
cq.groupBy(customer.get(Customer_.id));
cq.orderBy(cb.desc(cb.count(orders)));
List<Tuple> result = em.createQuery(cq).getResultList();
for (Tuple t : result) {
    Customer c = (Customer) t.get(0);
    Long cnt = (Long) t.get(1);
    System.out.println("Customer " + c.getName() + " has " + cnt + " orders");
}

上述方法使用Metamodel。如果不喜欢,可以将Customer_.orders替换为"orders",将Customer_.id替换为"id"

如果OneToMany 属性属于其他类型,请将CollectionJoin 替换为正确类型的集合(ListJoinSetJoinMapJoin)。

【讨论】:

  • 感谢您的回答。不幸的是,它在我的情况下不起作用 - 原因是我试图将我的代码集成到其中的代码是如此构造,以至于它使用来自 SimpleJpaRepositoryfindAll(Specification&lt;T&gt; spec, Pageable pageable) - 传入一个也必须包含的 Pageable 对象按列排序。如果我没有在PageRequest(int page, int size, Sort sort) 中指定要排序的列,而是按照您上面的建议通过 CriteariaBuilder 进行排序 - 它只是假设排序为空并覆盖我在 CriteriaBuilder 中指定的任何顺序。
  • 我不明白,你能澄清一下吗?然而,这似乎是一个完全不同的问题。如果它回答了原始问题,我建议打开一个新答案并标记此答案,看起来确实如此。
  • 这发生在SimpleJpaRepository,第417行:query.orderBy(toOrders(pageable.getSort(), root, builder));。我正在检查 spring-data 源以找到一种方法使其与 Pageable 一起使用,而不是覆盖我在 CriteriaBuilder 中指定的顺序。如果您能进一步建议我,我将不胜感激。虽然我知道你已经帮了我很多。谢谢。
  • 抱歉,没有看到您的回复。是的,你是对的,可能值得提出另一个问题。我还是要澄清一下:代码的作用是:customerRepository.findAll(new CustomerSearchSpecification(searchFilter), pageable);。 Pageable 看起来像这样:PageRequest(1, 10, new Order(Direction.DESC, Customer_.name.getName()); 对于我的情况 - 我不能使用 Metamodel 列 - 所以我为 Order 传递 null - 在这种情况下,我在 CriteriaBuilder 中指定的内容并不重要,它只是覆盖它添加选择没有顺序。
  • Sort.Direction 和 PageRequest 是 spring 类,我不是这方面的专家。请就此提出一个新问题。但是,在 Order 构造函数中使用 Direction 看起来很可疑:Direction 是要持久化的属性吗?我会说不。
【解决方案2】:

在规范中使用这个

cq.orderBy(cb.desc(cb.count(orders)));

同时发送PageRequest(1, 10, Sort.unsorted())。我就是这样做的。 如果您将排序值作为未排序传递,然后使用您自己对连接实体的排序逻辑覆盖条件查询

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 2019-04-07
    • 2018-10-09
    相关资源
    最近更新 更多