Even better, JPA 2 criteria is compiled (generated from the source code) and type safe. So, for example, whenever an attribute is removed or changed on the entity, the queries stop compiling immediately, instead of having to wait until the application is running to detect errors. Neat, huh?
However, there's a problem. The way it is, queries are unusable. Well, usable, but very, VERY hard to code, read and maintain. Not for the JSR 317 expert group, of course, but everyone I've asked, has the same opinion as me.
Take a look (example extracted from this link, with little changes):
CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Person> criteria = builder.createQuery( Person.class); Root<Person> personRoot = criteria.from(Person.class); criteria.select(personRoot); ParameterExpression<String> eyeColorParam = builder. parameter(String.class); criteria.where(builder.equal(personRoot.get( Person_.eyeColor), eyeColorParam)); TypedQuery<Person> query = em.createQuery(criteria); query.setParameter(eyeColorParam, "brown"); List<Person> people = query.getResultList();
Is this example anything close to 'easy'? The very same query in JPQL would be:
String jpql = "select p from Person p where p.eyeColor = :eyeColor"; TypedQuery<Person> query = em.createQuery(jpql, Person.class); query.setParameter("eyeColor", "brown"); List<Person> people = query.getResultList();
To make things a bit worse, the Query object returned from the em.createQuery(criteria) never has parameters already set. And parameters are only used when a ParameterExpression is created. Otherwise, the values are passed as literals (so, subject to things like SQL injection). Yikes! There's absolutely no reason for this. Even the plain old Hibernate criteria already converted given literals to bind parameters...
C'mon, how could an expert group do such terrible decisions, impacting the lives of thousands Java programmers out there having to live with this abomination?
Thanks God, there is a very nice solution. It's Querydsl. It has the main advantage of JPA 2 criteria: being type safe (an annotation processor is used to generate a meta model which is used on queries), uses fluent interfaces (code is very readable) and generates queries with bind parameters on all expressions. The Querydsl metamodel has a Q prefix, for example, QEntity, instead of JPA's Entity_. So, let's take a look on the same previous example in Querydsl:
JPAQuery query = new JPAQuery(em); QPerson person = QPerson.person; List<Person> people = query.from(person) .where(person.eyeColor.eq("brown")) .list(person);
Now, that's readable!!! Also, in the project I'm working, I've also extended the query (actually, extending AbstractJPAQuery) and added other useful methods, like page(currentPage, pageSize). Such things can't be done in JPA because all objects (Query, CriteriaQuery, CriteriaBuilder) are interfaces given by the JPA provider, and can't be easily extended.
So, here is my tip to anyone thinking about using a Criteria API: Give Querydsl a try! By the way, did I mention that it can also be used with JDO, Lucene, JDBC and even plain collections?