About 2 years ago, Oracle announced a maintenance release for JPA as part of Java EE 8. Lukas Jungmann presented his plans for it in his session at Java One 2015. We all know about the Java EE 8 situation in 2016 and JPA was also affected by it. The JPA tracker shows that there was no progress within the last year.
In the meantime, Hibernate added new, proprietary features. Here are 6 of them that I would like to see in the JPA specification.
Date and Time API
Java 8 introduced the Date and Time API, and there’s probably no Java developer who doesn’t prefer it over the old java.util.Date. Unfortunately, there is still no built-in support for it in JPA. The reason for that is simple; the latest JPA release is older than Java 8. The Date and Time API didn’t exist, when JPA 2.1 was released.
The next JPA release will hopefully support the Date and Time API classes as data types. The JPA spec tracker contains an old improvement request for it .
Repeatable annotations are another language feature added in Java 8. That might look like a small change, but that changes as soon as you’ve used some of Hibernate’s proprietary, repeatable annotations. It happens quite often that you want to add an annotation multiple times to the same entity. A typical example for that is the @NamedQuery annotation. The only way to do that with JPA 2.1 is to wrap it into a @NamedQueries annotation.
Hibernate 5.2 provides proprietary annotations with the same name. These are repeatable, and you don’t need the @NamedQueries annotation anymore.
I explained Hibernate’s repeatable annotations in more detail in Benefits of @Repeatable annotations in Hibernate 5.2. The short description in JPA_SPEC-115 indicates, that JPA 2.2 will offer a similar support of repeatable annotations.
Stream query results
Streams were also added in Java 8 and provide a comfortable and efficient way to process a list of Objects. As with the previous Java 8 features, Hibernate 5 already provides query results as a Stream and it’s a requested feature for JPA 2.2.
You might say that you can already get a query result as a Stream with JPA 2.1. That’s basically right because you can get the result as a List and call the stream() method to get a Stream representation of it.
This approach gives you a Stream of the selected result set. But Hibernate offers a better solution for it. The getResultList() method requires Hibernate to fetch all records of the result set and to put them into the List. If you use a Stream to process the records, you don’t need to fetch all of them at the beginning. You process them one by one anyways. Hibernate, therefore, uses it’s proprietary ScrollableResultSet to scroll through the result set and fetch the records in small batches.
Good support for natural IDs is another feature I’m missing in JPA. Most domain models don’t use them as primary keys, but that doesn’t mean that the business logic doesn’t need them. A lot of use cases rely on natural IDs instead of the generated surrogate keys.
With JPA, you need to write custom queries to retrieve entities by their natural ID.
Hibernate offers a proprietary API that not only provides some convenience features. It also utilizes the existing caches.
I explain Hibernate’s natural ID support in more details in @NaturalId – A good way to persist natural IDs with Hibernate?
Ad hoc join of unrelated entities
When your domain model defines relationships with lots of entities, you most likely don’t want to manage them with JPA or Hibernate. A typical example for that is the relationship between an entity that defines a product and an entity that represents a sold instances of it. The risk is too high that someone just calls a getter method and Hibernate loads a few thousand entities from the database. If that line of code sneaks onto your production system, it will most likely cause a few angry customer complaints.
The common solution is to not model this relationship between your entities. That prevents everyone from calling the getter method, but with JPA, it also prevents you from joining these entities in a JPQL query. You either have to use a cross join or a native query.
Hibernate also supports ad hoc joins of unrelated entities, like you can see in the following code snippet.
Load by multiple ID
Loading multiple entities by their primary key is another feature I like in Hibernate. You can see an example of it in the following code snippet.
Hibernate splits the array of primary keys into one or more chunks and provides them as parameters to an IN statement.
That is obviously just a convenience feature, but it makes it a lot easier to load multiple entities by their primary key efficiently.
Most of the features I like to see in the JPA specification are convenience features. The main reason is that the specification already provides good solutions for most common use cases. One exception is, of course, the missing Java 8 support and I’m also missing a good solution for multi-tenancy. But besides that, there are not a lot of things missing and multi-tenancy will (hopefully) be addressed on Java EE level and involve multiple specifications.
Does that mean that we don’t need the promised maintenance release?
No! Especially the Java 8 support is overdue and there were already a lot of discussions about multi-tenancy. If JPA shall stay relevant, these features need to be added. Until then, we need to use proprietary implementation features. That is fine as long as you don’t need to switch your JPA provider.
If you want to learn more about these and other advanced Hibernate features, you should join my Advanced Hibernate Online Training.