Query with Implicit SELECT and No Explicit Result Type
Session flush and persist
Refreshing/Locking Detached Entities
Cascading Persistence for @Id and @MapsId Attributes
Temporal Types Returned by Native Queries
XML FormatMapper Changes
Criteria API and Implicit Treats
SessionFactory Name (and JNDI)
Entity Discovery
@OrderColumn in Unowned @OneToMany Associations
ValidationMode#AUTO
Update queries affecting immutable entities
Changes Affecting DDL
Default Precision for timestamp
DDL type for Java
float
and
double
changed on Oracle
Array Mapping Changes
DB2, SAP HANA, SQL Server and Sybase ASE
MySQL/MariaDB
Default DDL Type for char and Character
Changes Related to Settings
LockOptions
Connection Pools
Changes to the Maven Plugin
While the Hibernate team still believes that our long published interpretation of LGPL
to be compatible with ASL and similar licenses, enough FUD has been spread about it to
be considered a lost cause in terms of inclusion in various projects. At this point,
it just makes sense to change - it opens up wider acceptance amongst Apache/Jakarta
projects and others.
As part of this effort, the Hibernate team reached out to the authors of
"non-trivial" contributions to request permission to relicense their
work under the Apache License. The response was overwhelming positive, although
we never heard back from some contributors and another explicitly disagreed.
This required a few actions on our part:
Nulls
(JPA) v.
NullPrecedence
(Hibernate), including JPA’s new
Order#getNullPrecedence()
returning
Nulls
colliding with Hibernate’s
SqmSortSpecification#getNullPrecedence
returning
NullPrecedence
. Hibernate’s form
was renamed to
SqmSortSpecification#getHibernateNullPrecedence
to avoid the collision.
SchemaManager
is now also a JPA contract exposed as
EntityManagerFactory#getSchemaManager
which leads to type issues for
Hibernate’s
SessionFactory#getSchemaManager
. Hibernate’s
SchemaManager
now extends the new JPA
SchemaManager
.
But that is a bytecode incompatibility.
JPA has added support in its Graph API for things Hibernate has supported for some time. Some of those are collisions
requiring changes to the Hibernate API.
Transaction#getTimeout
. JPA 3.2 adds
#getTimeout
but uses
Integer
whereas Hibernate has historically used
int
. Note that this raises the possibility of a
NullPointerException
during migration if, e.g., performing direct comparisons on the timeout value against an in (auto unboxing).
Hibernate Models
For many years Hibernate has used the Hibernate Commons Annotations (HCANN) library for handling various low-level tasks
related to understanding the structure of an application domain model, reading annotations and weaving in XML
mapping documents.
However, HCANN suffers from a number of limitations that continued to be problematic. And given
the use of HCANN across multiple projects, doing the needed refactoring was simply not possible.
The
Hibernate Models
project was developed to be a better alternative
to HCANN. Hibernate Models is essentially an abstraction over reflection (
Type
,
Class
,
Member
, …) and
annotations. Check out its project page for complete details.
7.0 uses Hibernate Models in place of HCANN.
This section describes changes to contracts (classes, interfaces, methods, etc.) which are consider
API
.
Defer to JPA
A general theme in 7.0 has been to remove Hibernate-specific features that have a direct replacement in JPA.
Session#load
Session#load
methods have been removed in favor of
Session#getReference
which have the same semantic.
Session#get
Session#get
methods were deprecated in favor of the JPA-standard
Session#find
, and new overloads of
Session#find
were added.
Session#refresh(String entityName, Object object)
Removed in favor of
Session#refresh(Object object)
Session#refresh(String entityName, Object object, LockOptions lockOptions)
Removed in favor of
Session#refresh(Object object, LockOptions lockOptions)
org.hibernate.graph Package
The
EntityGraph
API was enhanced in JPA 3.2, and made much more useful.
The incubating package
org.hibernate.graph
contains extensions to that API, which have been significantly impacted by the migration to JPA 3.2, and by the addition of new functionality.
Furthermore, some legacy operations were declared with incorrect generic type signatures (by both JPA, and by Hibernate).
This package has been significantly re-engineered, and the impact of this effort includes:
Removed
@TestForIssue
(for testing purposes) → use
org.hibernate.testing.orm.junit.JiraKey
or
org.hibernate.testing.orm.junit.JiraKeyGroup
Removed
@Target
- see
Replace @Target
Here we focus on
@Proxy#lazy
attribute which, again, was hardly ever useful.
By default (true), Hibernate would proxy an entity when possible and when asked for.
"Asked for" includes calls to
Session#getReference
and lazy associations.
All such cases though are already controllable by the application.
FetchType.EAGER
(the default for to-one associations anyway), possibly combined with
@Fetch
,
EntityGraph
, or a
@FetchProfile
.
Replace @Target
The
@Target
annotation has been replaced with the new
@TargetEmbeddable
, which is intended to indicate the
@Embeddable
class that is the implementation target of the embedded instance.
This new annotation is only allowed on members which are embedded (
@Embedded
) or are a collection-of-embeddables (
@ElementCollection
).
For
@Embedded
cases, the annotation may be placed on the member or the member’s declared type.
For
@ElementCollection
cases, however, it must be placed on the member.
See the
User Guide
for details.
Metamodel API layering
The following changes were made to the package
org.hibernate.metamodel.model.domain
to remove layer-breakers.
Domain metamodel types no longer inherit
SqmExpressible
or
SqmPathSource
, and
PathSource
was introduced to compensate.
DomainType
no longer extends
BiindableType
.
JDBC exception interpretation
Hibernate now does a better and more consistent job of interpreting database-specific error codes in
JDBCException
s and translating to subtypes of
PersistenceException
.
In particular, interpretation of integrity constraint violations was improved significantly.
Also,
LockAcquisitionException
now extends
PessimisticLockException
.
Removed Query#setOrder
Query#setOrder
was an incubating API added in support of Hibernate’s Jakarta Data and Repositories implementations.
It was never a great solution and has been replaced with a better alternative -
QuerySpecification
.
Miscellaneous
Removed
org.hibernate.Metamodel
in favor of
org.hibernate.metamodel.model.domain.JpaMetamodel
Removed
SqmQualifiedJoin
- all joins are qualified.
Both
NaturalIdLoadAccess#using(Map)
and
NaturalIdMultiLoadAccess#compoundValue()
have been removed in favor of
Map#of()
Removed
Session.LockRequest
- use
LockOptions
instead
SessionFactory.createEntityManager()
now returns
Session
for convenience
CommonQueryContract.setFlushMode()
was deprecated in favor of
setQueryFlushMode
accepting a
QueryFlushMode
Incubating interfaces
BindableType
,
OutputableType
, and
BindingContext
were moved to
org.hibernate.type
This section describes changes to contracts (classes, interfaces, methods, etc.) which are consider
SPI
.
Configurable generators
The signature of the
Configurable#configure
method changed from accepting just a
ServiceRegistry
instance to the new
GeneratorCreationContext
interface, which exposes a lot more useful information when configuring the generator itself. The old signature has been deprecated for removal, so you should migrate any custom
Configurable
generator implementation to the new one. Or better yet, consider migrating to
@IdGeneratorType
.
Integrator
The previously deprecated method
org.hibernate.integrator.spi.Integrator#integrate(Metadata,SessionFactoryImplementor,SessionFactoryServiceRegistry)
have been removed in favor of its replacement
org.hibernate.integrator.spi.Integrator#integrate(Metadata,BootstrapContext,SessionFactoryImplementor)
Interceptor
Quite a few (again, previously deprecated) methods on
Interceptor
have been removed in favor of their replacement. This mainly deals with the change in expected Java type of identifiers (done in 6.0) from
Serializable
to
Object
.
Changes to UserType and CompositeUserType
The API interfaces
UserType
and
CompositeUserType
leaked the SPI types
SharedSessionContractImplementor
and
SessionFactoryImplementor
, which was a layer-breaker.
The solution was to change the signature of
nullSafeSet()
and
nullSafeGet()
in
UserType
via deprecation of the previous declarations, and remove some unnecessary parameters from methods of the incubating interface
CompositeUserType
.
JFR SPI
The types
EventMonitor
and
DiagonosticEvent
replace the now-deprecated SPIs
EventManager
and
HibernateMonitoringEvent
use for integration with Java Flight Recorder.
Hibernate now reports many more kinds of
DiagnosticEvent
to JFR.
Miscellaneous
org.hibernate.metamodel.spi.MetamodelImplementor
was removed in favor of
org.hibernate.metamodel.MappingMetmodel
or
org.hibernate.metamodel.model.domain.JpaMetamodel
Removed
AdditionalJaxbMappingProducer
in favor of
AdditionalMappingContributor
.
Removed
MetadataContributor
in favor of
AdditionalMappingContributor
Removed incubating
setOrder()
from
SelectionQuery()
in favor of
SelectionSpecification.sort()
Various JDBC types were moved to an internal package — use
@JdbcTypeCode
instead of
@JdbcType
to map your attributes to these types.
For example use
@JdbcTypeCode(SqlTypes.INTERVAL_SECOND)
instead of
@JdbcType(PostgreSQLIntervalSecondJdbcType.class)
.
Other moved types include
PostgreSQLEnumJdbcType
,
PostgreSQLOrdinalEnumJdbcType
,
PostgreSQLUUIDJdbcType
,
DB2StructJdbcType
,
H2DurationIntervalSecondJdbcType
,
H2JsonJdbcType
,
OracleBooleanJdbcType
,
OracleEnumJdbcType
,
OracleJsonJdbcType
,
OracleOrdinalEnumJdbcType
,
OracleReflectionStructJdbcType
,
OracleXmlJdbcType
.
PersistentAttributeType
As of 7.0, Hibernate applies much better validation of an attribute specifying multiple PersistentAttributeTypes.
Jakarta Persistence 3.2 has clarified this in the specification. E.g., the following examples are all now illegal -
@Basic
@ManyToOne
private Employee manager;
@ManyToOne
private
Employee
manager
;
Misplaced Annotations
7.0 does much more in-depth checking that annotations appear in the proper place. While previous versions
did not necessarily throw errors, in most cases these annotations were simply ignored.
For example, this code now results in an error:
@Entity
class Book {
// specifies FIELD access, properties should not be annotated
Integer id;
// previously ignored, this is an error now
@Column(name="category")
String getType() { ... }
Identifier Generators
Starting in 7.0 it is no longer valid to combine GenerationType#SEQUENCE with anything other than
@SequenceGenerator nor GenerationType#TABLE with anything other than @TableGenerator. Previous
versions did not validate this particularly well.
JavaBean Conventions
Previous versions allowed some questionable (at best) attribute naming patterns.
For example, this property declaration is no longer allowed:
@Basic
String isDefault();
Disallowed Converters
JPA AttributeConverters are incompatible with the annotations @Id, @Version, @Enumerated, @Embedded, @Temporal and all association-mapping annotations.
Previously, any converter applied to an attribute with an incompatible annotation was simply ignored.
Hibernate now reports an error in this situation.
This includes auto-applied converters.
To suppress the error for an auto-applied converter, use @Convert(disableConversion=true).
StatelessSession Behavior
The behavior of Hibernate’s StatelessSession has changed in 2 specific ways to be aware of:
StatelessSession and Second-Level Cache
A stateless session now makes use of the second-level cache by default. This will affect migrating applications using second-level cache and StatelessSession.
To completely bypass the second-level cache, recovering the previous behavior, call setCacheMode(CacheMode.IGNORE).
It’s often important to explicitly disable puts to the second-level cache in code which performs bulk processing.
Set the cache mode to GET or configure jakarta.persistence.cache.storeMode to BYPASS.
StatelessSession and JDBC Batching
The configuration property hibernate.jdbc.batch_size now has no effect on a StatelessSession.
JDBC batching may be enabled by explicitly calling setJdbcBatchSize().
However, the preferred approach is to use the new explicit batch operations via insertMultiple(), updateMultiple(), or deleteMultiple().
As of Hibernate 7, the method remains deprecated, and potentially-ambiguous queries are no longer accepted.
Migration paths include:
add X.class or Object[].class as a second argument, to disambiguate the interpretation of the query, or
in the case where the query should return exactly one entity, explicitly assign the alias this to that entity.
Session flush and persist
The removal of CascadeType.SAVE_UPDATE slightly changes the persist and flush behaviour to conform with the Jakarta Persistence specification.
Making a transient entity persistent or flushing a managed entity now results in an jakarta.persistence.EntityExistsException if:
the entity has an association with cascade = CascadeType.ALL or cascade = CascadeType.PERSIST, and
the association references a detached instance of the associated entity class.
To avoid this exception, the reference to the detached instance should be replaced with a reference to a managed instance associated with the current session.
Such a reference may be obtained by calling merge() or getReference() on the detached entity instance.
Consider the following model
@Entity
class Parent {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
@LazyCollection(value = LazyCollectionOption.EXTRA)
private Set<Child> children = new HashSet<>();
public void addChild(Child child) {
children.add( child );
child.setParent( this );
@Entity
class Child {
@ManyToOne
private Parent parent;
Assuming we have c1 as a detached Child, the following code will now result in jakarta.persistence.EntityExistsException being thrown at flush time:
Parent parent = session.find( Parent.class, parentId );
parent.addChild( c1 );
Instead, c1 must first be re-associated with the Session using merge:
Parent parent = session.find( Parent.class, parentId );
Child merged = session.merge( c1 );
parent.addChild( merged );
Refreshing/Locking Detached Entities
Traditionally, Hibernate allowed detached entities to be refreshed. However, Jakarta Persistence prohibits this practice and specifies that an IllegalArgumentException should be thrown instead. Hibernate now fully aligns with the JPA specification in this regard.
Along the same line of thought, also acquiring a lock on a detached entity is no longer allowed.
To this effect the hibernate.allow_refresh_detached_entity, which allowed Hibernate’s legacy refresh behaviour to be invoked, has been removed.
Cascading Persistence for @Id and @MapsId Attributes
Previously Hibernate automatically enabled cascade=PERSIST for association fields annotated @Id or @MapsId.
This was undocumented and unexpected behavior, and arguably against the intent of the Persistence specification.
Existing code which relies on this behavior should be modified by addition of explicit cascade=PERSIST to the association field.
Temporal Types Returned by Native Queries
In the absence of a @SqlResultSetMapping, previous versions of Hibernate used java.sql types (Date, Time, Timestamp) to represent date/time types returned by a native query.
In 7.0, such queries return types defined by java.time (LocalDate, LocalTime, LocalDateTime) by default.
The previous behavior may be recovered by setting hibernate.query.native.prefer_jdbc_datetime_types to true.
Previous versions of Hibernate ORM used an undefined/provider-specific format for serialization/deserialization of
collections, maps and byte arrays to/from XML, which was not portable.
XML FormatMapper implementations now use a portable format for collections, maps, and byte arrays.
This change is necessary to allow mapping basic arrays as SqlTypes.XML_ARRAY.
The migration requires to read data and re-save it.
To retain backwards compatibility, configure the setting hibernate.type.xml_format_mapper.legacy_format to true.
Criteria API and Implicit Treats
It was previously possible to use the string version of the jakarta.persistence.criteria.Path#get and jakarta.persistence.criteria.From#join methods with names of attributes defined in an inheritance subtype of the type represented by the path expression. This was handled internally by implicitly treating the path as the subtype which defines said attribute. Since Hibernate 7.0, aligning with the JPA specification, the Criteria API will no longer allow retrieving subtype attributes this way, and it’s going to require an explicit jakarta.persistence.criteria.CriteriaBuilder#treat to be called on the path first to downcast it to the subtype which defines the attribute.
Implicit treats are still going to be applied when an HQL query dereferences a path belonging to an inheritance subtype.
SessionFactory Name (and JNDI)
Hibernate defines SessionFactory#getName (specified via cfg.xml or hibernate.session_factory_name) which is used to
help with (de)serializing a SessionFactory. It is also, unless hibernate.session_factory_name_is_jndi is set to false,
used in biding the SessionFactory into JNDI.
This SessionFactory#getName method pre-dates Jakarta Persistence (and JPA). It now implements EntityManagerFactory#getName
inherited from Jakarta Persistence, which states that this name should come from the persistence-unit name.
To align with Jakarta Persistence (the 3.2 TCK tests this), Hibernate now considers the persistence-unit name if no
hibernate.session_factory_name is specified.
However, because hibernate.session_factory_name is also a trigger to attempt to bind the SessionFactory into JNDI,
this change to consider persistence-unit name, means that each SessionFactory created through Jakarta Persistence now
has a name and Hibernate attempts to bind it to JNDI.
To work around this we have introduced a new hibernate.session_factory_jndi_name setting that can be used to explicitly
specify a name for JNDI binding. The new behavior is as follows (assuming hibernate.session_factory_name_is_jndi is not explicitly configured):
Hibernate can use the persistence-unit name for binding into JNDI as well, but hibernate.session_factory_name_is_jndi
must be explicitly set to true.
Entity Discovery
Entity discovery, i.e. automatic detection of mapped Java classes through classpath scanning, now requires by default the new hibernate-scan-jandex module. Standalone Hibernate ORM applications relying on this feature will need to add a dependency to this new module in order to continue using it.
Users can still provide custom org.hibernate.Scanner implementations via the org.hibernate.boot.archive.scan.spi.ScannerFactory service or the hibernate.archive.scanner configuration parameter.
@OrderColumn in Unowned @OneToMany Associations
In an unowned (mappedBy) one-to-many association, an @OrderColumn should, in principle, also be mapped by a field of the associated entity, and the value of the order column should be determined by the value of this field, not by the position in the list.
Previously, since version 4.1, Hibernate would issue superfluous SQL UPDATE statements to set the value of the order column based on the state of the unowned collection.
This was incorrect according to the JPA specification, and inconsistent with the natural semantics of Hibernate.
In Hibernate 7, these SQL UPDATE statements only occur if the @OrderColumn is not also mapped by a field of the entity.
ValidationMode#AUTO
Starting in 7.0, when ValidationMode#AUTO is specified and a Bean Validation provider is available but creating the ValidatorFactory results in an exception, that exception is now propagated (re-thrown).
Update queries affecting immutable entities
Previously, hibernate.query.immutable_entity_update_query_handling_mode defaulted to warning, and update and delete queries affecting immutable entities were allowed.
Now, by default, such update and delete queries result in an exception.
hibernate.query.immutable_entity_update_query_handling_mode=allow
to suppress this error and re-allow bulk update for immutable entities.
Default Precision for timestamp
The default precision for Oracle timestamps was changed to 9, i.e. nanosecond precision.
The default precision for SQL Server timestamps was changed to 7, i.e. 100 nanosecond precision.
Note that these changes only affect DDL generation.
DDL type for Java float and double changed on Oracle
Previous version of Hibernate ORM mapped Java float and double to Oracle float(p), real or double precision
types, which are all internally implemented as number. To avoid potential misbehavior compared to Java execution
and match the expectations of the IEEE floating point semantics as requested by using Java float/double,
the default DDL types were changed to Oracles IEEE floating point types binary_float and binary_double respectively.
Migration requires multiple steps because Oracle doesn’t support online type changes:
alter table TBL add (NEW_COLUMN binary_float);
update TBL set NEW_COLUMN=OLD_COLUMN;
alter table TBL drop column OLD_COLUMN;
alter table TBL rename column NEW_COLUMN to OLD_COLUMN;
Note that changing the schema is not required for Hibernate ORM to work correctly.
The previous behavior may be recovered by setting hibernate.dialect.oracle.use_binary_floats to false.
Array Mapping Changes
DB2, SAP HANA, SQL Server and Sybase ASE
On DB2, SAP HANA, SQL Server and Sybase ASE, basic arrays now map to the SqlTypes.XML_ARRAY type code,
whereas previously, the dialect mapped arrays to SqlTypes.VARBINARY.
The SqlTypes.XML_ARRAY type uses the xml DDL type which enables using arrays in other features through the various XML functions.
The migration requires to read data and re-save it. Note that XML support on Sybase ASE is not enabled by default
and requires to run sp_configure 'enable xml', 1.
To retain backwards compatibility, configure the setting hibernate.type.preferred_array_jdbc_type to VARBINARY.
MySQL/MariaDB
On MySQL and MariaDB, basic arrays now map to the SqlTypes.JSON_ARRAY type code,
whereas previously, the dialect mapped arrays to SqlTypes.VARBINARY.
The SqlTypes.JSON_ARRAY type uses the json DDL type which enables using arrays in other features through the various JSON functions.
The migration requires to read data and re-save it.
To retain backwards compatibility, configure the setting hibernate.type.preferred_array_jdbc_type to VARBINARY.
Default DDL Type for char and Character
Previously, char and Character fields were, by default, mapped to char(1) columns by the schema export tool.
However, MySQL treats a char(1) containing a single space as an empty string, resulting in broken behavior for some HQL and SQL functions.
Now, varchar(1) is used by default.
LockOptions has been marked deprecated.
Since JPA 3.2 and Hibernate 7, a LockMode (or LockModeType), Timeout, or PessimisticLockScope may be passed directly as an option to find(), refresh(), or lock().
Therefore, this class is obsolete as an API and will be moved to an SPI package.
Use this
We have decided to drop built-in support for the Vibur, Proxool and UCP Connection Pools for a variety of reasons - the main one being that we are not able to properly test them.
We recommend using Agroal or HikariCP instead.
Alternatively, you may implement the ConnectionProvider interface to integrate the connection pool of your choice.
In fact, some connection pools already include their own implementations of ConnectionProvider.
The Maven bytecode enhancement plugin has been completely rewritten since version 7.0. The change that has the
biggest consequence is that the artifactId and groupId of the Maven artifact have changed. As of now, you will
need to include the plugin in the build section in your Maven pom file as illustrated below.
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-maven-plugin</artifactId>
<version>7.0.10.Final</version>
<executions>
<execution>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
Also, because of a regression and in contrast with the corresponding Ant task and Gradle plugin, the default values
of the enableLazyInitialization and enableDirtyTracking are set to false while it used to be true. This is fixed in Hibernate ORM 7.1.
The documentation in the userguide has been rewritten accordingly and completed with more examples.