【问题标题】:Need help creating JPA criteria query需要帮助创建 JPA 条件查询
【发布时间】:2012-02-04 13:23:02
【问题描述】:

我正在使用 Glassfish 和 JSF 构建我的第一个 Java EE Web 应用程序。我对条件查询相当陌生,我有一个需要执行的查询,但 javaee6 教程在示例上似乎有点薄。无论如何,我很难创建查询。

目标:我想拉出存储文件最多的公司。 公司与 Documents 具有 OneToMany 关系。 Documents 与多个表具有 ManyToOne 关系,“usertype”列将它们区分开来。

MySQL 查询:

SELECT USERID, COUNT(USERID) AS CNT 
FROM DOCUMENTS 
WHERE USERTYPE="COMPANY" 
GROUP BY USERID 
ORDER BY CNT DESC

谢谢

--更新-- 根据用户的反馈,这是我目前所拥有的:

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Documents> cqry = cb.createQuery(Documents.class);
        //Intersting Stuff
        Root<Documents> root = cqry.from(Documents.class);
        Expression userid = root.get("userID");
        Expression usertype = root.get("userType");
        Expression count = cb.count(userid);
        cqry.multiselect(userid, count);
        Predicate userType = cb.equal(usertype, "COMPANY");
        cqry.where(userType);
        cqry.groupBy(userid);
        cqry.orderBy(cb.desc(count));
        //more boilerplate
        Query qry = em.createQuery(cqry);
        List<Documents> results = qry.getResultList();

我得到的错误是:

Exception Description: Partial object queries are not allowed to maintain the cache or be edited.  You must use dontMaintainCache().

典型错误,对我来说毫无意义!

【问题讨论】:

  • 尝试将查询从 CriteriaQuery 反类型化为 CriteriaQuery,以便在不违反 API 的情况下选择计数。如果您还发布有关表/实体的更多信息,我可以尝试其他方式

标签: java jpa criteria-api


【解决方案1】:

您的查询不会返回 complete 实体对象,因为您只选择了给定表的两个字段(这就是为什么您会收到错误提示 yadayadapartialyadayada em>)。

您的解决方案几乎是正确的,这是您需要进行更改才能使其正常工作的地方——使其部分

您必须通过调用CriteriaBuilder.createTupleQuery() 创建一个元组 CriteriaQuery&lt;..&gt;,而不是普通的CriteriaQuery&lt;...&gt;。 (基本上,您可以调用CriteriaBuilder.createQuery(...) 并将Tuple.class 作为参数传递给它。Tuple 是一种通配符 实体类。)

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq= cb.createTupleQuery();

Root<Documents> root = cq.from(Documents.class);
Expression<Integer> userId = root.get("USERID");
Expression<String> userType = root.get("USERTYPE");
Expression<Long> count = cb.count(userId);

cq.multiselect(userId.alias("USERID"), count.alias("CNT"));
cq.where(cb.equal(userType, "COMPANY");
cq.groupBy(userId);
cq.orderBy(cb.desc(count));

TypedQuery<Tuple> tq = em.createQuery(cq);
for (Tuple t : tq.getResultsList()) {
  System.out.println(t.get("USERID"));
  System.out.println(t.get("CNT"));
}

(如果我没有为它们使用别名,访问Tuple 的字段会给我一个错误(在multiselect(...) 中)。这就是我使用别名的原因,但这可以更干净地解决通过使用 JPA 2 的 Metamodel API,该规范在规范中进行了相当详尽的描述。)

CriteriaQuery.multiselect(...) 的文档更深入地描述了使用 Tuple 对象的查询行为。

【讨论】:

  • 太棒了!它几乎开箱即用。在这篇文章和@Mechkov 分享的文章之间,我对标准查询有了更好的理解。谢谢。
  • 很好的答案,帮助我解决了以下错误You must use dontMaintainCache()
  • 感谢您的代码示例。我学到了很多关于使用元组(而不是实体)的 jpa 条件查询的知识。
  • 嗨,我想为 cb.count(userId) 创建一个别名,并通过引用别名在后续顺序中使用该别名。这可能吗?
  • @yaswanth You do something like this。您在 multiselect 调用中定义别名。
【解决方案2】:

如果您使用的是 Hibernate,这应该可以工作:

ProjectionList pl = Projections.projectionList()
.add(Projections.groupProperty("userid"))
.add(Projections.property("userid"))
.add(Projections.count("userid"));

Criteria criteria = session.createCriteria(Document.class)
.add(Restrictions.eq("usertype",usertype))
.setProjection(pl)
.addOrder(Order.desc("cnt"));

希望对你有帮助!

【讨论】:

  • 这意味着使用 Hiberate,这超出了这里的范围。你至少应该提到这一点。
  • 是的,恐怕我没有使用 Hibernate。虽然我开始希望我是!
  • 哦,非常抱歉。没想到!
【解决方案3】:

看看这个简单的教程。它使用 JPA2 和 Criteria

http://www.jumpingbean.co.za/blogs/jpa2-criteria-api

问候!

【讨论】:

  • 您发布的教程很好,但是 OP 想要的查询提出了一个有趣的问题:当查询返回不同类型的对象时,Criteria API 真的有用吗?
  • 谢谢,教程对我帮助很大。至于不同的类型,我只需要 id (long)。
  • 酷。很高兴它有帮助。我自己用过,不久前。
猜你喜欢
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多