= 6.0 Migration Guide :toc: 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 https://github.com/eclipse/transformer[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. // todo (6.0) : reference to `${root}/rules ? 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` 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`. e.g. ``` @Entity class Person { ... @ManyToOne Address address ... } @Entity class Address { ... } List result = session.createQuery("from Person p join p.address").list(); List 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 postAndComments = entityManager.createNamedQuery("fn_person_and_phones" ).setParameter( 1, 1L ).getResultList(); ``` is going to throw an `IllegalArgumentException`. The migration code is ``` List 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`.