Java Persistence 2.0 Proposed Final Draft

When we released the JPA 2.0 Public Draft several months ago, one of our main goals was to get feedback on the new functionality – and, in this case, the most important new piece was the criteria API.

We did.  We received quite a few responses to the criteria API—many of you pleased that we had addressed this area—others suggesting that we should look at providing a typesafe API rather than a string-based API.

Gavin King immediately took up the challenge.  Gavin's proposal to the group, described here in his blog, was a typesafe API based on a metamodel of the managed classes in the persistence unit.

The expert group was in agreement that the metamodel would be valuable to have, independent of its use to support a criteria API.  (Actually, we had discussed the usefulness of a metamodel API back in the JPA 1.0 days, although understandably it was low on our priority list back then). The group was in less agreement on the form of the criteria API itself.  After many discussions on the pluses and minuses of a metamodel-based API compared to a string-based API, we decided to adopt the typesafe API, but to provide developers the option of using a string-based approach if they prefer.  More on that below—but first, some background on the metamodel API and how to use the typesafe API.


Metamodel API

From a query point of view, the metamodel captures what the spec refers to in JPQL terms as the "abstract schema" of the persistence unit — that is, the logical view over the persistent state and relationships of the managed classes of the persistence unit (entities, mapped superclasses, embeddables).

The metamodel can be browsed dynamically, using the new javax.persistence.metamodel interfaces. These interfaces can be used directly to write typesafe queries with the new API or, more conveniently,  metamodel classes that capture the metamodel of the persistence unit can be generated at the time the corresponding managed classes are compiled.  To facilitate this, we plan to release an annotation processor to be run in conjunction with javac to generate these classes.

To illustrate what such metamodel classes look like, consider the following set of interrelated entity classes that I used in my earlier post :

@Entity public class Customer {
  @Id int custId;
  String name;
  ...
  @OneToMany(mappedBy="customer") Set<Order> orders;
  ...
}

@Entity public class Order {
  @Id int orderId;
  ...
  @ManyToOne Customer customer;
  @OneToMany(mappedBy="order") Set<LineItem> items;
  ...
}

@Entity public class LineItem {
  @Id int id;
  @ManyToOne Order order;
  @ManyToOne Product product;
  ...
}

@Entity public class Product {
  @Id int productId;
  String name;
  String productType;
  ...
}

The corresponding metamodel classes look like this:

import javax.persistence.metamodel.\*;
@TypesafeMetamodel
public class Customer_ {
    public static volatile Attribute<Customer, Integer> custId;
    public static volatile Attribute<Customer, String> name;
    public static volatile Set<Customer, Order> orders;
    ...
}

import javax.persistence.metamodel.\*;
@TypesafeMetamodel
public class Order_ {
    public static volatile Attribute<Order, Integer> orderId;
    public static volatile Attribute<Order, Customer> customer;
    public static volatile Set<Order, LineItem> items;
   ...
}

import javax.persistence.metamodel.\*;
@TypesafeMetamodel
public class LineItem_ {
    public static volatile Attribute<LineItem, Integer> id;
    public static volatile Attribute<LineItem, Order> order;
    public static volatile Attribute<LineItem, Product> product;
    ...
}

import javax.persistence.metamodel.\*;
@TypesafeMetamodel
public class Product_ {
    public static volatile Attribute<Product, Integer> productId;
    public static volatile Attribute<Product, String> name;
    public static volatile Attribute<Product, String> productType;
    ...
}


The advantage of using generated metamodel classes, rather than using the javax.persistence.metamodel API directly, is that it makes the writing of typesafe queries very easy, as I'll describe in the sections below.  The API is otherwise much in the spirit of that in the Public Draft.  It improves over this earlier API in that it provides a more "semantic" factorization of interfaces as well as the ability to browse the constitute parts of a criteria query object.

For the sake of providing a more direct comparison with our earlier version, I'm going to take what I wrote about our earlier version of the criteria API and translate sample queries to the new criteria API.


Writing Queries with the New Criteria API

