【问题标题】:JPA Criteria API: select count(*) from subquery with joins and group by for pagination implementationJPA Criteria API:从子查询中选择计数(*)并通过连接和分组实现分页
【发布时间】:2022-01-26 04:01:00
【问题描述】:

我想在从数据库返回的产品列表中实现分页功能。所以我需要知道查询的总结果。

数据库表是:

PRODUCT TABLE             
-----------------------------
ID             NAME
-------------- --------------
1              Product 1
2              Product 2

PRODUCT SUPPLIER TABLE
--------------------------------------------
ID             PRODUCT ID     PRICE
-------------- -------------- --------------
1              1              35
2              1              30
3              2              70
4              2              75

期望的结果是不同供应商的最低价格的产品列表。

Product 1    30
Product 2    70

主查询应该是这样的:

select p.name, min(ps.price) from product
inner join product_supplier ps on ps.product_id = p.id
group by p.name

所以计数查询应该是这样的:

select count(*) from (
   select p.name, min(ps.price) from product
   inner join product_supplier ps on ps.product_id = p.id
   group by p.name
)

我用来获取分页结果的代码是:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> tupleQuery = cb.createTupleQuery();

Root<product> product = tupleQuery.from(product.class);
Join<product, productSupplier> productSuppliers = product.join("productSuppliers");

tupleQuery.multiselect(
   product,
   cb.min(productSuppliers.get("price")).alias("price")
);
tupleQuery.groupBy(product);
tupleQuery.orderBy(cb.asc(product.get("name")));

TypedQuery<Tuple> query = entityManager
                            .createQuery(tupleQuery)
                            .setMaxResults(pageable.getPageSize())
                            .setFirstResult(Long.valueOf(pageable.getOffset()).intValue());

List<Tuple> products = query.getResultList();
for (Tuple productResult : products) {
   Product product = productResult.get(product);
   Double minPrice = (Double)productResult.get("price");
   ProductDTO productDTO = new ProductDTO(product, minPrice);  
}

但我不确定如何从主查询中获取计数。我使用的代码是:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class)
    
Root<Product> product = countQuery.from(Product.class);
Join<Product, ProductSupplier> productSuppliers = product.join("productSuppliers");
    
countQuery.groupBy(product);
countQuery.select(cb.count(product));

long count = entityManager.createQuery(countQuery).getSingleResult();

但它会产生以下 SQL:

select
   count(product0_.id) as col_0_0_ 
from
   pu_products product0_ 
inner join
   pu_product_suppliers productsu1_ 
      on product0_.id=productsu1_.product_id
group by
   product0_.id

返回多个结果,抛出如下异常:

org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 31

所以我不知道如何让它工作。

谢谢。

【问题讨论】:

    标签: java spring hibernate jpa jpa-criteria


    【解决方案1】:

    这样的命名查询怎么样:

    select count(distinct ps.product) from product_supplier ps where ps.price is not null
    

    【讨论】:

    • 感谢您的回答@grigouille。使用 Criteria API 背后的想法是动态构建查询,通过 Predicate 接口添加 where、and、or、... 子句。所以在那种情况下,不能使用“静态”命名查询。
    【解决方案2】:

    似乎无法通过 Criteria API 从聚合查询中执行 select count (*) from

    所以最后我应用了this answer中的这个解决方案

    • 通过 Criteria API 构建查询后,获取最终查询字符串
    • select count(*) from ({final_query_string})包装它。
    • 使用select count (*) from 包装最终查询字符串的结果创建本机查询。

    这个解决方案的完整代码this answer来自@fliX

    问候。

    【讨论】:

      猜你喜欢
      • 2020-10-02
      • 1970-01-01
      • 2020-10-07
      • 2012-03-08
      • 2017-06-11
      • 2015-02-15
      • 2018-07-04
      • 2020-03-20
      • 2022-12-21
      相关资源
      最近更新 更多