我有两种方法,一种尝试使用 CriteriaBuilder 的 sum
方法对实体上的 numMinutes 字段求和,另一种返回实体列表,我手动循环遍历它们并对 numMinutes 字段求和。
第一个方法返回 0,但第二个方法返回正确答案。诊断此问题时,我在 persistence.xml 中打开了 SQL 打印,第一种方法不打印任何内容。调试时,HQL 与您期望的非常相同。
下面有两个方法,我的问题是,为什么第一个方法这么糟糕,我该怎么做?
尝试对数据库层上的值求和的错误方法:
public int getTotalTimeRecordedForContract(SupplierServiceContract contract) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Integer> criteria = builder.createQuery(Integer.class);
Root<TimeUserTransaction> root = criteria.from(TimeUserTransaction.class);
List<Predicate> allPredicates = new ArrayList<Predicate>();
allPredicates.add(
builder.between(root.get(TimeUserTransaction_.dateRecorded), contract.getStartDate(), contract.getExpiryDate())
);
allPredicates.add(
builder.equal(root.get(TimeUserTransaction_.transaction).get(TransactionPlus_.supplierService), contract.getSupplierService())
);
TypedQuery<Integer> query = this.getEntityManager().createQuery(
criteria.select(builder.sum(root.get(TimeUserTransaction_.numMinutes))).where(allPredicates.toArray(new Predicate[allPredicates.size()]))
);
return query.getFirstResult();
}
返回列表的方法应该是相同的,只是它返回所有实体,而不尝试聚合和求和 numMinutes 列:
public List<TimeUserTransaction> getTimeRecordedForContract(SupplierServiceContract contract) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<TimeUserTransaction> criteria = builder.createQuery(TimeUserTransaction.class);
Root<TimeUserTransaction> root = criteria.from(TimeUserTransaction.class);
List<Predicate> allPredicates = new ArrayList<Predicate>();
allPredicates.add(
builder.between(root.get(TimeUserTransaction_.dateRecorded), contract.getStartDate(), contract.getExpiryDate())
);
allPredicates.add(
builder.equal(root.get(TimeUserTransaction_.transaction).get(TransactionPlus_.supplierService), contract.getSupplierService())
);
TypedQuery<TimeUserTransaction> query = this.getEntityManager().createQuery(
criteria.select(root).where(allPredicates.toArray(new Predicate[allPredicates.size()]))
);
return query.getResultList();
}
在调试时,JPQL 似乎适用于第一种方法:
select sum(generatedAlias0.numMinutes) from TimeUserTransaction as generatedAlias0 where ( generatedAlias0.dateRecorded between :param0 and :param1 ) and ( generatedAlias0.transaction.supplierService=:param2 )
第二个:
select generatedAlias0 from TimeUserTransaction as generatedAlias0 where ( generatedAlias0.dateRecorded between :param0 and :param1 ) and ( generatedAlias0.transaction.supplierService=:param2 )
这更让我困惑,好像第一个方法生成 HQL,为什么它不将其转换为 JPQL 并在数据库上运行。
我正在使用连接到 mysql 数据库的 JPA 的 hibernate 实现 - 以防万一这是相关的。
最佳答案
您不需要.groupBy(root.get(TimeUserTransaction_.numMinutes))
。如果您选择多列并将一列与计数、总和等聚合函数一起使用,则需要 GROUP BY
。由于您只选择 numMinutes 的总和,因此您不需要这样做。
当您这样做时,它会将所有唯一的 numMinutes
及其总和分组。如果 numMinutes 为 0,那么它们也将被加起来为 0 并被分组在一起。 0 将位于顶部,因为默认情况下它将按升序排序,并且 query.getFirstResult();
将返回 0。
public int getTotalTimeRecordedForContract(SupplierServiceContract contract) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Integer> criteria = builder.createQuery(Integer.class);
Root<TimeUserTransaction> root = criteria.from(TimeUserTransaction.class);
List<Predicate> allPredicates = new ArrayList<Predicate>();
allPredicates.add(
builder.between(root.get(TimeUserTransaction_.dateRecorded), contract.getStartDate(), contract.getExpiryDate())
);
allPredicates.add(
builder.equal(root.get(TimeUserTransaction_.transaction).get(TransactionPlus_.supplierService), contract.getSupplierService())
);
TypedQuery<Integer> query = this.getEntityManager().createQuery(
criteria.select(builder.sum(root.get(TimeUserTransaction_.numMinutes))).where(allPredicates.toArray(new Predicate[allPredicates.size()]));
);
return query.getFirstResult();
}
关于java - JPA Criteria Builder sum 查询与返回列表的另一个查询相同,不会生成任何 sql,仅返回 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34588788/