The core interfaces of the criteria API are the CriteriaQuery interface, which provides the methods that construct the constituent parts of a criteria query (select clauses, where clauses, etc.) and the QueryBuilder interface—which serves as the factory for criteria query objects and for the operations that are used to construct expressions and predicates.

The QueryBuilder interface is obtained from the EntityManager or EntityManagerFactory.   Using the QueryBuilder object, you create an "empty" CriteriaQuery object:

EntityManager em = ... ;
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery q = qb.create();


As before, the specification of query roots is the first step in constructing a query.  These correspond to the range variables defined in a SQL FROM clause, and specify the domain objects on which the query is based.  The from method of the CriteriaQuery interface is used to add a query root to a CriteriaQuery instance. The from method is additive.  The addition of further query roots, like additional range variables in JPQL, creates a cartesian product with the existing roots.

CriteriaQuery q = qb.create();
Root<Customer> customer = q.from(Customer.class);


Query roots are instances of the Root interface.  Notice that the Root variable customer is parameterized by the type of the entity that it represents. 

Given one or more query roots, the query domain can be modified using join operations.   The argument to the join method is a metamodel attribute which captures both the source of the join and the join target.  For an entity relationship or for an element collection this attribute is of type javax.persistence.metamodel.Collection, javax.persistence.metamodel.Set, javax.persistence.metamodel.List, or javax.persistence.metamodel.Map. When performing a join to an embeddable, it is of type javax.persistence.metamodel.Attribute. 

For example, to modify the above query to operate over customers and their orders, we would add:

Join<Customer,Order> order = customer.join(Customer_.orders);

The argument to the join method here is of type Set<Customer, Order>.   The typesafe parameterization of the join method insures that it is not possible to construct an invalid join from the customer root.

When writing queries using the typesafe API in this way, in general the only places that you will use the attributes of metamodel classes is to specify navigation (joins in the from-clause, and path navigation in the other clauses of the query).  From a typing point of view, these objects play the role of Java member literals (if only the Java language had member literals!) in providing type information.


The SELECT and WHERE Clauses

Every criteria query needs a select clause.  Every non-trivial query has a where clause.

Continuing our earlier example, a query to return all customers ordering products of type 'printer' would look something like this with JPQL:

SELECT c.name
FROM Customer c JOIN c.orders o JOIN o.items i
WHERE i.product.productType = 'printer'


The criteria API equivalent is the following:

QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery q = qb.create();
Root<Customer> customer = q.from(Customer.class);
Join<Order, LineItem> item = customer.join(Customer_.orders).join(Order_.items);
q.where(qb.equals(item.get(Item_.product).get(Product_.productType), "printer"))
 .select(customer.get(Customer_.name));


There are a couple of things to note:

The argument to the where method is a boolean expression (of type Expression<Boolean>).  As mentioned above, the QueryBuilder interface serves as a factory for such expressions.

In this particular example, the first argument to the equals method is a Path instance.  The get method is used to traverse a path.  Like the join method, it relies on use of a metamodel attribute to achieve typesafe navigation.  In this case, the path is derived from invoking get(Item_.product) on the item object and is then further chained to navigate to the productType attribute.  

The where method returns the modified CriteriaQuery instance.  If the where method is applied again, the restrictions are replaced.   If the where method is invoked without an argument, they are removed.

The select method also returns the modified CriteriaQuery instance.  If the select method is applied again, the select list is replaced.  If the select method is invoked without an argument, they are removed (and the query will not be executable).  


Coverage

As with the version presented in the Public Draft, the new criteria API is intended to provide support for all the functionality of JPQL.

In the following sections we examine how we might rewrite some of the JPQL examples in my earlier blog.  I'm just going to show the JPQL queries here and assume the existence of the obvious managed classes and their corresponding metamodel classes.


Navigation

 JPQL:

SELECT DISTINCT p.billedTo
FROM Employee e JOIN e.contactInfo c JOIN c.phones p
WHERE e.contactInfo.address.zipcode = '95054'
    AND p.phonetype = PhoneType.OFFICE


