我正在尝试在使用 JPA 2.0 类型安全标准 API 编写的应用程序中使用子查询,并以 Hibernate 3.6.1.Final 作为我的提供程序。我选择基本类型(Long、MyEntity 等)没有问题,但我想选择多个列。
这是一个完全合理的例子。忽略子查询的不必要使用——它只是为了说明。
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Subquery<Tuple> subQ = cq.subquery(Tuple.class);
Expression<Long> subqCount;
{
Root<MyEntity> root = subQ.from(MyEntity.class);
Path<MyEntity> filter = root.get(MyEntity.challenge);
subqCount = cb.count(root);
// How to select tuple?
Selection<Tuple> tuple = cb.tuple(filter, subqCount);
// !! Run-time exception here
Expression<Tuple> tupleExpr = (Expression<Tuple>) tuple;
// Not sure why I can't use multiSelect on a subQuery
// #select only accepts Expression<Tuple>
createSubQ.select(tupleExpr);
createSubQ.groupBy(filter);
}
cq.multiselect(subqCount);
虽然编译器没有抱怨,但我仍然遇到运行时异常。
java.lang.ClassCastException: org.hibernate.ejb.criteria.expression.CompoundSelectionImpl cannot be cast to javax.persistence.criteria.Expression
multiselect
,那么如何执行 groupBy
?groupBy
,为什么它会出现在 API 中?我也有同样的问题。
我只能尝试回答你的最后一个问题,说你只能真正使用子查询来执行非常简单的查询,例如:
SELECT name FROM Pets WHERE Pets.ownerID in (
SELECT ID FROM owners WHERE owners.Country = "SOUTH AFRICA"
)
我想说的另一件事是这件事让我想起了xkcd #979。
我也有类似的问题。 我有规范,并且我想获取与该规范匹配的对象的 id。
我的解决方案:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> tupleCriteriaQuery = criteriaBuilder.createTupleQuery();
Root<Issue> root = tupleCriteriaQuery.from(Issue.class);
tupleCriteriaQuery = tupleCriteriaQuery.multiselect(root.get(IssueTable.COLUMN_ID));//select did not work.
tupleCriteriaQuery = tupleCriteriaQuery.where(issueFilter.toPredicate(root, tupleCriteriaQuery, criteriaBuilder));
List<Tuple> tupleResult = em.createQuery(tupleCriteriaQuery).getResultList();
首先,我选择列(在我的情况下,我只需要一列),然后调用 where 方法来与我给定的规范合并。
Subquery<String> duplicateUserSubquery = cQuery.subquery(String.class);
Root<User> subUser = duplicateUserSubquery.from(User.class);
Expression<String> concat = builder.concat(subUser.get("firstname"), subUser.get("surname"));
duplicateUserSubquery.select(concat)
.where(resultParams.toArray(new Predicate[0]))
.groupBy(subUser.get("firstname"), subUser.get("surname"))
.having(builder.gt(builder.count(subUser), 1));
Predicate duplicateCondition = builder.and(
builder.concat(queryRootUser.get("firstname"), queryRootUser.get("surname")).in(duplicateUserSubquery)
);
resultParams.add(duplicateCondition);