374 lines
13 KiB
Plaintext
374 lines
13 KiB
Plaintext
= 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<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`.
|
|
|
|
The migration code is
|
|
```
|
|
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`.
|