Using the criteria API, this query can be written as follows.

CriteriaQuery q = qb.create();
Root<Employee> e = q.from(Employee.class);
Join<Employee, ContactInfo> c = e.join(Employee_.contactInfo);
Join<ContactInfo, Phone> p = c.join(ContactInfo_.phones);
q.where(qb.equal(c.get(ContactInfo_.address).get(Address_.zipcode), "95054"),
        qb.equal(p.get(Phone_.phonetype), PhoneType.OFFICE))
  .select(p.get(Phone_.billedTo)).distinct(true);



Maps

JPQL:

SELECT p
FROM PictureCategory c JOIN c.photos p
WHERE c.name = 'birds' AND KEY(p) LIKE '%egret%'

Using the criteria API:

CriteriaQuery q = qb.create();
Root<PictureCategory> c = q.from(PictureCategory.class);
MapJoin<PictureCategory, String, Photo> p = c.join(PictureCategory_.photos);
q.where(qb.equal(c.get(PictureCategory_.name), "birds"),
        qb.like(p.key(), "%egret%"))
 .select(p);

As in the JPQL query, a variable referring to a map type corresponds to the map value.  Notice that the MapJoin instance is parameterized on the type of the source entity, and the types of the map key and map value.

JPQL:

SELECT v.location.street, ENTRY(i)
FROM VideoStore v JOIN v.videoInventory i
WHERE v.location.zipcode = '95054'
   AND KEY(i).director = 'Hitchcock' AND VALUE(i) > 0

Using the criteria API:

CriteriaQuery q = qb.create();
Root<VideoStore> v = q.from(VideoStore.class);
MapJoin<VideoStore, Movie, Integer> i = v.join(VideoStore_.videoInventory);
q.where(qb.equal(v.get(VideoStore_.location).get(Address_.zipcode), "95054"),
        qb.equal(i.key().get(Movie_.director), "Hitchcock"),
        qb.gt(i, 0))
 .select(v.get(VideoStore_.location).get(Address_.street), i.entry());



Ordered Lists

JPQL:

SELECT e
FROM Employee e JOIN e.dept d
WHERE d.name='Marketing' AND INDEX(e) < 5


Using the criteria API:

