- Background
- Known changes
- Jakarta Persistence
- Reading from JDBC
- Type system / custom types
- Procedure Parameters, enable passing nulls
- Query
- Ordinal Parameters binding
- Native Query
- SQM
- Remove support for special plural attribute properties
- Remove support for comparing association against FK value
- Removals
- org.hibernate.Interceptors
- Fetch behaviour change
- Removal of legacy Hibernate Criteria API
- Removal of loader walkers
- Restructuring of the loader package
- Restructuring of the sql package
- Changes in the type package
- Dropped support for pass-through HQL tokens
This guide discusses migration from Hibernate ORM version 6.0. For migration from earlier versions, see any other pertinent migration guides as well.
6.0 moves from Java Persistence as defined by the Java EE specs to
Jakarta Persistence as defined by the Jakarta EE spec. The most immediate
impact of this change is that applications would need to be updated to use
the Jakarata Persistence classes (jakarta.persistence.
) instead of the Java
Persistence ones (javax.persistence.
).
The Jakarta spec also renames the JPA settings (again, from javax.persistence.
to
'jakarta.persistence.') and defines a new set of XSD namespaces for orm.xml
and
persistence.xml
files.
Jakarta provides a transformer tool which, along with appropriate "rules", will transform a project from Java Persistence to Jakarta Persistence. This can update package names in source, settings, xsd references and more.
Note
|
As far as the XSD and setting changes, Hibernate does support both sets as a temporary aid in migration. It logs a deprecation warning when the Java EE variants are used. |
-
The default type for
Duration
was changed toNUMERIC
which could lead to schema validation errors
Passing null or not is now triggered by whether setting the parameter was called at all. In other ords a distinction is made between calling setParameter
passing null versus not calling setParameter
at all. In the first case, we pass along the null value; in the second we do not.
HQL ordinal parameter binding is 1-based, this means that queries like
s.createQuery( "select p from Parent p where id in ?0", Parent.class );
query.setParameter( 0, Arrays.asList( 0, 1, 2, 3 ) );
that uses a 0-based positional binding are not supported, and they should be changed to the following
s.createQuery( "select p from Parent p where id in ?`", Parent.class );
query.setParameter( 1, Arrays.asList( 0, 1, 2, 3 ) );
HQL queries that use joins without specifying a select clause e.g. from Person p join p.address
do not return a List<Object[]>
with an entry per join anymore, but a list of Person
.
The HQL query select p,a from Person p join p.address a
returns instead a List<Object[]>
.
e.g.
@Entity
class Person {
...
@ManyToOne
Address address
...
}
@Entity
class Address {
...
}
List<Person> result = session.createQuery("from Person p join p.address").list();
List<Object[]> results = session.createQuery("select p, a from Person p join p.address a").list();
jakarta.persistence.Query#getResultStream()
and org.hibernate.query.Query#stream()
do not return a Stream
decorator anymore, so in order to close the underlying IO resources is now necessary to explicitly call the Stream#close()
method. The JDK Stream
documentation is quite explicit about the need for an explicit call to close
by the user to avoid resource leakages, so we build upon this requirement.
Given the NamedNativeQuery
@NamedNativeQuery(
name = "fn_person_and_phones",
query = "{ ? = call fn_person_and_phones( ? ) }",
callable = true,
resultSetMapping = "person_with_phones"
)
the code
scope.inTransaction(
entityManager -> {
try {
List<Object[]> postAndComments = entityManager.createNamedQuery("fn_person_and_phones" ).setParameter( 1, 1L ).getResultList();
is going to throw an IllegalArgumentException
.
If you want to retain the named version, you can change the definition to
@NamedStoredProcedureQuery(
name = "fn_person_and_phones",
procedureName = "fn_person_and_phones",
resultSetMapping = "person_with_phones",
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
parameters = {
@StoredProcedureParameter(type = Long.class)
}
)
and call this like
List<Object[]> postAndComments = entityManager.createNamedStoredProcedureQuery( "fn_person_and_phones" ).setParameter( 1, 1L ).getResultList();
or not define the stored procedure and use this code
List<Object[]> postAndComments = entityManager.createStoredProcedureQuery( "fn_person_and_phones", "person_with_phones" ).setParameter( 1, 1L ).getResultList();
Prior to 6.0, it was possible to de-reference special properties on plural attributes like size
which was dropped.
The special properties lead to confusion and were sometimes ambiguous. The replacement is the function syntax.
- size
-
The collection size can be determined by using the
size( pluralAttribute )
function instead - elements
-
The collection elements can be referred to by using the
value( pluralAttribute )
function instead - indices
-
The collection indices can be referred to by using the
index( pluralAttribute )
orkey( pluralAttribute )
function instead - index
-
The collection index can be referred to by using the
index( pluralAttribute )
orkey( pluralAttribute )
function instead - maxindex
-
The collection maximum index can be determined by using the
maxindex( pluralAttribute )
function instead - minindex
-
The collection minimum index can be determined by using the
minindex( pluralAttribute )
function instead - maxelement
-
The collection maximum element can be determined by using the
maxelement( pluralAttribute )
function instead - minelement
-
The collection minimum element can be determined by using the
minelement( pluralAttribute )
function instead
Previously Hibernate did allow comparing an association with an FK value like … where alias.association = 1
or … where alias.association = alias.association.id
or even … where alias.association = :param
where param
is bound to an integer 1
. This was supported prior to Hibernate 6.0 if the foreign key for the association is an integer.
The right way to do this is de-referencing the association by the FK attribute … where alias.association.id = 1
which is guaranteed to not produce a join, or use an entity reference for … where alias.association = :param
where param
is bound to entityManager.getReference(EntityClass.class, 1)
.
-
JMX integration
-
JACC integration
-
@Deprecated features:
-
'hibernate.classLoader.application', 'hibernate.classLoader.resources', 'hibernate.classLoader.hibernate' and 'hibernate.classLoader.environment': use 'hibernate.classLoaders' instead.
-
'hibernate.hbm2dll.create_namespaces': use 'jakarta.persistence.create-database-schemas' or 'hibernate.hbm2ddl.create_namespaces'
-
The method
boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types)
has been removed in favour of
boolean onSave(Object entity, Object id, Object[] state, String[] propertyNames, Type[] types)
We changed the way we detect circularity, we do not follow anymore a deep first detection, so what happens is that in a model like
@Entity
class Node {
@ManyToOne
Node node1;
@ManyToOne
Node node2;
}
being all eager we are executing a query with 4 joins
FROM Node
JOIN Node.node1
JOIN Node.node1.node2
JOIN Node.node2
JOIN Node.node2.node1
whereas before we
FROM Node
JOIN Node.node1
JOIN Node.node1.node2
and issue a select for Node.node2
if the FK of Node.node2
is not null
FROM Node.node2
JOIN Node.node2.node1
JOIN Node.node2.node1.node2
In this simple example this is not such a big deal, but if we increase the number of eager fetched self-associations to e.g. 3 like here:
@Entity
class Node {
@ManyToOne
Node node1;
@ManyToOne
Node node2;
@ManyToOne
Node node3;
}
this results in mind-blowing 15 joins
FROM Node
JOIN Node.node1
JOIN Node.node1.node2
JOIN Node.node1.node2.node3
JOIN Node.node1.node3
JOIN Node.node1.node3.node2
JOIN Node.node2
JOIN Node.node2.node1
JOIN Node.node2.node1.node3
JOIN Node.node2.node3
JOIN Node.node2.node3.node1
JOIN Node.node3
JOIN Node.node3.node1
JOIN Node.node3.node1.node2
JOIN Node.node3.node2
JOIN Node.node3.node2.node1
as you can see, this leads to a lot of joins very quickly, but the behavior of 5.x simply was not intuitive.
To avoid creating so many joins, and also in general, we recommend that you use lazy fetching i.e. @ManyToOne(fetch = FetchType.LAZY)
or @OneToOne(fetch = FetchType.LAZY)
for most associations, but this is especially important if you have multiple self-referencing associations as you can see in the example.
The legacy Hibernate Criteria API which was deprecated back in Hibernate 5.x was removed in 6.0. Usually, all queries using the legacy API can be modeled with the JPA Criteria API. In some cases it is necessary to use the Hibernate JPA Criteria extensions.
The special walkers/visitors in the loader package were removed. This is now all controlled through LoaderSelectBuilder
.
The contents of the loader.collection
package were restructured into loader.ast.spi
and loader.ast.internal
as well as adapted to the SQM API.
The contents of loader.custom
were adapted and moved to query.sql
.
The contents of loader.entity
and loader.plan
were removed as that is now handled through LoaderSelectBuilder
.
The contents of sql.ordering
were adapted and moved to metamodel.mapping.ordering.ast
.
Classes of the sql
package that were previously used for building SQL, but aren’t needed anymore, were removed.
The SQL generation is now fully handled through the SqlAstTranslator
which a Dialect
exposes a factory for.
One of the main changes in Hibernate 6 which ripples through quite a few contracts is the change for reading by position rather than by name from JDBC. We took this as a chance to fix-up some contracts which were named badly and cleanup basic types in general.
Various contracts in org.hibernate.type
and org.hibernate.usertype
were changed to now offer a read-by-position
method. The read-by-name methods were removed.
Almost all BasicType
implementations in org.hibernate.type
were removed because the responsibilities these classes
had were moved to the JdbcType
and JavaType
contracts as well as sub-contracts like AdjustableJdbcType
,
VersionJavaType
and TemporalJavaTypeDescriptor
.
The new implementation for almost all basic types is NamedBasicTypeImpl
which just wraps a JdbcType
and JavaType
along with a name.
The StandardBasicTypes
class previously exposed BasicType
instance fields, which now have been replaced with fields
of the type BasicTypeReference
. APIs that previously accepted just a BasicType
have been adapted to also accept a
BasicTypeReference
which allows for uses of StandardBasicType
fields to stay mostly source compatible.
Previously the package org.hibernate.type.descriptor.java
contained JavaTypeDescriptor
implementations
for various basic types named with a suffix of Type
, JavaType
or JavaTypeDescriptor
.
The JavaTypeDescriptor
interface was renamed to JavaType
and implementations were renamed to have the suffix JavaType
.
Previously the package org.hibernate.type.descriptor.sql
contained SqlTypeDescriptor
implementations
for various basic types named with a suffix of TypeDescriptor
.
The SqlTypeDescriptor
interface was renamed to JdbcType
and implementations were renamed to have the suffix JdbcType
.
The package was also changed from org.hibernate.type.descriptor.sql
to org.hibernate.type.descriptor.jdbc
.
The use of plain HQL identifiers in e.g. functions which couldn’t be interpreted as an attribute of a FROM
root
were passed through as-is to SQL in Hibernate 5.x which was dropped in 6.0 because we believe this is unsafe
and might lead to surprising results. HQL queries that relied on this, need to be changed and use the newly introduced
sql
function, which allows passing through the content of a string literal to SQL.
An HQL query like select substring( e.description, 21, 11, octets ) from AnEntity e
, which relies on this for passing through octets
can be migrated to select substring( e.description, 21, 11, sql('octets') ) from AnEntity e
.