692 lines
22 KiB
Plaintext
692 lines
22 KiB
Plaintext
= 6.0 Migration Guide
|
|
:toc:
|
|
:toclevels: 4
|
|
:docsBase: https://docs.jboss.org/hibernate/orm/6.0
|
|
:userGuideBase: {docsBase}/userguide/html_single/Hibernate_User_Guide.html
|
|
:javadocsBase: {docsBase}/javadocs
|
|
:fn-converter: footnote:converter[Think `AttributeConverter`]
|
|
|
|
|
|
|
|
|
|
This guide discusses migration from Hibernate ORM version 6.0. For migration from
|
|
earlier versions, see any other pertinent migration guides as well.
|
|
|
|
|
|
|
|
== 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.
|
|
|
|
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. See the `rules/`
|
|
directory in the project root for the configuration used to migrate Hibernate itself.
|
|
|
|
|
|
== Java 11
|
|
|
|
With 6.0, Hibernate ORM has moved to expect Java 11 as its baseline version
|
|
|
|
|
|
[[read-jdbc]]
|
|
== Reading from JDBC
|
|
|
|
One of the main reasons for 6.0 development was the move from reading results
|
|
from the JDBC `ResultSet` by name (read-by-name) as done in previous versions
|
|
of Hibernate, to reading the results by position (read-by-position).
|
|
|
|
Throughput testing of Hibernate showed that its use of read-by-name was its limiting factor
|
|
in any further scaling in terms of throughput - much of the issue was actually the call into
|
|
the `ResultSet`. We like to improve performance all the time :)
|
|
|
|
This change, along with <<sql>>, helped achieve this goal.
|
|
|
|
As discussed in <<type>> though, this change has a very big impact on Hibernate's mapping type system
|
|
|
|
|
|
[[sql]]
|
|
== Generated SQL
|
|
|
|
1. Column aliases are no longer generated.
|
|
2. Column references are "unique-d".
|
|
3. Better definition of joins
|
|
4. Better determination of unnecessary joins (secondary tables, inheritance tables)
|
|
|
|
|
|
== Bulk SQM against entities mapped to multiple tables
|
|
|
|
// todo (6.0) - @Christian - can you add some info here?
|
|
|
|
|
|
|
|
[[identifier-object]]
|
|
== Identifier as Object
|
|
|
|
Previous versions of Hibernate required that all identifier types implement `Serializable`. 6.0
|
|
removes this restriction - identifiers can be any `Object`.
|
|
|
|
This change affects many api and spi methods previously defined using `Serializable`.
|
|
|
|
|
|
[[id-gen-type]]
|
|
== @IdGeneratorType
|
|
|
|
6.0 adds a new `@IdGeneratorType` annotation that allows better, type-safe way
|
|
to define custom generators to use for identifier generation.
|
|
|
|
// todo (6.0 - @Steve - need to add content about this to the User Guide
|
|
|
|
|
|
[[type]]
|
|
== Type system
|
|
|
|
Another change is to generally modernize Hibernate's mapping annotations and make them
|
|
more type-safe.
|
|
|
|
We decided this is the right time since 6.0 is a major release and most of the type-related
|
|
contracts were already changing to implement the <<read-jdbc,read-by-position>> changes.
|
|
|
|
One part of this work was the removal of various String-based approaches for specifying Types to use from annotations, including
|
|
the removal of `@Type`, `@AnyMetaDef`, `@AnyMetaDefs`, `@MapKeyType`, @TypeDef` and `@TypeDefs`, as well as
|
|
removing annotation attributes accepting the type to use as a String (e.g. `org.hibernate.annotations.CollectionType#type`)
|
|
|
|
The https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#domain-model[User Guide]
|
|
covers the details of mapping your domain model.
|
|
|
|
|
|
[[rename-java-type]]
|
|
=== Renaming of JavaTypeDescriptor contract
|
|
|
|
The interface `org.hibernate.type.descriptor.java.JavaTypeDescriptor` has been renamed to
|
|
`org.hibernate.type.descriptor.java.JavaType`
|
|
|
|
|
|
[[rename-jdbc-type]]
|
|
=== Renaming of SqlTypeDescriptor contract
|
|
|
|
The interface `org.hibernate.type.descriptor.sql.SqlTypeDescriptor` has been renamed to
|
|
`org.hibernate.type.descriptor.jdbc.JdbcType`.
|
|
|
|
|
|
[[basic-type]]
|
|
=== Basic types
|
|
|
|
Basic mappings are no longer configurable through the `BasicType` contract. Instead,
|
|
users configure the different aspects of mapping the basic value to the database -
|
|
|
|
* `JavaType`
|
|
* `JdbcType`
|
|
* `BasicValueConverter` {fn-converter}
|
|
* `MutabilityPlan`
|
|
|
|
This also made the various implementations of `BasicType` obsolete, thus they have been removed.
|
|
`NamedBasicTypeImpl` takes the role of all the previous specific implementations by wrapping a
|
|
`JdbcType` and `JavaType`.
|
|
|
|
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.
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#basic for details.
|
|
|
|
==== UserType
|
|
|
|
`UserType` is still supported, and is specified using the new `Type` annotation.
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-custom for details.
|
|
|
|
|
|
==== Boolean converters
|
|
|
|
Hibernate now provides standard `AttributeConverter` implementations for handling different database representations
|
|
as boolean values in the domain model:
|
|
|
|
`YesNoConverter`:: Handles values stored in the database as either `Y` or `N`. Replaces the removed `YesNoBooleanType` (`yes_no`)
|
|
`TrueFalseConverter`:: Handles values stored in the database as either `T` or `F`. Replaces the removed `TrueFalseBooleanType` (`true_false`)
|
|
`NumericBooleanConverter`:: Handles values stored in the database as either `1` or `0`. Replaces the removed `NumericBooleanType` (`numeric_boolean`)
|
|
|
|
E.g.
|
|
|
|
```
|
|
@Type(type="yes_no")
|
|
boolean isActive;
|
|
```
|
|
|
|
becomes
|
|
|
|
```
|
|
@Convert(converter=YesNoConverter.class)
|
|
boolean isActive;
|
|
```
|
|
|
|
In fact, if your application consistently maps booleans to the same database representation you can
|
|
even register one as an auto-apply converter.
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#basic-boolean for details.
|
|
|
|
|
|
=== Embeddables / components
|
|
|
|
Mapping of embeddables had a few changes as well.
|
|
|
|
|
|
==== Different embeddable mappings
|
|
|
|
Multiple component mappings for the same Java class with different property mappings is no
|
|
longer supported. Every property mapping combination should have its own Java class
|
|
|
|
|
|
==== EmbeddableInstantiator
|
|
|
|
6.0 introduces the new `EmbeddableInstantiator` contract.
|
|
|
|
`EmbeddableInstantiator` supports constructor-injection! Note, however, that embeddables used as
|
|
identifiers cannot use constructor injection.
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#embeddable-instantiator for details.
|
|
|
|
|
|
=== Plural attributes
|
|
|
|
6.0 defines 2 main ways to influence collection mapping `@CollectionType` and `@CollectionTypeRegistration`
|
|
|
|
[[collection-type-ann]]
|
|
==== `@CollectionType`
|
|
|
|
The `@CollectionType` annotation is kept from 5.x. However, where it used to define
|
|
|
|
```
|
|
String type();
|
|
```
|
|
|
|
it now defines
|
|
|
|
```
|
|
Class<? extends UserCollectionType> type();
|
|
```
|
|
|
|
The type to use must be a `UserCollectionType` (can no longer be a `CollectionType`) and
|
|
it no longer works with type-definitions. See <<type>> for further discussion of general type changes.
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#collection-type-ann
|
|
for details of using `@CollectionType`
|
|
|
|
|
|
[[collection-type-reg-ann]]
|
|
==== `@CollectionTypeRegistration`
|
|
|
|
Allows to "auto apply" a `UserCollectionType` whenever Hibernate encounters a particular
|
|
plural attribute classification
|
|
|
|
See https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#collection-type-reg-ann
|
|
for details of using `@CollectionTypeRegistration`
|
|
|
|
|
|
[[collection-list-as-bag]]
|
|
==== List as BAG
|
|
|
|
Historically, Hibernate treated `List` attributes with no explicit ordering details using its
|
|
BAG semantics.
|
|
|
|
```
|
|
@ElementCollection
|
|
List<String> names;
|
|
```
|
|
|
|
|
|
|
|
This is not the most natural decision and is rooted in the original annotation
|
|
support being too closely related to the `hbm.xml` mapping format.
|
|
|
|
Starting in 6.0, Hibernate now makes the more natural interpretation of such a mapping using its
|
|
LIST semantics. It treats the above example with an implicit an `@OrderColumn`. The above mapping
|
|
is now equivalent to
|
|
|
|
```
|
|
@ElementCollection
|
|
@OrderColumn(...)
|
|
List<String> names;
|
|
```
|
|
|
|
A new "compatibility flag" has been added to allow continuing with the legacy behavior:
|
|
|
|
```
|
|
hibernate.mapping.default_list_semantics=bag
|
|
```
|
|
|
|
The same can be achieved for specific attributes using the new `@Bag` annotation as well.
|
|
|
|
|
|
=== Misc
|
|
|
|
* The default type for `Duration` was changed to `NUMERIC` which could lead to schema validation errors
|
|
|
|
|
|
[[query]]
|
|
== Query
|
|
|
|
// todo (6.0) - Query parameter binding overloads accepting `Type`, `BindableType`
|
|
// todo (6.0) - addition of parameter binding overloads accepting Class - AttributeConverter, UserType, Java Type (resolved from JavaTypeRegistry), ...
|
|
|
|
[[query-stream]]
|
|
=== Stream
|
|
|
|
`jakarta.persistence.Query#getResultStream()` and `org.hibernate.query.Query#stream()` no longer
|
|
return a `Stream` decorator. In order to close the underlying IO resources, it is now necessary to
|
|
explicitly call the `Stream#close()` method.
|
|
|
|
This change makes the Streams returned by Hibernate behave as defined in the JDK
|
|
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html[Stream]
|
|
documentation, which is quite explicit about the need for an explicit call to `close` by the user
|
|
to avoid resource leakages.
|
|
|
|
|
|
[[query-iterate]]
|
|
=== Iterate
|
|
|
|
The `Query#iterate()` method has been removed. The alternative is to use one of
|
|
|
|
* `Query#stream()`
|
|
* `Query#getResultStream()`
|
|
* Get the `Iterator` from `List` returned by `Query#list()` / `Query#getResultList()`
|
|
|
|
|
|
[[query-sqm]]
|
|
== SQM (HQL/Criteria)
|
|
|
|
Another major change in 6.0 is the move to a dedicated tree structure to model
|
|
HQL and Criteria queries. This tree structure is called the Semantic Query Model, or
|
|
SQM for short.
|
|
|
|
todo (6.0) - cover functions
|
|
todo (6.0) - cover new temporal capabilities
|
|
todo (6.0) - cover new syntaxes
|
|
todo (6.0) - cover bulk manipulation query handling
|
|
|
|
|
|
[[query-sqm-rows]]
|
|
==== Result "rows"
|
|
|
|
Queries that use joins without specifying a select clause (e.g. `from Person p join p.address`)
|
|
used to return a `List<Object[]>`. Starting with 6.0, such a query instead returns
|
|
`List<Person>`
|
|
|
|
The HQL query `select p, a from Person p join p.address a` returns instead a `List<Object[]>`.
|
|
|
|
```
|
|
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();
|
|
```
|
|
|
|
|
|
[[query-sqm-pass-thru]]
|
|
==== Pass-through 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`.
|
|
|
|
|
|
[[query-sqm-distinct]]
|
|
==== DISTINCT
|
|
|
|
Starting with Hibernate ORM 6 it is no longer necessary to use *distinct* in JPQL and HQL
|
|
to filter out the same parent entity references when join fetching a child collection.
|
|
The returning duplicates of entities are now always filtered by Hibernate.
|
|
|
|
Which means that for instance it is no longer necessary to set `QueryHints#HINT_PASS_DISTINCT_THROUGH` to `false`
|
|
in order to skip the entity duplicates without producing a `distinct` in the SQL query.
|
|
|
|
From Hibernate ORM 6, `distinct` is always passed to the SQL query and the flag `QueryHints#HINT_PASS_DISTINCT_THROUGH`
|
|
has been removed.
|
|
|
|
|
|
==== Association Comparisons
|
|
|
|
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)`.
|
|
|
|
|
|
|
|
[[query-sqm-psuedo-attr]]
|
|
==== Collection psuedo-attributes
|
|
|
|
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
|
|
|
|
|
|
|
|
[[query-native]]
|
|
== NativeQuery
|
|
|
|
As `NativeQuery` extends from `Query`, all the changes listed in <<query>> also apply
|
|
to `NativeQuery`.
|
|
|
|
Some additional changes apply specifically to `NativeQuery`
|
|
|
|
|
|
[[query-ordinal-param]]
|
|
=== 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 ) );
|
|
```
|
|
|
|
[[proc-call-nativequery]]
|
|
=== Callable via NativeQuery
|
|
|
|
Using `NativeQuery` to call SQL functions and procedures is no longer
|
|
supported. `org.hibernate.procedure.ProcedureCall` or
|
|
`jakarta.persistence.StoredProcedureQuery` should be used instead.
|
|
|
|
`@NamedNativeQuery` references defining execution of procedure or
|
|
functions should be migrated to use `@NamedStoredProcedureQuery`
|
|
instead.
|
|
|
|
E.g., the following `@NamedNativeQuery` -
|
|
|
|
```
|
|
@NamedNativeQuery(
|
|
name = "personAndPhones",
|
|
query = "{ ? = call fn_person_and_phones( ? ) }",
|
|
callable = true,
|
|
resultSetMapping = "personWithPhonesResultMapping"
|
|
)
|
|
|
|
...
|
|
|
|
final List<Object[]> personAndPhones = entityManager
|
|
.createNamedQuery("personAndPhones" )
|
|
.setParameter( 1, 1L )
|
|
.getResultList();
|
|
```
|
|
|
|
should be changed to use `@NamedStoredProcedureQuery` instead -
|
|
|
|
```
|
|
@NamedStoredProcedureQuery(
|
|
name = "personAndPhones",
|
|
procedureName = "fn_person_and_phones",
|
|
resultSetMapping = "personWithPhonesResultMapping",
|
|
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
|
|
parameters = @StoredProcedureParameter(type = Long.class)
|
|
)
|
|
```
|
|
|
|
Either `org.hibernate.procedure.ProcedureCall` or `jakarta.persistence.StoredProcedureQuery`
|
|
can be used to execute the named query -
|
|
|
|
```
|
|
// Use StoredProcedureQuery
|
|
final List<Object[]> personAndPhones = entityManager
|
|
.createNamedStoredProcedureQuery( "personAndPhones" )
|
|
.setParameter( 1, 1L )
|
|
.getResultList();
|
|
|
|
// Use ProcedureCall
|
|
final List<Object[]> personAndPhones = entityManager
|
|
.unwrap( Session.class )
|
|
.getNamedProcedureCall( "personAndPhones" )
|
|
.setParameter( 1, 1L )
|
|
.getResultList();
|
|
```
|
|
|
|
|
|
It is also no longer supported to execute procedures and functions
|
|
via a dynamic (unnamed) `NativeQuery`. All such usages should be converted
|
|
to use `ProcedureCall` or `StoredProcedureQuery` instead via
|
|
`Session#createStoredProcedureCall` or `EntityManager#createStoredProcedureQuery`,
|
|
respectively.
|
|
|
|
```
|
|
// Use StoredProcedureQuery
|
|
final List<Object[]> personAndPhones = entityManager
|
|
.createStoredProcedureQuery( "fn_person_and_phones", "personWithPhonesResultMapping" )
|
|
.setParameter( 1, 1L )
|
|
.getResultList();
|
|
|
|
// Use ProcedureCall
|
|
final List<Object[]> personAndPhones = entityManager
|
|
.unwrap( Session.class )
|
|
.createStoredProcedureCall( "fn_person_and_phones", "personWithPhonesResultMapping" )
|
|
.setParameter( 1, 1L )
|
|
.getResultList();
|
|
```
|
|
|
|
|
|
[[proc-call-param]]
|
|
== ProcedureCall / StoredProcedureQuery Parameters
|
|
|
|
For parameters defined on a ProcedureCall as accepting binding (IN and INOUT), a distinction is now
|
|
made between whether `setParameter` is called or not. If `setParameter` was called, whatever value
|
|
was set by the user is passed to the database. If it was not called, Hibernate will not
|
|
set any value which triggers the default value defined on the database procedure argument be used
|
|
|
|
|
|
== Interceptor
|
|
|
|
The signature of the `#onSave` method has been changed from
|
|
```
|
|
boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types)
|
|
```
|
|
|
|
to
|
|
|
|
```
|
|
boolean onSave(Object entity, Object id, Object[] state, String[] propertyNames, Type[] types)
|
|
```
|
|
|
|
to account for the general change in expected identifier type from `Serializable` to `Object`.
|
|
See <<identifier-object>>.
|
|
|
|
If custom Interceptor implementations do not use `@Override` on their implementations, this
|
|
can lead to situations where a custom Interceptor no longer overrides this method. Moral
|
|
of the story... always use `@Override` - this is why it exists
|
|
|
|
|
|
== Removals
|
|
|
|
|
|
=== Legacy Hibernate Criteria API
|
|
|
|
The legacy Hibernate Criteria API which was deprecated back in Hibernate 5.x and 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.
|
|
|
|
=== JMX integration
|
|
|
|
Hibernate no longer provides built-in support for integrating itself with JMX environments.
|
|
|
|
=== JACC integration
|
|
|
|
Hibernate no longer provides built-in support for integrating itself with JACC environments.
|
|
|
|
|
|
=== Previously 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'
|
|
|
|
// todo (6.0) - surely there are more than this...
|
|
|
|
|
|
== Width-first fetch determination
|
|
|
|
Previous versions of Hibernate determined fetches using a depth-first approach, which occasionally led
|
|
to odd "circularity" determination. Starting with 6.0, we now perform fetch determination using a width
|
|
first approach.
|
|
|
|
As back-ground, Hibernate does not always know that a fetch is truly
|
|
circular. So it uses the approach that seeing the same table and column(s) as keys might be a circularity
|
|
and stops processing fetches using that table/column(s) combination.
|
|
|
|
Given a model such as
|
|
|
|
```
|
|
@Entity
|
|
class Node {
|
|
|
|
@ManyToOne
|
|
|
|
Node node1;
|
|
|
|
@ManyToOne
|
|
Node node2;
|
|
|
|
}
|
|
```
|
|
|
|
Hibernate previously walked the graph for the `Node#node1` sub-tree prior to walking the `Node#node2` sub-tree
|
|
|
|
// todo (6.0) : clarify this some more?
|
|
|
|
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.
|
|
|
|
|
|
== Restructuring of `org.hibernate.loader`
|
|
|
|
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
|
|
|
|
|
|
== 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.
|
|
|
|
|
|
== Deprecation of hbm.xml mappings
|
|
|
|
Legacy `hbm.xml` mapping format is considered deprecated and will no longer supported beyond 6.x.
|
|
|
|
|