CriteriaQuery q = qb.create();
Root<Department> d = q.from(Department.class);
ListJoin<Department, Employee> e =  d.join(Department_.members);
q.where(qb.equal(d.get(Department_.name), "Marketing),
        qb.lt(e.index, 5))
 .select(e);


Notice that the index operation here is properly typed as applying to a ListJoin instance.  Instead of joining from Employee to Department, as in the JPQL query,  we therefore join from Department to Employee.  From a database point of view, of course, the two are identical.


Restricted Polymorphism

JPQL:

SELECT e
FROM Employee e JOIN e.dept d
WHERE d.name = 'Marketing' AND TYPE(e) IN (PartTimeEmployee, Contractor)


In the criteria query, entity class objects are used to specify entity type arguments to the in method.  The in method supports use of a builder pattern, which this query illustrates:

CriteriaQuery q = qb.create();
Root<Employee> e = q.from(Employee.class);
Join<Employee, Department> d = e.join(Employee_.dept);
q.where(qb.equal(d.get(Department_.name), "Marketing"),
        qb.in(e.type()).value(PartTimeEmployee.class).value(Contractor.class))
  .select(e);


Case Expressions

This query shows the use of the general form of case expressions.

SELECT c, CASE WHEN c.annualSpending > 10000 THEN 'Premier'
               WHEN c.annualSpending >  5000 THEN 'Gold'
               WHEN c.annualSpending >  2000 THEN 'Silver'
               ELSE 'Bronze'
          END
FROM Customer c

CriteriaQuery q = qb.create();
Root<Customer> c = q.from(Customer.class);
Expression<Integer> annualSpending = c.get(Customer_.annualSpending);
q.select(c, qb.selectCase()
              .when(qb.gt(annualSpending, 10000),  "Premier")
              .when(qb.gt(annualSpending, 10000),  "Gold")
              .when(qb.gt(annualSpending, 10000),  "Silver")
              .otherwise("Bronze")); 



This one uses a NULLIF expression:

SELECT AVG(NULLIF(e.salary, -99999))
FROM Employee e

CriteriaQuery q = qb.create();
Root<Employee> e = q.from(Employee.class);
q.select(qb.avg(qb.nullif(e.get(Employee_.salary), -99999)));


Subqueries

The following JPQL query returns those customers whose unpaid balance is less than half of the average.  This makes use of a non-correlated subquery.

SELECT goodCustomer
FROM Customer goodCustomer
WHERE goodCustomer.balanceOwed < (
         SELECT AVG(c.balanceOwed)/2.0 FROM Customer c)


Using the criteria API, the corresponding query can be written as shown below.  There are Root objects for both the subquery and the containing query.  The subquery method of the CriteriaQuery interface creates a Subquery instance.  This instance is typed according to its expected result type.

CriteriaQuery q = qb.create();
Root<Customer> goodCustomer = q.from(Customer.class);
Subquery<Double> sq = q.subquery(Double.class);
Root<Customer> c = sq.from(Customer.class);
q.where(qb.lt(goodCustomer.get(Customer_.balanceOwed),
            sq.select(qb.toDouble(qb.quot(qb.avg(c.get(Customer_.balanceOwed)),
                                    2.0)))))
 .select(goodCustomer);


The following JPQL query selects those employees that make more than all of the managers in their department.  This has a correlated subquery, as it uses the emp range variable of the containing query.

SELECT emp
FROM Employee emp
WHERE emp.salary > ALL (
    SELECT m.salary
    FROM Manager m
    WHERE m.department = emp.department)


This is the criteria API equivalent.

CriteriaQuery q = qb.create();
Root<Employee> emp = q.from(Employee.class);
Subquery<BigDecimal> sq = q.subquery(BigDecimal.class);
Root<Manager> m = sq.addRoot(Manager.class);
sq.select(m.get(Manager_.salary))
  .where(qb.equal(m.get(Manager_.dept), emp.get(Employee_.dept)));
q.select(emp)
 .where(qb.gt(emp.get(Employee_.salary), qb.all(sq)));



String-based Use of the Criteria API

In addition to the typesafe API, the criteria API also supports string-based navigation, as did the API of the Public Draft.  As I mentioned, the Expert Group was somewhat divided over the need for string-based support, but many members felt strongly that some developers would prefer it.

As you might have already inferred from the examples above, the main difference in the string-based usage of the criteria API is that strings are used to specify joins and path navigation.

The following queries, taken from those above, show how a criteria query would be written to use the methods that take strings rather than metamodel types as arguments:

CriteriaQuery q = qb.create();
Root<Employee> e = q.from(Employee.class);
Join<Employee, ContactInfo> c  =  e.join("contactInfo");
Join<ContactInfo, Phone> p = c.join("phones");
q.where(qb.equal(c.get("address").get("zipcode"), "95054"),
        qb.equal(p.get("phonetype"), PhoneType.OFFICE))
  .select(p.get("billedTo")).distinct(true);


CriteriaQuery q = qb.create();
Root<VideoStore> v = q.from(VideoStore.class);
MapJoin<VideoStore, Movie, Integer> i = v.join("videoInventory");
q.where(qb.equal(v.get("location").get("zipcode"), "95054"),
        qb.equal(i.key().get("title"), "Vertigo"),
        qb.gt(i, 0))
 .select( v.get("location").get("street"));

If  you prefer to use raw types rather than the parameterized types shown above, you can do so, however this will result in compiler warnings unless @SuppressWarnings("unchecked") is used:

CriteriaQuery q = qb.create();
Root e = q.from(Employee.class);
Join c = e.join("contactInfo");
Join p = c.join("phones");
q.where(qb.equal(c.get("address").get("zipcode"), "95054"),
        qb.equal(p.get("phonetype"), PhoneType.OFFICE))
  .select(p.get("billedTo")).distinct(true);



Send Us Your Feedback


This draft of the spec contains some important changes, particularly with regard to the Criteria API, and can be downloaded here.  As always, we welcome your feedback and suggestions.   If you would like to send feedback to the expert group, you can reach us at jsr-317-pfd-feedback@sun.com.

thanks!


Comments:

Advantages of the Metamodel API:

- Auto-complete support in the IDE
- Refactoring support
- Compiler type checking
- Protection against SQL injection

The idea is that IDE creates the metamodel classes dynamically (while editing the code).

Posted by Thomas Mueller on March 27, 2009 at 05:56 PM PDT #

Hey Thomas,
Note that while the IDE can explicitly generate the metamodel on the fly, it can also delegate to the annotation processor that is triggered by the Java compiler. Even if the IDE has no specific JPA plugin, the metamodel is still generated on the fly. Same for plain old command line compilation.

Posted by Emmanuel Bernard on March 27, 2009 at 11:39 PM PDT #

> [the IDE] can ... delegate to the annotation processor

Yes, that's what I wanted to say. Currently, many (most?) IDEs don't fully support on-the-fly annotation processing. But once the Metamodel API is standardized, I'm sure IDE support will improve.

The syntax is still a bit verbose:

q.where(qb.equal(c.get(ContactInfo_.address).get(Address_.zipcode), "95054"),
qb.equal(p.get(Phone_.phoneType), PhoneType.OFFICE))
.select(p.get(Phone_.billedTo)).distinct(true);

I hope this can be simplified to:

q.where(ContactInfo_.address.zipcode).eq("95054").
and(Phone_.phoneType).eq(PhoneType.OFFICE).
selectDistinct(Phone_.billedTo);

that's quite a bit closer to C# LINQ / SQL.

Posted by Thomas Mueller on March 28, 2009 at 01:21 AM PDT #

Why are one to many conveyed as a 'set', pertaining to the crit api, shouldn't there be a clearer translation of the relationship instead of the artifact in the metamodel? Meaning,

public static volatile Set<Order, LineItem> items;

should be

public static volatile OnToMany<Order, LineItem> items;

Is there anything in the metamodel/crit API where a Set vs. List vs. Bag would exhibit different type-safe signatures vs. using the more explicit translation of the relationship names?

Posted by Jacob Hookom on March 28, 2009 at 02:00 AM PDT #

On that note, the ManyToOne-- are being translated as 'Attribute', wouldn't you want to preserve that type in the metamodel if there's ever specific behavior which needs to be introduced into the crit api?

Posted by Jacob Hookom on March 28, 2009 at 02:19 AM PDT #

Hey Jacob,
Yes indeed a Set is different than a List and a Map:
- from a List node you can access the index() property and the value()
- from a Map node, you can access a key() and a value()
- from a set no index of any kind is accessible

Why would you want to retain the cardinality in the logical model? It seems that from a Java point of view, you don't really care about cardinality

Posted by Emmanuel Bernard on March 28, 2009 at 03:15 AM PDT #

What's driving changes to the metamodel long term? Is it the 'Java' point of view or the JPA Crit API? Bringing in stronger typing here based on the relationship, not on the collection type could help clear up more complex query scenarios. Half of our uses today of the crit API end up using projections, and your actual 'Java' model is unchanged, you still have Map, Set, List, but I think those monikers are misplaced here within expressing relationships in the crit API.

Posted by Jacob Hookom on March 28, 2009 at 03:41 AM PDT #

Hi Thomas,

I expect to see the IDE support improve as well, particularly for the annotation-only case. For the annotation processor, though, we are also planning the handle the more complex cases as well, such as where XML only or a combination of annotations and XML might have been used.

We also looked at syntaxes such as you suggest. Part of the issue here is handling all of the more complex expressions that might be used in a where clause. Our goal with this API is to support everything that JPQL currently supports (and features we think we might be adding to JPQL).

Posted by Linda DeMichiel on March 28, 2009 at 03:48 AM PDT #

Hi Jacob,

Yes, we did make tradeoffs in the metamodel API between the granularity of types and the complexity of the API. Longer term, we are thinking about expanding the metamodel to encompass the object/relational mapping information.

Posted by Linda DeMichiel on March 28, 2009 at 04:00 AM PDT #

Please unbound the QueryBuilder from EntityManager

Posted by Vladimir on March 29, 2009 at 11:30 PM PDT #

Great Work!
In my opinion the typesafe API is a big step for JPA!
That's what I was missing compared with .NET LINQ.
No I will have it more or less in JPA 2.0.
Thanks a lot!

Posted by Simon Martinelli on March 31, 2009 at 07:47 PM PDT #

Is JPA or JPA2 has an on the fly entity beans generation options?
.NET has a .dbml file and it may generate classes run time.
i am sorry bu as a java developper. This usage is more difficult than .NET Linq.
JPQL and scripting languages should be embeddable.

May we merge metamodel class and entity class?
for instance:
Join<Order, LineItem> item = customer.join(Customer.orders).join(Order.items);
q.where(qb.equals(item.get(Item.product).get(Product.productType), "printer"))
.select(customer.get(Customer.name));

Posted by Fırat KÜÇÜK on May 13, 2009 at 04:41 PM PDT #

I agree with Thomas. I wish the syntax was a little less verbose. The constant p.get(X_) really takes away from the query. That said, I love the idea of the type safe query api and appreciate all the hard work you guys are going. Is this api finalized yet? Is there a chance that the syntax can be slimmed down?

q.where(qb.equal(c.get(ContactInfo_.address).get(Address_.zipcode), "95054"),
qb.equal(p.get(Phone_.phoneType), PhoneType.OFFICE))
.select(p.get(Phone_.billedTo)).distinct(true);

I hope this can be simplified to:

q.where(ContactInfo_.address.zipcode).eq("95054").
and(Phone_.phoneType).eq(PhoneType.OFFICE).
selectDistinct(Phone_.billedTo);

Posted by sboulay on May 14, 2009 at 06:34 AM PDT #

No, we can't use infix notation because we would lose the typesafety. You can't do \*everything\* with Java generics.

Also, I personally find hungarian notation much more natural in a language without operator overloading - it emphasizes the symmetry of the operation.

If you want infix notation, you need Java to add generified operator overloading :-)

