This guide discusses migration to Hibernate ORM version 8.0. For migration from earlier versions, see any other pertinent migration guides as well.
Requirements
See the website for the list of requirements for the 8.0 series.
Jakarta Persistence 4.0
One specific requirement change that is important to call out is the move to from Jakarta Persistence version 3.2 to 4.0 which has a number of impacts, many of which are discussed below.
See this issue for more details.
Jakarta Validation lifecycle event changes
Jakarta Persistence 4.0 redefines which lifecycle events trigger automatic bean validation.
Previously, validation was tied to pre-persist and pre-update events.
Now, validation is organized into EntityManager-level events (pre-persist, pre-merge, pre-remove)
and DB-level events (pre-insert, pre-update, pre-upsert, pre-delete),
each with its own configuration property.
By default, only pre-insert, pre-update, and pre-upsert trigger validation with the jakarta.validation.groups.Default group.
All other events (pre-persist, pre-merge, pre-remove, pre-delete) have empty groups and do not validate unless explicitly configured.
The following new configuration properties are available:
-
jakarta.persistence.validation.group.pre-merge -
jakarta.persistence.validation.group.pre-insert -
jakarta.persistence.validation.group.pre-upsert -
jakarta.persistence.validation.group.pre-delete
Breaking change: the property jakarta.persistence.validation.group.pre-persist previously was enabled by default.
It is now responsible for the validation at the pre-persist lifecycle event and is empty by default.
Applications that relied on pre-persist to configure insert-time validation groups should switch to pre-insert,
especially if the application plans to rely on usage of a stateless session.
New Features
See the website for the list of new features in the 8.0 series.
Changes to API
This section describes changes to contracts (classes, interfaces, methods, etc.) which are considered API.
Removed Session.replicate()
The deprecated Session.replicate() methods and the associated ReplicationMode enum have been removed.
There is no direct replacement.
For some import or overwrite workflows, consider StatelessSession#upsert(Object).
StatelessSession
Jakarta Persistence now defines a contract for "stateless" processing, named EntityAgent, similar to Hibernate’s StatelessSession.
Hibernate’s StatelessSession now implements EntityAgent.
2 methods on EntityAgent clash with methods already defined on StatelessSession -
-
insert()-EntityAgentdefines avoidreturn type, whereas Hibernate has historically returned the identifier of the inserted entity. -
fetch()-EntityAgentdefines that the fetched value be returned, whereasStatelessSessionhas historically defined avoidreturn type.
In both cases, StatelessSession has been changed to match the EntityAgent signatures.
Query API
One of the biggest changes in Jakarta Persistence 4.0 is the better modeling of "queries" into selections (TypedQuery) and mutations (its new Statement contract),
aligning closely with Hibernate’s already existing SelectionQuery and MutationQuery contracts.
At the same time, it deprecated all "operation methods" from its "raw" Query contract.
Hibernate’s Query contracts implement the Jakarta Persistence ones, so changes were needed here.
Another impact is that @NamedQuery and @NamedNativeQuery (as well as Hibernate’s variants) may no longer be used to define mutation queries -
-
@NamedQuery- ASelectionQuerydefined using HQL/JPQL -
@NamedStatement- AMutationQuerydefined using HQL/JPQL -
@NamedNativeQuery- ASelectionQuerydefined using native SQL -
@NamedNativeStatement- AMutationQuerydefined using native SQL
Removal of org.hibernate.transform package
The entire org.hibernate.transform package, which was deprecated since 6.0,
has been deleted. This includes Transformers, AliasToBeanResultTransformer,
AliasToBeanConstructorResultTransformer, AliasToEntityMapResultTransformer,
and ToListResultTransformer.
Use org.hibernate.query.TupleTransformer (a @FunctionalInterface) as a direct replacement.
Since it is a functional interface, lambdas can be used inline:
// Before (removed):
query.setTupleTransformer( Transformers.mapTransformer() );
// After:
query.setTupleTransformer( (tuple, aliases) -> {
Map<String, Object> map = new HashMap<>( tuple.length );
for ( int i = 0; i < tuple.length; i++ ) {
if ( aliases[i] != null ) {
map.put( aliases[i], tuple[i] );
}
}
return map;
} );
For bean-based result transformation (formerly Transformers.beanTransformer()),
use HQL/JPQL dynamic instantiation (select new MyDto(…)) or a
@jakarta.persistence.ConstructorResult mapping instead.
A TupleTransformer lambda also works well for simple DTO projections:
// Before (removed):
query.setTupleTransformer( Transformers.beanTransformer( MyDto.class ) );
// After:
query.setTupleTransformer( (tuple, aliases) -> {
MyDto dto = new MyDto();
dto.setId( (Integer) tuple[0] );
dto.setName( (String) tuple[1] );
return dto;
} );
Query and Transaction Timeouts
Historically, both Hibernate and Jakarta Persistence have defined timeouts as integer values.
Confusingly, Hibernate generally used second precision while Jakarta Persistence used millisecond precision.
Starting in 3.2, Jakarta Persistence added a new Timeout class which helps alleviate this potential confusion.
Originally this new Timeout type was not exposed on any API directly, so there was no impact from an API perspective.
However, this has changed a bit in 4.0 where Timeout is now exposed on certain APIs.
This causes a signature conflict with a few of Hibernate API methods; as part of fixing those conflicts, it was decided to change all exposures of timeout in Hibernate APIs to use Timeout as this helps clarify these confusions.
AttributeNode Subgraphs
As part of its support for "entity graphs", Hibernate’s org.hibernate.graph.AttributeNode contract extends the Jakarta Persistence jakarta.persistence.AttributeNode.
Starting in version 4.0, Jakarta Persistence has "fixed" the use of raw types for the AttributeNode#getSubgraphs and AttributeNode#getKeySubgraphs methods.
Hibernate has therefore needed to do the same in its org.hibernate.graph.AttributeNode.
Join and Fetch in Criteria
Jakarta Persistence has changed/fixed the type signature of methods in the Criteria API which return Join and Fetch based on the String name of attributes. This can lead to compilation issues if an application uses these methods in certain ways.
ProcedureCall and ProcedureOutputs
The API for ProcedureCall and ProcedureOutputs has been redesigned for a number of reasons, mostly -
-
Fix a long-standing bug where multiple result mappings were not handled properly.
-
Better handling of
ProcedureOutputsto avoid casts and, even worse, often untyped casts. This is achieved by the new methods added toOutput. See link:https://docs.jboss.org/hibernate/orm/8.0/whats-new/whats-new.html#procedure-outputs-casting. -
Clean up leakage of various SPI contracts into these API contracts.
-
Align with changes in Jakarta Persistence 4.0.
FindMultipleOption
The four enums which are implementors of org.hibernate.FindMultipleOption have been moved as inner enums of org.hibernate.FindMultipleOption itself :
-
org.hibernate.OrderingMode→org.hibernate.FindMultipleOption.OrderingMode -
org.hibernate.SessionCheckMode→org.hibernate.FindMultipleOption.SessionCheckMode -
org.hibernate.RemovalsMode→org.hibernate.FindMultipleOption.RemovalsMode -
org.hibernate.BatchSize→org.hibernate.FindMultipleOption.BatchSize
These enums were previously incubating, except for org.hibernate.BatchSize which is now marked as deprecated.
Changes to Hibernate Processor
Jakarta Data and Jakarta Persistence are now more integrated, which has led to some usage changes.
Repository interface discovery
With the addition of static queries to Jakarta Persistence comes a requirement for the Jakarta Persistence provider to discover Jakarta Data repository interfaces.
When Hibernate is started via a PersistenceConfiguration, discovery is impossible, and so Jakarta Data repository interfaces must be explicitly registered with the configuration:
var config = new PersistenceConfiguration("Jakarta Data Example");
// register entities
List.of(Book.class, Author.class, Publisher.class)
.forEach(config::managedClass);
// register repository interfaces
List.of(Library.class, Bookshop.class)
.forEach(config::managedClass);
Repository implementation instantiation
Previously, generated Jakarta Data Repository implementation classes were named with a trailing underscore. These classes are now named with a leading underscore to avoid collision with Jakarta Persistence static query methods. Thus, to directly instantiate a Jakarta Data Repository implementation, client code must now use:
Library library = new _Library(entityAgent);
This change does not affect code which obtains a repository by injection.
Changes to SPI
This section describes changes to contracts (classes, interfaces, methods, etc.) which are considered SPI.
Graph-based Flushing
Many changes were needed across various SPIs to accommodate the new Graph-based ActionQueue, focused around the new
org.hibernate.action.queue.spi package which primarily defines -
-
ActionQueue -
ActionQueueFactory -
QueueType -
PlanningOptions -
graph planning/bind/decompose/meta/plan contracts
Main SPI touch points include -
-
SessionFactoryImplementor#getActionQueueFactory()exposes the configured queue factory -
org.hibernate.engine.spi.ActionQueuewas renamed toActionQueueLegacyimplementing the neworg.hibernate.action.queue.spi.ActionQueueinterface -
session/event access now points at
org.hibernate.action.queue.spi.ActionQueue -
Collection SPI additions for graph decomposition
-
PersistentCollection#getRemovedEntities -
#getAddedEntities -
#getChangeSet -
CollectionChangeSet -
SnapshotIndexed
-
-
Persister/state-management SPI hooks for graph decomposition
-
Split
org.hibernate.sql.model.MutationTargetintoGraphMutationTargetandLegacyMutationTarget -
StateManagementsupport for graph-aware collection/entity state changes -
Batchsplit-
Batchnarrowed to lifecycle only -
GroupedBatch(extendsBatch) supports legacy "grouped statement" execution -
SingleStatementBatch,StatementBinder, andBatchedResultCheckersupport single statement batching (graph)
-
Classmate removal
The dependency on Classmate was removed, which had a minor impact on certain SPIs.
In particular, ClassmateContext was completely removed.
Raw types in JPA Bootstrap
Raw Map types were eliminated from the signatures of methods of the class
org.hibernate.jpa.boot.spi.Bootstrap.
Query Memento
Hibernate uses a number of implementations of its NamedQueryMemento contract to
model "named queries". Starting in version 3.2, Jakarta Persistence added the
TypedQueryReference contract intended to serve the much the same goal.
Hibernate’s NamedQueryMemento implementations were changed to extend from
TypedQueryReference.
In version 4.0, Jakarta Persistence has added some additional methods to
TypedQueryReference which now conflict with some of Hibernate’s existing methods
requiring a rename. Notably, the addition of TypedQueryReference#getParameterTypes
caused conflict’s with Hibernate’s NamedSqmQueryMemento#getParameterTypes. To
address this conflict, we’ve renamed the NamedSqmQueryMemento method to
#getAnticipatedParameterTypes.
Scanning
Jakarta Persistence clarifies that scanning is the responsibility of the
container, rather than the provider. To that effect, the scanning SPI, and
related ArchiveDescriptor SPI, have been rewritten. During "EE bootstrap",
Hibernate no longer performs scanning. Instead, the jakarta.persistence.spi.PersistenceUnitInfo
has been augmented to make it clear that the container passes in any classes
discovered during scanning.
Hibernate does still provide this SPI for use by containers - Wild Fly for example takes advantage of this.
Hibernate also uses this scanning SPI when applications use HibernatePersistenceConfiguration
bootstrapping and supply the root and/or non-root URLs.
Changes in Behavior
This section describes changes in behavior that applications should be aware of.
Jakarta Validation DDL influence enabled by default
Jakarta Persistence 4.0 specifies that constraints from Jakarta Validation annotations,
such as @NotNull, @Size, @Digits and others, should influence the generated DDL
schema. For example, a property annotated @NotNull will result in a non-nullable column.
Previously, this behavior required explicitly setting the validation mode to DDL
(a Hibernate-specific extension to the JPA ValidationMode enum).
As a result:
-
org.hibernate.boot.beanvalidation.ValidationMode.DDLis deprecated for removal. -
A new setting
hibernate.tooling.schema.apply_validation_constraintshas been added toSchemaToolingSettingsas the way to control the influence of Jakarta Validation constraints on the generated DDL schema. Valid values are defined byValidationConstraintDdlInfluence:-
AUTO(default) — apply constraints if a Jakarta Validation provider is available, silently skip otherwise -
REQUIRED— apply constraints and fail if no Jakarta Validation provider is available -
DISABLED— do not apply constraints
-
-
The legacy setting
hibernate.validator.apply_to_ddlis also deprecated for removal in favor of the new setting.
Graph-based Flushing
As described in What’s New and SPI Changes, 8.0 changes how Hibernate coordinates work needed for flushing. This new coordinator can often lead to different SQL or SQL being performed in different order. The legacy strategy remains temporarily available by configuration using
hibernate.flush.queue.type=legacy
Queries Returning Detached Collections
A HQL query like:
select items from Order /* each query result is a collection */
select elements(items) from Order /* each query result is a collection */
select indices(items) from Order /* each query result is a collection */
now returns a detached collection without flattening.
In previous versions of Hibernate, such queries were accepted, and due to an undocumented and unsupported behavior, resulted in an implicit join and flattening of the collection. To recover this previous undocumented behavior, the singular form of the functions may be used:
select element(items) from Order /* flatten the items collection */
select indice(items) from Order /* flatten the items collection */
Procedure Results
The behavior of Hibernate’s handling for creating a ProcedureCall / StoredProcedureQuery with multiple result set mappings has been changed to fix a long-standing bug and comply with the Jakarta Persistence specification.
Hibernate now interprets the multiple mappings as one per result available from the procedure. E.g.
var call = session.createProcedureCall("the_proc",
Region.class, Initiative.class);
var outputs = call.getOutputs();
// old approach with casts still works -
List<Region> regions = ( (ResultSetOutput<Region>) outputs.getCurrent() ).getResultList();
outputs.gotToNext();
// but consider this instead -
List<Initiative> initiatives = outputs.getCurrent()
.asResultSetOutput(Initiative.class)
.getResultList();
Final fields of entities
A final field of an entity is now treated as if it were annotated @Immutable,
that is, it is excluded from dirty checking and from SQL UPDATE statements.
In order to eliminate the possibility of "silent" changes in behavior, attempts
to modify the value of a final fields using the merge() method are rejected.
If a field of an entity can actually be updated via merge(), the field must be
declared non-final.
Session.get
Jakarta Persistence now defines a series of overloaded EntityHandler.get() methods as corollaries to EntityHandler.find().
These methods are defined to throw an EntityNotFoundException rather than return null.
This operates quite differently from the older Hibernate Session.get() methods which
-
still returned null if no entity with that is was found
-
force initialized the entity, if there was one and it was previously uninitialized.
Applications which use(d) Session.get() should be aware of this change in behavior.
Scanning
As discussed in Scanning above, Hibernate no longer performs scanning during "EE bootstrap". It is expected by Jakarta Persistence that the container perform the scanning prior to bootstrapping Hibernate.
Remove AttributeNode from EntityGraph
Calls to EntityGraph.removeAttributeNode() and EntityGraph.removeAttributeNodes() now behave in specification compliant way -
when the graph is used as a "load graph", removing an attribute "suppresses inclusion of an attribute mapped for eager fetching".
Entity Listener Callbacks
Jakarta Persistence has clarified that listener-style callback classes (@EntityListeners) may define multiple
methods for a given event type -
An entity listener class may have multiple callback methods for a given type of lifecycle event, but at most one callback method for a given type of event and given parameter type.
Using <E> as a generic type for the signatures, it says -
where
Eis an entity class, a mapped superclass, or a supertype of the entity class or mapped superclass to which the entity listener applies. If multiple entity classes are assignable to the typeE, the callback method is invoked for any such class to which the entity listener applies.
@Entity
@EntityListeners( AnimalWatcher.class )
public class Cat implements Animal { ... }
@Entity
@EntityListeners( AnimalWatcher.class )
public class Dog implements Animal { ... }
public class AnimalWatcher {
@PostInsert
public void postInsert(Animal animal) { ... }
@PostInsert
public void postInsert(Cat animal) { ... }
@PostInsert
public void postInsert(Dog animal) { ... }
}
Be aware that creating a Cat, with the above model, will result in calls to both postInsert(Animal) and postInsert(Cat); and
that creating a Dog, both postInsert(Animal) and postInsert(Dog).
Discriminator-Based Multi-Tenancy and Row-Level Security
On databases with built-in support for row-level security, Hibernate now uses row-level security to enforce the visibility rules implied by discriminator-based multi-tenancy. This requires specific DDL which the schema management tooling is responsible for generating.
To recover the previous behavior, where discriminator-based multi-tenancy rules were not enforced by the database, set the configuration property hibernate.multi_tenant.rls_enabled=false.
Session Reentrancy
It has always been illegal to call the Hibernate session from an Interceptor or JPA entity lifecycle callback, but Hibernate has never previously enforced this, opening the door to undefined behavior in programs which did not respect this restriction.
Hibernate now throws IllegalStateException if the session is accessed in an unsafe way from a callback.
EntityExistsException and Exceptions from StatelessSession
JPA 4 specifies that EntityExistsException should be thrown on any sort of unique key violation that occurs while inserting an entity, which means that Hibernate now throws this exception type in some cases where ConstraintViolationException was previously thrown.
After the introduction of EntityAgent, the Hibernate StatelessSession now throws the exception types mandated by the JPA specification.
Previously, it threw native exception types.
CUBRID dialect
CUBRIDDialect now requires CUBRID 10.2 or later, the oldest version still supported by the vendor,
and declares several capability flags explicitly for 10.2. Two of these are visible at runtime.
getNullOrdering() is now SMALLEST, which matches CUBRID’s native ordering (NULL first when
ascending and last when descending). Queries that relied on the previous inherited default may order
NULL values differently.
Pessimistic locking now produces a FOR UPDATE clause. The dialect previously returned an empty lock
string, so PESSIMISTIC_READ and PESSIMISTIC_WRITE had no effect.
Changes in XSD
This section describes changes in XML Schema Descriptors
Table and Column Comments
Previous versions of the mapping.xsd defined table and column comments (for schema export)
using XSD attributes. This was a decision made at the time to facilitate users migrating
from hbm.xml mapping format. In the intervening period, Jakarta Persistence has
also added comments to its table and column XSD types, but using dedicated element.
We now align with the Jakarta Persistence approach of using elements. This will require
changes any mapping.xml documents which define table or column comments. E.g.,
<entity ...>
<table ... comment="some comment"/>
</entity>
would need to be changed to
<entity ...>
<table>
<comment>some comment</comment>
</table>
</entity>
Removal of orphan-removal from <many-to-many>
The orphan-removal attribute has been removed from the <many-to-many> element in the XML mapping XSD.
Although previous versions of the schema allowed specifying orphan-removal="true" on a <many-to-many> mapping, this attribute was never processed during boot. Orphan removal behavior was silently ignored for many-to-many associations. The attribute has been removed from the XSD to reflect the actual supported behavior.
Any mapping.xml documents using this attribute:
<many-to-many orphan-removal="true" ... />
should remove the orphan-removal attribute, as it had no effect.
Removal of proxy and polymorphism from Entity Mappings
TThe proxy and polymorphism XML mapping attributes have been removed from the entity mapping XSD.
This change follows the removal of the corresponding @Proxy and @Polymorphism annotations in Hibernate ORM 7.0.0.Beta1.
Both elements have been removed from the mapping-8.0.xsd schema along with the polymorphism-type simple type definition.
Any mapping.xml documents using these elements:
<entity ...>
<proxy>com.example.MyEntityProxy</proxy>
<polymorphism>EXPLICIT</polymorphism>
</entity>
should remove them, as they are no longer supported.
Migration of Spanner PostgreSQL Dialect to core
The SpannerPostgreSQLDialect, which was previously part of the hibernate-community-dialects artifact under the package name org.hibernate.community.dialect, has been migrated to the hibernate-core artifact and package name org.hibernate.dialect.
Users currently using the community dialect must update their dialect configuration to use org.hibernate.dialect.SpannerPostgreSQLDialect (or rely on Hibernate’s automatic dialect resolution).
Changes to DDL generation
This section describes changes to DDL generated by the schema export tooling. Such changes typically do not impact programs using a relational schema managed externally to Hibernate.
Optional: Migrating from Hibernate Envers to core @Audited
This migration is entirely optional. Hibernate Envers continues to work as before in 8.0. However, if you wish to take advantage of the new core @Audited functionality, this section describes the steps involved.
|
The new core audit support provides very similar functionality to Envers with a simpler programming model: you can use standard Session APIs (and even HQL queries!) within a temporal session opened via atChangeset(), or the more specialized AuditLog functionality, to access the audit logs of your entities.
Annotation changes
| Envers | Core |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map your REVINFO table
Define a @Changelog entity that maps to the existing REVINFO table (or use the built-in DefaultChangelog):
@Entity
@Table(name = "REVINFO")
@Changelog(listener = MyChangesetListener.class)
public class MyRevision extends ChangelogMapping { ... }
API changes
| Envers | Core |
|---|---|
|
|
|
|
|
|
|
|
|
HQL in |
|
|
Custom |
HQL with |