Java Persistence 2.0 Public Draft

Java Persistence 2.0 Public Draft The Java Persistence 2.0 Public Draft is scheduled to be released this week, so I thought it would be a good time to review some of the new features that you can expect to find.

If you're familiar with the first draft of the JPA 2.0 specification (the Early Draft), you know that it focused primarily on the area of O/R mapping improvements.  The Public Draft builds on these improvements, and provides ways to leverage them through extensions to the Java Persistence Query Language and through the new Criteria API.

Because there's a lot to cover, this will be a 2-part entry.  In this part, we'll examine some of the new JPQL functionality.  Part 2 will look at the Criteria API.

The functionality added in the Early Draft to support element collections, nested embeddable classes, embeddables having entity relationships, generalized maps, and ordered lists necessitated some changes to the JPQL syntax to make queries over these mapping types easy to write.  Further, we've also added some other requested (and much needed) functionality to the language.


As you might expect, we've extended the dot (".") navigation syntax to handle embeddables with relationships and embeddables of embeddables.

Let's suppose we have an Employee entity, with a ContactInfo embeddable class used for contact information, and a set of phones referenced by the ContactInfo embeddable.  The basic structure of the classes might look something like this:

@Entity public class Employee {
  @Id int empId;
  String name;
  @ManyToOne Department dept;
  ContactInfo contactInfo;


@Embeddable public class ContactInfo {
  Address address; 
  @OneToMany Set<Phone> phones;

@Embeddable public class Address {
  String street;
  String city;
  String zipcode;

@Entity public class Phone {
  @Id int phoneId;
  String areaCode;
  String localNumber;
  String internalExtension;
  PhoneType phoneType;
  @ManyToOne Account billedTo;
If I want to query for the accounts to which the office phones for employees at Sun's Santa Clara campus (zipcode 95054) are billed, I might write a query like this:

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

In this query, we're navigating across the ContactInfo embeddable in the FROM clause to the Phones relation, as well as navigating into the ContactInfo embeddable in the WHERE clause to extract the zipcode.

Note that in the FROM clause, navigation behaves like an inner join. Thus, for example, in the following query, if there is no contact information specified for some employees, those employees will not appear in the query result.

FROM Employee e JOIN e.contactInfo.phones p

Identification variables can reference embeddables in the FROM clause, and JOIN can also be used in the FROM clause to navigate over embeddables, so we could also have written this query as follows:

FROM Employee e JOIN e.contactInfo c JOIN c.phones p


To support our generalized map functionality, we've added the KEY and VALUE operators for extracting map keys and map values.  By default, an identification value that refers to an association of type Map denotes the map value, so, strictly speaking, the VALUE operator serves mainly the purpose of documentation.

For example, suppose we have a map from photo name to file name:
@Entity public class PictureCategory {
    @Id String name;
    @ElementCollection Map<String, String> photos;

To search for my egret photos,  I can write:

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

The following query is equivalent:

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

Let's look at another example.  This one keys the map on an entity type:

@Entity public class VideoStore {
  @Id int id;
  String name;
  Address location;
  @ElementCollection Map<Movie, Integer> videoInventory;

@Entity public class Movie {
  @Id int id;
  String title;
  String director;
  @ManyToMany Set<Actor> stars;

In the language of the spec, KEY(p) and VALUE(p) are "general identification variables"—which means that they can be used for further navigation. Thus, to search for the movie Vertigo near Sun's Santa Clara campus, I can write:

SELECT v.location.street
FROM VideoStore v JOIN v.videoInventory i
WHERE v.location.zipcode = '95054' AND KEY(i).title = 'Vertigo' AND VALUE(i) > 0

Map entries don't support navigation, but they can be selected.  The result is returned as an instance of type java.util.Map.Entry.

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

Ordered Lists

Ordered lists were another frequently-requested feature for which we added O/R mapping support in the Early Draft.

JPA 1.0 supported List as a collection type, but this support did not include support for maintaining a persistent ordering.  In the JPA 2.0 Early Draft, we added the ability to specify the OrderColumn annotation, which means that the persistence provider is required to maintain the list ordering using a separate column when you manipulate the order of the list.  The JPQL INDEX operator allows you to query over the ordering.

For example, suppose a department manager wants to maintain a rank ordering of employees for the purpose of issuing raises.  We might have:

@Entity public class Employee {
  @Id int empId;
  String name;
  @ManyToOne Department dept;

public class Department {
  @Id int deptId;
  String name;
  @OneToMany(mappedBy="dept") @OrderColumn List<Employee> members;

The following query retrieves the top employees in the marketing department.

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

Non-polymorphic Queries

Another feature that we've added to JPQL is support for non-polymorphic queries.

The TYPE operator allows you to select an entity's type and to restrict a query to one or more entity types.

Thus, if we have an Employee hierarchy with FullTimeEmployee, PartTimeEmployee, and Contractor subtypes, we can write queries such as the following:

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

PartTimeEmployee and Contractor are entity names.  (Recall that a entity name, by default, corresponds to the unqualified name of the entity class.)

The IN operator has also been extended to accept collection-valued parameters. Thus, you can also write the following:

FROM Employee e
WHERE TYPE(e) IN :empTypes

The argument that should be passed to this query is a collection of class objects.

An entity type can also be returned as the query result, as in the following.

FROM Employee e JOIN e.dept d

When the query is executed, the employee type is returned as a class object.

Case Expressions

Another addition to the query language is support for case expressions.  Both simple and general forms of CASE expressions are supported, as with SQL.  We now also support the NULLIF and COALESCE operators.

Here's an example of the CASE operator.  The value of the first CASE clause whose WHEN predicate is satisfied is returned.

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

This example illustrates the simple form of CASE in a bulk UPDATE statement:

SET e.salary = CASE e.rating WHEN 1 THEN e.salary \* 1.1
                             WHEN 2 THEN e.salary \* 1.05
                             ELSE e.salary \* 1.001

The NULLIF operator is useful particularly when the database encoding for missing or inapplicable information is represented in ways other than by nulls.  You may run across this when mapping to legacy databases.  Using NULLIF allows you to easily convert such values to nulls in queries.  If the arguments to NULLIF are equal, NULLIF returns null; otherwise it returns the value of the first argument.

Here's an example.  Let's suppose that salaries are represented as integer values and that missing salaries are encoded by -99999.

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

Scalar Expressions in the SELECT clause

While JPA 1.0 supported scalar expressions in the WHERE clause, it did not support them in the SELECT clause.    As the above queries illustrate, this frequently-requested feature has now also been added.

When you download the draft and review the spec, you'll notice that it flags a number of open issues and items that are in need of developer feedback.   Please feel free to send me and the expert group your thoughts at    Thanks!


I'm really looking forward to version 2.

Posted by Wesslan on November 13, 2008 at 10:44 PM PST #

Map support! Finally! Cannot wait to have 2.0 in my app's buildpath!

Posted by Sakuraba on November 14, 2008 at 12:29 AM PST #

Looks great, I'm looking forward to JPA 2.0. Can you please list requested features that won't make it into JPA 2.0, and also features in core Hibernate and TopLink that won't be in JPA 2.0? Thanks

Posted by Ryan on November 15, 2008 at 09:04 AM PST #

Did a concept similar to JDO Fetch Groups make it into JPA 2.0? I couldn't find it glancing at the spec draft...
Were there some technical concern around including this type of feature?

Posted by Cyril on November 17, 2008 at 01:18 AM PST #

Well, we're not done yet, so its still a little too early to comment on features that won't make it in. Are there any features that \*you\* see as high priority that are absent from the Public Draft? If so, please send a note to the feedback alias for the Expert Group. Thanks!

Posted by Linda DeMichiel on November 17, 2008 at 03:17 AM PST #

Sure, I'm happy to do that. I guess I just wanted to make sure first this indeed didn't make it in the draft that was just published under a different terminology or mechanism, which I may have missed. Do you confirm?

Posted by Cyril on November 17, 2008 at 03:20 AM PST #

Fetch groups aren't there, at least not yet. However, this topic is still on our minds, as we have recently been discussing APIs for determining whether an attribute or relationship is loaded. I anticipate that fetch groups (or some similar facility) will make it into JPA at some point. Unfortunately, however, the schedule for this release is quite tight, so it may not be in JPA 2.0.

Posted by Linda DeMichiel on November 17, 2008 at 03:22 AM PST #

Hi Cyril. (Looks like some weirdness in comment ordering is in effect here). The answer to your last question is 'yes' (i.e., confirmed).

Posted by Linda Demichiel on November 17, 2008 at 03:30 AM PST #

I second the recommendation for Fetch Groups and reiterate my support for being able to convert a JPQL statement to a QueryDefinition and vice versa within the API.


Posted by Sam on December 26, 2008 at 03:23 AM PST #

this is cool, this is what we want dude......

Posted by links of london on November 28, 2009 at 10:22 AM PST #

<a href="">links of london friendship bracelet</a>
<a href="">links of london sweetie</a>
<a href="">links of london ring</a>

Posted by linksoflondon on December 04, 2009 at 01:21 PM PST #

Is it possible to map a Map<String, List<String>> using JPA2?

Posted by Dave on January 19, 2010 at 06:34 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed

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.


Top Tags
« July 2016