Posted by Gavin King on May 14, 2009 at 12:36 PM PDT #

> we can't use infix notation because we would lose the typesafety.

No. At least JaQu doesn't lose the typesafety. It works with:
q.where(ContactInfo_.address.zipcode).eq("95054") because
q: Query<T>
Query<T>.where: <A> QueryCondition<T, A> where(A x)
QueryCondition<T, A>.eq: QueryWhere<T> eq(A y)
See: http://code.google.com/p/h2database/source/browse/#svn/trunk/h2/src/tools/org/h2/jaqu

> You can't do \*everything\* with Java generics.

Well, you can do a lot.

> I personally find hungarian notation much more natural

I understand that. With the infix notation can't use () to
override precedence. Example:

where id=10 and name='a' or name='b'
where(id).is(10).and(name).is("a").or(name).is("b")

But there is no easy 'completely fluent' way to write:
where id=10 and (name='a' or name='b')
where (id=10 and name='a') or name='b'

You would need some kind of 'escape' syntax:

where(id).is(10).and(q.test(name).is("a").or(name).is("b"))
where(q.test(id).is(10).and(name).is("a")).or(name).is("b")

Posted by Thomas Mueller on May 14, 2009 at 05:07 PM PDT #

"No. At least JaQu doesn't lose the typesafety. It works with:"

