Today we released the second milestone build of Jakarta Persistence 4.0.
I previously blogged about the new features of JPA 4 which were already available in the first milestone. Here I’m going to list the changes we’ve made since then.
Improvements to criteria queries
We’ve introduced new operations to obtain a criteria query object representing a JPQL query given as a string:
CriteriaQuery<Book> query =
builder.createQuery(Book.class,
// the base query
"from Book left join fetch authors");
if (titlePattern != null) {
// add a restriction to the query
query.where(book.get(Book_.title).like("%Persistence%"));
}
List<Book> books = agent.createQuery(query).getResultList();
The example demonstrates a second improvement in this release. We have added:
-
specialized expression types
ComparableExpression,NumericExpression,TextExpression,TemporalExpression, andBooleanExpression, and -
matching metamodel attribute types such as
ComparableAttribute,NumericAttribute,TextAttribute,TemporalAttribute, andBooleanAttribute.
This results in a modest improvement to the verbosity of criteria queries. We can now write:
query.where(book.get(Book_.title).like("%Persistence%"))
instead of:
query.where(builder.like(book.get(Book_.title), "%Persistence%"))
saving two tokens and enhancing readability.
Improved control over fetching with EntityGraph
It’s now possible to control association fetching strategies via the EntityGraph API.
Continuing with the pattern we established in JPA 3.2 with FindOption, RefreshOption, and LockOption, we have introduced a new FetchOption interface.
There are four built-in fetch options defined by the specification itself:
-
FetchType, allowing explicit selection ofLAZYorEAGERfetching, -
CacheRetriveModeandCacheStoreMode, controlling interaction with the second-level cache, and -
the brand new
BatchSize(which is also aFindOption).
Of course, implementations of the Persistence API are encouraged to provide their own FetchOptions.
For example, Hibernate will provide an option to request the use of subselect fetching.
@EntityListener
Since JPA 1.0, it has been possible to separate entity lifecycle callback listeners from the entity class itself, via the @EntityListeners annotation or equivalent XML descriptor.
This works, but in the absence of an XML descriptor, it means that the entity class must know about all its listeners at compilation time.
The new @EntityListener annotation removes the dependence of the entity class on its listener classes.
We could write:
@EntityListener
class BookListener {
@PostInsert
void bookInserted(Book book) {
...
}
}
and the listener will be automatically associated with the Book class.
Alternatively, we could write:
@EntityListener
class GenericListener {
@PostInsert
void inserted(Object book) {
...
}
}
and this listener will be associated with every entity class in the persistence unit (since every entity is a subtype of Object).
Entity listeners are now permitted to have multiple listeners for the same type of callback.
@EntityListener
class BookListener {
@PostInsert
void bookInserted(Book book) {
...
}
@PostInsert
void authorInserted(Author book) {
...
}
}
More control over flushing
We have added:
-
FlushModeType.EXPLICIT(similar to Hibernate’sFlushMode.MANUAL), -
QueryFlushModeenumerating flush modes relevant to query execution (identical to Hibernate’sQueryFlushMode), -
flushto@NamedQueryand@NamedNativeQuery, allowing a named query to specify aQueryFlushMode, and -
Query.setQueryFlushMode()as a replacement for the now-deprecatedQuery.setFlushMode()
StatementOrTypedQuery
In M1 we introduced a distinction between a Statement and a TypedQuery in the APIs for query execution.
In this release we have refined this split by adding StatementOrTypedQuery for additional type safety.
@Discoverable annotations
In the past, only managed classes (entities, embeddable types, mapped superclasses, and converters) were automatically discoverable by the Jakarta EE container, which led to certain limitations when JPA was used in Jakarta EE.
For example, a @NamedQuery annotation had to be placed on a managed class, or it would be ignored at runtime.
We have now introduced the meta-annotation @Discoverable.
Any annotation type annotated @Discoverable is considered a discoverable annotation type.
A JPA provider may even define its own discoverable annotation types.
Any class (even an interface, package descriptor or module descriptor) packaged in a persistence unit and annotated with any discoverable annotation type must now be passed by the Jakarta EE container to the JPA provider at startup.
So, for example, @NamedQuery may now be placed on any class or interface in the persistence unit, or even on a package descriptor or module descriptor.
Other changes
We have also:
-
specified that
@MapsIdmay be used with@IdClass, -
improved the
@Indexannotation, allowing greater control over generated DDL, -
consolidated the proposed
@ReadQueryOptionsand@WriteQueryOptionsannotations into a single annotation@StaticQueryOptions, -
allowed
@EntityResultand friends to be specified on a@StaticNativeQuerymethod, -
added support for aliases in the new programmatic
ResultSetMappingAPI, -
reworked the proposed new annotations for defining named entity graphs as inner annotations of
@NamedEntityGraph, and -
made a number of important clarifications to the specification and Javadoc.
We could love to hear your feedback on all this!