Skip to content

Latest commit

 

History

History
392 lines (265 loc) · 13.4 KB

migration-guide.adoc

File metadata and controls

392 lines (265 loc) · 13.4 KB

6.0 Migration Guide

This guide discusses migration from Hibernate ORM version 6.0. For migration from earlier versions, see any other pertinent migration guides as well.

Background

Known changes

Jakarta Persistence

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.

Reading from JDBC

Read-by-position rather than read-by-name

Type system / custom types

Type changes

  • The default type for Duration was changed to NUMERIC which could lead to schema validation errors

Influencing JdbcTypeDescriptor to use

Discuss JdbcTypeCode, JdbcType, JdbcTypeRegistration

Influencing JavaTypeDescriptor to use

Discuss @JavaType, @JavaTypeRegistration

Component Mapping

Multiple component mappings for the same java class with different property mappings is not supported anymore. Every property mapping combination should have its own java class"

Procedure Parameters, enable passing nulls

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.

Query

Ordinal Parameters binding

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 results

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();

Stream

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.

Iterate

The Query#iterate() method has been removed. The alternative is to use Query#stream() or Query#getResultStream().

Native Query

Native query as a function call is no longer supported.

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();

SQM

  • Functions

  • Multi-table bulk manipulation HQL/Criteria query handling

Remove support for special plural attribute properties

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 ) or key( pluralAttribute ) function instead

index

The collection index can be referred to by using the index( pluralAttribute ) or key( 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

Remove support for comparing association against FK value

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).

Removals

  • 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'

org.hibernate.Interceptors

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)

Fetch behaviour change

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.

Removal of legacy Hibernate Criteria API

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.

Removal of loader walkers

The special walkers/visitors in the loader package were removed. This is now all controlled through LoaderSelectBuilder.

Restructuring of the loader package

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.

Restructuring of the sql package

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.

Changes in the type package

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.

Replace read-by-name with read-by-position

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.

Removal of various BasicType implementations

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.

Renaming of JavaTypeDescriptor contract

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.

Renaming of SqlTypeDescriptor contract

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.

Dropped support for pass-through HQL tokens

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.