I don't buy that. Some operations simply don't apply to all types. For example you can't multiply strings, or lower() numbers.

I don't believe it's possible to constrain the operations available to the correct type when you use infix notation.

Posted by Gavin King on May 14, 2009 at 05:19 PM PDT #

I am sure that there are good technical reasons why your not using the \*infix\* notation but I am a little worried that it will hurt the readability and usability of the criteria api .. IMO. Are there other syntax's you guys are considering?

Posted by sboulay on May 14, 2009 at 11:03 PM PDT #

IMHO the syntax does look awkward, but not excessively so. The primary goal here is type safety. If my query is 100% type safe, but at the cost of being 10% harder to read, then I can live with that. At least then I wouldn't have to wait until my app gets built and the tests run to find out I fat-fingered some property name or forgot a comma. And of course, I can always fallback to JPQL for a particular query if I feel the readability is more important.

Just like with any new language, the more you expose yourself to it and use it, the more natural it feels. JPQL seems easy to read because it is similar to SQL, which many of us have been using for years. But even SQL syntax can be cumbersome, especially when you're first learning it. I think this would eventually get to the point where it feels, if not elegant, at least familiar and comprehensible.

Posted by Eric McIntyre on May 15, 2009 at 04:50 PM PDT #

- The .NET guys are not laughing at us any more, they are just smiling
- Yet another obvious reason to use Scala to write your domain and services layers

