Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help creating JPA criteria query

I'm building my first Java EE web application using Glassfish and JSF. I'm fairly new to the criteria query and I have a query I need to perform but the javaee6 tutorial seems a little thin on examples. Anyway, I'm having a hard time creating the query.

Goal: I want to pull the company with the most documents stored. Companies have a OneToMany relationship with Documents. Documents has a ManyToOne relationship with several tables, the "usertype" column distinguishes them.

MySQL query:

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

Thanks

--update-- Based on user feedback, here is what I have so far:

        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();

The error I get is:

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

Typical error, means nothing to me!

like image 512
Alan B. Dee Avatar asked Jan 06 '12 20:01

Alan B. Dee


People also ask

How do you write a criteria builder query?

Let's see it step by step: Create an instance of Session from the SessionFactory object. Create an instance of CriteriaBuilder by calling the getCriteriaBuilder() method. Create an instance of CriteriaQuery by calling the CriteriaBuilder createQuery() method.

Why do we need JPA Criteria API?

Java Prime Pack The Criteria API is a predefined API used to define queries for entities. It is the alternative way of defining a JPQL query. These queries are type-safe, and portable and easy to modify by changing the syntax. Similar to JPQL it follows abstract schema (easy to edit schema) and embedded objects.

How can we add criteria to a SQL query?

In the query design grid, click the Criteria row of the field where you want to add the criterion.

How do you execute a criteria query?

Executing Queries To prepare a query for execution, create a TypedQuery<T> object with the type of the query result by passing the CriteriaQuery object to EntityManager. createQuery . Queries are executed by calling either getSingleResult or getResultList on the TypedQuery<T> object.


2 Answers

Your query doesn't return a complete entity object as you're only selecting two fields of the given table (this is why you're getting an error that says yadayadapartialyadayada).

Your solution is almost right, here's what you need to change to make it work—making it partial.

Instead of a plain CriteriaQuery<...> you have to create a tuple CriteriaQuery<..> by calling CriteriaBuilder.createTupleQuery(). (Basically, you can call CriteriaBuilder.createQuery(...) and pass Tuple.class to it as an argument. Tuple is a sort of wildcard entity class.)

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"));
}

(Accessing fields of a Tuple gave me an error if I didn't use aliases for them (in multiselect(...)). This is why I've used aliases, but this can be tackled more cleanly by using JPA 2's Metamodel API, which is described in the specification quite thoroughly. )

The documentation for CriteriaQuery.multiselect(...) describes the behaviour of queries using Tuple objects more deeply.

like image 141
Kohányi Róbert Avatar answered Sep 22 '22 12:09

Kohányi Róbert


If you are using Hibernate, this should work:

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"));

Hope it helps!

like image 23
Gonzalo Avatar answered Sep 26 '22 12:09

Gonzalo