使用CriteriaBuilder统计实体异常:结果为正确值与总条目数乘积
问题解析:CriteriaBuilder统计时
cq.from(ItemForSale.class)与root的差异 这个问题其实是对Criteria API中from()方法的使用理解不到位导致的,我来给你拆解清楚两者的区别,以及为什么会出现这种倍数错误:
核心差异点
Root<ItemForSale> root = cq.from(ItemForSale.class):这行代码是在Criteria查询中创建唯一的根实体,它对应SQL里的主查询表(这里就是ItemForSale表)。后续所有的条件过滤、字段选择都应该基于这个root对象,它代表了查询的数据源。cq.from(ItemForSale.class):每次调用这个方法,都会在当前查询中新增一个对ItemForSale表的关联——相当于SQL里没有关联条件的笛卡尔积连接,而不是复用已有的根对象。
为什么你的统计结果会是倍数?
看你原来的统计代码:
cq.select(cm.count(cq.from(ItemForSale.class)));
这里犯了一个关键错误:你已经通过root = cq.from(ItemForSale.class)创建了第一个ItemForSale表的关联(我们叫它表A),但在select统计时又调用了一次cq.from(ItemForSale.class),这会再创建一个ItemForSale表的关联(表B)。
数据库执行这个查询时,会先做表A和表B的笛卡尔积:
- 当查询Category 0时,表A匹配到40条符合条件的商品,表B是全量80条商品,两者笛卡尔积的结果就是40×80=3200,这就是你看到的错误统计数;
- 查询子分类时,表A匹配10条,表B还是80条,10×80=800,完全对应你的实际结果。
而当你把cq.from(ItemForSale.class)替换成root后,统计的是表A中符合条件的行数,自然就得到了正确的40和10。
额外优化:更简洁的统计写法
其实你的条件部分也可以简化,用in替代多个or的拼接,代码更清爽:
public int getItemsForSaleCount(Category category) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Long> cq = cb.createQuery(Long.class); Root<ItemForSale> root = cq.from(ItemForSale.class); // 用in替代多个or条件,逻辑更清晰 List<Category> categories = category.getAllCategories(); cq.where(root.get("category").in(categories)); // 基于根对象统计行数 cq.select(cb.count(root)); return Math.toIntExact(em.createQuery(cq).getSingleResult()); }
内容的提问来源于stack exchange,提问作者user8619936
相关产品推荐
相关产品推荐