Posted by Hossam Karim on May 27, 2009 at 07:47 AM PDT #

> MHO the syntax does look awkward, but not excessively so.
> The primary goal here is type safety.
> If my query is 100% type safe, but at the cost of being 10% harder to read,
> then I can live with that. At least then I wouldn't have to wait until my
> app gets built and the tests run to find out I fat-fingered some property
> name or forgot a comma. And of course, I can always fallback to JPQL for
> a particular query if I feel the readability is more important.

Actually, we're considering waiting for JPA 2 to use the Criteria. However, the criteria syntax scared the hell out of me. I don't think it's 10% harder to read, but 100% harder.
Look at this example from the JPA 2.0 proposed final draft spec (and it shows a trivial query):

QueryBuilder qb = ...
CriteriaQuery q = qb.create();
Root<Customer> cust = q.from(Customer.class);
Join<Customer, Order> order = cust.join(Customer_.orders);
Join<Order, Item> item = order.join(Order_.lineitems);
q.select(cust.get(Customer_.name))
.where(qb.equal(item.get(Item_.product).get(Product_.productType),
"printer"));

This query is equivalent to the following Java Persistence query language query:
SELECT c.name
FROM Customer c JOIN c.orders o JOIN o.lineitems i
WHERE i.product.productType = 'printer'

Posted by Luis Fernando Planella Gonzalez on June 01, 2009 at 03:58 AM PDT #

We have tried to combine typesafe and compact queries in Querydsl. We support both JPA and JDO based backends.

See for yourself how it compares to the proposed JPA 2 syntax.

JPA 2 query :

QueryBuilder qb = ...
CriteriaQuery q = qb.create();
Root<Customer> cust = q.from(Customer.class);
Join<Customer, Order> order = cust.join(Customer_.orders);
Join<Order, Item> item = order.join(Order_.lineitems);
q.select(cust.get(Customer_.name))
.where(qb.equal(item.get(Item_.product).get(Product_.productType),"printer"));

Querydsl query :

HQLQuery query = ...
QCustomer cust = QCustomer.customer;
QOrder order = QOrder.order;
QLineItem lineItem = QLineItem.lineItem;

List<String> customerNames = query.from(cust)
.innerJoin(cust.orders, order)
.innerJoin(order.lineitems, lineitem)
.where(lineItem.product.productType.eq("Printer"))
.list(cust.name);

(QCustomer, QOrder and QLineItem are Querydsl metamodel types, they are immutable and can be reused over queries)

JPAQL query :

SELECT c.name
FROM Customer c JOIN c.orders o JOIN o.lineitems i
WHERE i.product.productType = 'printer'

What do you think?

Posted by Timo Westkämper on June 04, 2009 at 11:30 PM PDT #

How can I do this:

SELECT \* FROM Customer c

Posted by Jefferson on May 10, 2010 at 10:50 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Linda DeMichiel is the Specification Lead for Java EE 7 and for Java Persistence 2.1, and a member of the Java Platform, Enterprise Edition team at Oracle. She was formerly the Specification Lead for JPA 2.0 and for EJB 3.0.

Search

Top Tags
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today