work on migration guide and 6.0 announcement
This commit is contained in:
parent
8b9c68b8db
commit
cb4691e98e
|
@ -1,17 +1,20 @@
|
|||
= Hibernate 6.0 is Coming
|
||||
= Hibernate 6.0 Final
|
||||
Steve Ebersole
|
||||
:awestruct-tags: ["Hibernate ORM"]
|
||||
:awestruct-layout: blog-post
|
||||
:docs-url: https://docs.jboss.org/hibernate/orm/6.0
|
||||
:javadocs-url: {docs-url}/javadocs
|
||||
:migration-guide-url: {docs-url}/migration-guide/migration-guide.html
|
||||
:user-guide-url: {docs-url}/userguide/html_single/Hibernate_User_Guide.html
|
||||
:jakarta-transformer-url: https://github.com/eclipse/transformer
|
||||
|
||||
As Hibernate ORM 6.0 approaches Final, I wanted to take a moment to look back at its origins and
|
||||
driving forces; and to give insight into why certain choices were made.
|
||||
It has been years in the making, but ORM 6.0 Final has finally been released!
|
||||
|
||||
This post will be followed by a series of more focused posts targeting specific improvements or
|
||||
cool new features.
|
||||
This announcement will discuss the major changes as well as give insight into why
|
||||
certain choices were made.
|
||||
|
||||
We will also be following up with a series of more focused posts targeting specific
|
||||
improvements or cool new features. Stay tuned!
|
||||
|
||||
|
||||
[[api-spi]]
|
||||
|
@ -26,14 +29,17 @@ See https://hibernate.org/community/compatibility-policy/ for a discussion of wh
|
|||
an API versus an SPI.
|
||||
====
|
||||
|
||||
Applications which use only the Jakarta Persistence APIs will be "source compatible" within the
|
||||
Applications which use only the Jakarta Persistence APIs will be source compatible within the
|
||||
discussion in <<jpa>>.
|
||||
|
||||
Applications using Hibernate APIs will generally be bytecode and source compatible, aside
|
||||
from the removal of deprecated stuff. There are a few one-off changes that break bytecode and/or
|
||||
source compatibility; these are covered in the link:{migration-guide-url}[migration guide].
|
||||
|
||||
Quite a few SPI changes have changed to support many of the topics discussed here as well as in
|
||||
One specific change to note is that many of these contracts have been better defined with type
|
||||
parameters. Theses where inconsistently and sometimes poorly defined in previous versions.
|
||||
|
||||
Quite a few SPI contracts have changed to support many of the topics discussed here as well as in
|
||||
the link:{migration-guide-url}[migration guide]. Many will also be the subject of the mentioned
|
||||
follow-up posts.
|
||||
|
||||
|
@ -83,7 +89,8 @@ which were later used to access the specific result. We've all seen these "ugly
|
|||
changes, those select-clause aliases are no longer needed resulting in much more readable generated
|
||||
SQL.
|
||||
3. Although we implemented some improved support for limiting needed joins within an entity mapping
|
||||
(joined inheritance, secondary tables) in 5.x, 6.0 allows even better opportunity for this.
|
||||
(joined inheritance, secondary tables) in 5.x, 6.0 allows even better opportunity for this. In
|
||||
fact, the support for this in 5.x was conceptually back-ported from the 6.0 work.
|
||||
4. (2) and (3) combined results in much smaller SQL needing to be sent to the server which can
|
||||
have an impact on network communication. Every bit helps.
|
||||
|
||||
|
@ -94,9 +101,22 @@ This was by far the biggest force behind 6.0 initially.
|
|||
[[mapping-model]]
|
||||
== Mapping Model
|
||||
|
||||
- Object-oriented
|
||||
- More user-friendly
|
||||
- Attribute order
|
||||
The mapping model is an SPI and as such will not be seen by all users. But
|
||||
it is a major development and impacts many users providing extensions.
|
||||
|
||||
The main driving force behind this mapping model work was <<read-by-position>>,
|
||||
and we had a number of design goals in developing it:
|
||||
|
||||
- support positional processing of attributes
|
||||
- make it object-oriented
|
||||
- make it user friendly
|
||||
|
||||
This model can be accessed though
|
||||
link:{javadocs-url}/org/hibernate/engine/spi/SessionFactoryImplementor.html#getRuntimeMetamodels()[`RuntimeMetamodelsImplementor`]
|
||||
which provides access to both:
|
||||
|
||||
- The Jakarta Persistence model : link:{javadocs-url}/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.html#getJpaMetamodel()[`JpaMetamodelImplementor`]
|
||||
- Hibernate's mapping model : link:{javadocs-url}/org/hibernate/metamodel/spi/RuntimeMetamodelsImplementor.html#getMappingMetamodel()[`MappingMetamodelImplementor`]
|
||||
|
||||
|
||||
[[annotations]]
|
||||
|
@ -118,21 +138,34 @@ This was by far the biggest force behind 6.0 initially.
|
|||
[[sqm]]
|
||||
== Semantic Query Model
|
||||
|
||||
- HQL and Criteria
|
||||
- selection of a single entity - de-dup
|
||||
- set operations (union, intersect, except)
|
||||
- set aggregations
|
||||
- window functions
|
||||
- Functions
|
||||
- ILIKE support
|
||||
- improved temporal support (arithmetic, etc)
|
||||
Hibernate's Semantic Query Model (SQM) is its semantic representation of HQL
|
||||
and Criteria queries. HQL is interpreted into SQM; Hibernate's Criteria
|
||||
implementations are SQM nodes.
|
||||
|
||||
6.0 implements quite a few changes shared between HQL and Criteria. Most of
|
||||
these are covered in link:{user-guide-url}#query-language[HQL] and
|
||||
link:{user-guide-url}#criteria[Criteria] chapters of the User Guide.
|
||||
|
||||
Some specific changes include
|
||||
|
||||
- Automatic de-duplication of single entity results in a Query. See the link:{migration-guide-url}#query-sqm-rows[Migration Guide] for details
|
||||
- Set operations (union, intersect, except)
|
||||
- Set aggregations (listagg, e.g.)
|
||||
- Window operations (over, e.g.)
|
||||
- Vastly improved function support. See the link:{user-guide-url}#hql-exp-functions[User Guide] for details.
|
||||
- ILIKE operator
|
||||
- Improved temporal support (arithmetic, etc)
|
||||
|
||||
|
||||
[[hql]]
|
||||
== HQL
|
||||
|
||||
- poor and unmaintainable grammars
|
||||
- Antlr 2 -> Antlr 4
|
||||
Previous versions of Hibernate used Antlr 2 for parsing. 6.0 updates to Antlr 4 for a few reasons:
|
||||
|
||||
- Antlr 2 is no longer supported, and has not for years
|
||||
- Antlr 4 is faster than Antlr 2
|
||||
- Antlr 4 grammars are easier to maintain, while the previous Antlr 2 grammars were poorly defined (largely as a function of Antlr 2 itself) and difficult to maintain.
|
||||
|
||||
|
||||
|
||||
[[criteria]]
|
||||
|
|
|
@ -60,31 +60,6 @@ As discussed in <<type>> though, this change has a very big impact on Hibernate'
|
|||
3. Better definition of joins
|
||||
4. Better determination of unnecessary joins (secondary tables, inheritance tables)
|
||||
|
||||
|
||||
== Bulk SQM against entities mapped to multiple tables
|
||||
|
||||
The implementations for bulk SQM DML statements like `insert`, `update` and `delete` were significantly improved in 6.0.
|
||||
An important bug fix is, that `delete` statements now properly clean up collection tables.
|
||||
`insert` statements now also support inserting into multi-table entities by making use of special purpose temporary tables
|
||||
into which the insert goes and is then split up into the respective tables.
|
||||
|
||||
There are currently 2 implementation strategies:
|
||||
|
||||
* Using temporary tables (the default)
|
||||
* Using DML in CTEs (used on DB2 and PostgreSQL)
|
||||
|
||||
The temporary table approach is pretty simple and works in a similar way to how 5.x already implemented it.
|
||||
Data or primary key values are first inserted into a temporary table and then the DML changes are applied to the various
|
||||
tables that are affected by the SQM DML statement.
|
||||
|
||||
The CTE approach is new and implements a more performant approach by executing a single statement,
|
||||
containing the various individual DML statements that would normally be executed separately.
|
||||
This allows to run SQM DML statements in a single JDBC operation that does not move any data between the database and the application,
|
||||
which should provide a significant boost for statements that involve many rows.
|
||||
|
||||
Note that the configuration property `hibernate.hql.bulk_id_strategy` was changed to `hibernate.query.mutation_strategy`
|
||||
which will now refer to classes or objects implementing `org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy`.
|
||||
|
||||
[[identifier-object]]
|
||||
== Identifier as Object
|
||||
|
||||
|
@ -322,105 +297,16 @@ To retain backwards compatibility, configure the setting `hibernate.type.preferr
|
|||
[[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), ...
|
||||
Quite a few changes have been made to how Query works.
|
||||
|
||||
[[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-result-cache]]
|
||||
== Query result cache
|
||||
|
||||
Another change in 6.0 is related to the query result cache.
|
||||
|
||||
In previous versions, when the query-cache is enabled and a query returning entities is executed, only the entity identifiers were stored in the query-cache. If second-level caching is enabled for a returned entity, the entity data was stored in its second-level cache region.
|
||||
|
||||
Storing just the identifiers in the query-cache has a major drawback when fetching is defined for the query (dynamic fetch, entity-graph, etc) as it can, and often does, lead to N+1 selects.
|
||||
|
||||
Starting in 6.0, we now store the complete set of data for the entity into the query-cache. This also can have a drawback related to the size of caches. We plan to address this further in later 6.x releases to allow storing just the identifiers along the lines of the previous behavior.
|
||||
|
||||
E.g.
|
||||
|
||||
```
|
||||
Statistics stats = sessionFacroty.getStatistics();
|
||||
|
||||
// First time the query is executed, query and results are cached and both the query and entity chache will be populated.
|
||||
|
||||
TypedQuery<Employee> query = session.createQuery( "select e from Employee e", Employee.class )
|
||||
.setHint( HINT_CACHEABLE, true );
|
||||
|
||||
List<Employee> employees = query.getResultList();
|
||||
assertEquals( 1, employees.size() );
|
||||
|
||||
assertEquals( 0, stats.getQueryCacheHitCount() );
|
||||
assertEquals( 1, stats.getQueryCacheMissCount() );
|
||||
assertEquals( 1, stats.getQueryCachePutCount() ); // query cache is populated.
|
||||
|
||||
assertEquals( 0, stats.getSecondLevelCacheHitCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 1, stats.getSecondLevelCachePutCount() ); // entity cache is populated as well.
|
||||
|
||||
stats.clear();
|
||||
|
||||
// Second time the same query is executed only the query cache will be hit.
|
||||
|
||||
TypedQuery<Employee> query = session.createQuery( "select e from Employee e", Employee.class )
|
||||
.setHint( HINT_CACHEABLE, true );
|
||||
List<Employee> employees = query.getResultList();
|
||||
assertEquals( 1, employees.size() );
|
||||
|
||||
assertEquals( 1, stats.getQueryCacheHitCount() ); // the query cache is hit.
|
||||
assertEquals( 0, stats.getQueryCacheMissCount() );
|
||||
assertEquals( 0, stats.getQueryCachePutCount() );
|
||||
|
||||
assertEquals( 0, stats.getSecondLevelCacheHitCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCachePutCount() ); // No need to hit the entity cache because the query cache contains all the entity data.
|
||||
|
||||
```
|
||||
|
||||
[[query-sqm]]
|
||||
== SQM (HQL/Criteria)
|
||||
=== 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-criteria-copy]]
|
||||
== Hibernate Criteria behavior change
|
||||
|
||||
By default, when bootstrapping Hibernate through the native bootstrap APIs or when explicitly disabling the newly introduced
|
||||
`hibernate.criteria.copy_tree` configuration property, it is expected that criteria queries passed to
|
||||
`jakarta.persistence.EntityManager#createQuery(CriteriaQuery)`, `jakarta.persistence.EntityManager#createQuery(CriteriaUpdate)`
|
||||
or `jakarta.persistence.EntityManager#createQuery(CriteriaDelete)` are not mutated afterwards to avoid the need for copying the criteria query.
|
||||
|
||||
Prior to 6.0, mutations to criteria queries didn't affect `Query` instances created from that.
|
||||
To retain backwards compatibility, enable the `hibernate.criteria.copy_tree` configuration property.
|
||||
|
||||
[[query-sqm-rows]]
|
||||
==== Result "rows"
|
||||
|
@ -433,10 +319,48 @@ The HQL query `select p, a from Person p join p.address a` returns instead a `Li
|
|||
|
||||
```
|
||||
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();
|
||||
List<Object[]> results
|
||||
```
|
||||
|
||||
|
||||
==== Multi-table Mutation Queries
|
||||
|
||||
The implementations for bulk SQM DML statements like `insert`, `update` and `delete` were significantly improved in 6.0.
|
||||
An important bug fix is, that `delete` statements now properly clean up collection tables.
|
||||
`insert` statements now also support inserting into multi-table entities by making use of special purpose temporary tables
|
||||
into which the insert goes and is then split up into the respective tables.
|
||||
|
||||
There are currently 2 implementation strategies:
|
||||
|
||||
* Using temporary tables (the default)
|
||||
* Using DML in CTEs (used on DB2 and PostgreSQL)
|
||||
|
||||
The temporary table approach is pretty simple and works in a similar way to how 5.x already implemented it.
|
||||
Data or primary key values are first inserted into a temporary table and then the DML changes are applied to the various
|
||||
tables that are affected by the SQM DML statement.
|
||||
|
||||
The CTE approach is new and implements a more performant approach by executing a single statement,
|
||||
containing the various individual DML statements that would normally be executed separately.
|
||||
This allows to run SQM DML statements in a single JDBC operation that does not move any data between the database and the application,
|
||||
which should provide a significant boost for statements that involve many rows.
|
||||
|
||||
Note that the configuration property `hibernate.hql.bulk_id_strategy` was changed to `hibernate.query.mutation_strategy`
|
||||
which will now refer to classes or objects implementing `org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy`.
|
||||
|
||||
|
||||
|
||||
[[query-criteria-copy]]
|
||||
==== Hibernate Criteria behavior change
|
||||
|
||||
By default, when bootstrapping Hibernate through the native bootstrap APIs or when explicitly disabling the newly introduced
|
||||
`hibernate.criteria.copy_tree` configuration property, it is expected that criteria queries passed to
|
||||
`jakarta.persistence.EntityManager#createQuery(CriteriaQuery)`, `jakarta.persistence.EntityManager#createQuery(CriteriaUpdate)`
|
||||
or `jakarta.persistence.EntityManager#createQuery(CriteriaDelete)` are not mutated afterwards to avoid the need for copying the criteria query.
|
||||
|
||||
Prior to 6.0, mutations to criteria queries didn't affect `Query` instances created from that.
|
||||
To retain backwards compatibility, enable the `hibernate.criteria.copy_tree` configuration property.
|
||||
|
||||
|
||||
[[query-sqm-pass-thru]]
|
||||
==== Pass-through tokens
|
||||
|
||||
|
@ -508,7 +432,7 @@ The collection minimum element can be determined by using the `minelement( plura
|
|||
|
||||
|
||||
[[query-native]]
|
||||
== NativeQuery
|
||||
=== NativeQuery
|
||||
|
||||
As `NativeQuery` extends from `Query`, all the changes listed in <<query>> also apply
|
||||
to `NativeQuery`.
|
||||
|
@ -533,132 +457,79 @@ 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",
|
||||
resultSetMappings = "personWithPhonesResultMapping",
|
||||
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
|
||||
parameters = @StoredProcedureParameter(type = Long.class)
|
||||
)
|
||||
```
|
||||
|
||||
Callable named native queries in hbm.xml files should be migrated to the orm.xml version.
|
||||
|
||||
E.g., the following `<sql-query callable="true">` -
|
||||
|
||||
```
|
||||
<sql-query name="simpleScalar" callable="true">
|
||||
<return-scalar column="name" type="string"/>
|
||||
<return-scalar column="`value`" type="long"/>
|
||||
{ ? = call simpleScalar(:number) }
|
||||
</sql-query>
|
||||
|
||||
...
|
||||
|
||||
final List<Object[]> results = entityManager
|
||||
.createNamedQuery("simpleScalar" )
|
||||
.setParameter( 1, 1L )
|
||||
.getResultList();
|
||||
```
|
||||
|
||||
should be changed to use `<named-stored-procedure-query/>` instead -
|
||||
|
||||
```xml
|
||||
<named-stored-procedure-query name="simpleScalar" procedure-name="simpleScalar">
|
||||
<parameter class="java.lang.Integer" mode="IN" name="number"/>
|
||||
<result-set-mapping>simpleScalar</result-set-mapping>
|
||||
<hint name="org.hibernate.callableFunction" value="true"/>
|
||||
</named-stored-procedure-query>
|
||||
<sql-result-set-mapping name="simpleScalar">
|
||||
<column-result name="name" class="java.lang.String"/>
|
||||
<column-result name="value" class="java.lang.Long"/>
|
||||
</sql-result-set-mapping>
|
||||
```
|
||||
|
||||
TIP: To ease the migration, `<sql-query callable="true"/>` and `@NamedNativeQuery(callable = true)` queries
|
||||
will be translated and registered as named stored procedure in 6.0, but future versions will drop this automatic translation.
|
||||
|
||||
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( "simpleScalar" )
|
||||
.setParameter( 1, 1L )
|
||||
.getResultList();
|
||||
|
||||
// Use ProcedureCall
|
||||
final List<Object[]> personAndPhones = entityManager
|
||||
.unwrap( Session.class )
|
||||
.getNamedProcedureCall( "simpleScalar" )
|
||||
.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
|
||||
=== 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
|
||||
|
||||
[[query-result-cache]]
|
||||
=== Query result cache
|
||||
|
||||
Another change in 6.0 is related to the query result cache.
|
||||
|
||||
In previous versions, when the query-cache is enabled and a query returning entities is executed, only the entity identifiers were stored in the query-cache. If second-level caching is enabled for a returned entity, the entity data was stored in its second-level cache region.
|
||||
|
||||
Storing just the identifiers in the query-cache has a major drawback when fetching is defined for the query (dynamic fetch, entity-graph, etc) as it can, and often does, lead to N+1 selects.
|
||||
|
||||
Starting in 6.0, we now store the complete set of data for the entity into the query-cache. This also can have a drawback related to the size of caches. We plan to address this further in later 6.x releases to allow storing just the identifiers along the lines of the previous behavior.
|
||||
|
||||
E.g.
|
||||
|
||||
```
|
||||
Statistics stats = sessionFacroty.getStatistics();
|
||||
|
||||
// First time the query is executed, query and results are cached and both the query and entity chache will be populated.
|
||||
|
||||
TypedQuery<Employee> query = session.createQuery( "select e from Employee e", Employee.class )
|
||||
.setHint( HINT_CACHEABLE, true );
|
||||
|
||||
List<Employee> employees = query.getResultList();
|
||||
assertEquals( 1, employees.size() );
|
||||
|
||||
assertEquals( 0, stats.getQueryCacheHitCount() );
|
||||
assertEquals( 1, stats.getQueryCacheMissCount() );
|
||||
assertEquals( 1, stats.getQueryCachePutCount() ); // query cache is populated.
|
||||
|
||||
assertEquals( 0, stats.getSecondLevelCacheHitCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 1, stats.getSecondLevelCachePutCount() ); // entity cache is populated as well.
|
||||
|
||||
stats.clear();
|
||||
|
||||
// Second time the same query is executed only the query cache will be hit.
|
||||
|
||||
TypedQuery<Employee> query = session.createQuery( "select e from Employee e", Employee.class )
|
||||
.setHint( HINT_CACHEABLE, true );
|
||||
List<Employee> employees = query.getResultList();
|
||||
assertEquals( 1, employees.size() );
|
||||
|
||||
assertEquals( 1, stats.getQueryCacheHitCount() ); // the query cache is hit.
|
||||
assertEquals( 0, stats.getQueryCacheMissCount() );
|
||||
assertEquals( 0, stats.getQueryCachePutCount() );
|
||||
|
||||
assertEquals( 0, stats.getSecondLevelCacheHitCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 0, stats.getSecondLevelCachePutCount() ); // No need to hit the entity cache because the query cache contains all the entity data.
|
||||
|
||||
```
|
||||
|
||||
|
||||
[[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.
|
||||
|
||||
|
||||
== Interceptor
|
||||
|
||||
|
@ -681,47 +552,6 @@ can lead to situations where a custom Interceptor no longer overrides this metho
|
|||
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.
|
||||
|
||||
=== HQL fetch all properties clause
|
||||
|
||||
The `fetch all properties` clause was removed from the HQL language without a replacement.
|
||||
A similar behavior can be achieved by constructing an entity graph and applying that as load graph:
|
||||
|
||||
```java
|
||||
EntityGraph<Document> entityGraph = entityManager.createEntityGraph( Document.class );
|
||||
for ( Attribute<Document, ?> attr : entityManager.getMetamodel().entity( Document.class ).getAttributes() ) {
|
||||
entityGraph.addAttributeNodes( attr.getName() );
|
||||
}
|
||||
List<Document> documents = s.createQuery( "from Document", Document.class )
|
||||
.setHint( "jakarta.persistence.loadgraph", entityGraph )
|
||||
.getResultList();
|
||||
```
|
||||
|
||||
=== 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...
|
||||
|
||||
|
||||
== Fetch circularity determination
|
||||
|
||||
As back-ground, Hibernate does understand whether a fetch is actually, truly circular. It simply
|
||||
|
@ -880,6 +710,10 @@ Consider the following example:
|
|||
Prior to 6.0, a query would return a list of tuples [`Organization`, `Employee`],
|
||||
but now this will return a list of `Organization` with an initialized `employments` collection.
|
||||
|
||||
== Removals
|
||||
|
||||
The following features have been removed
|
||||
|
||||
== hbm.xml multiple <column/> now disallowed
|
||||
|
||||
In 6.0 the support for basic property mappings with multiple columns was removed. The only use case for that was when a
|
||||
|
@ -906,3 +740,168 @@ have to be migrated to proper components:
|
|||
```
|
||||
|
||||
The component class attribute now supports interpreting a `CompositeUserType` class properly.
|
||||
|
||||
=== 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.
|
||||
|
||||
[[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()`
|
||||
|
||||
|
||||
[[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",
|
||||
resultSetMappings = "personWithPhonesResultMapping",
|
||||
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
|
||||
parameters = @StoredProcedureParameter(type = Long.class)
|
||||
)
|
||||
```
|
||||
|
||||
Callable named native queries in hbm.xml files should be migrated to the orm.xml version.
|
||||
|
||||
E.g., the following `<sql-query callable="true">` -
|
||||
|
||||
```
|
||||
<sql-query name="simpleScalar" callable="true">
|
||||
<return-scalar column="name" type="string"/>
|
||||
<return-scalar column="`value`" type="long"/>
|
||||
{ ? = call simpleScalar(:number) }
|
||||
</sql-query>
|
||||
|
||||
...
|
||||
|
||||
final List<Object[]> results = entityManager
|
||||
.createNamedQuery("simpleScalar" )
|
||||
.setParameter( 1, 1L )
|
||||
.getResultList();
|
||||
```
|
||||
|
||||
should be changed to use `<named-stored-procedure-query/>` instead -
|
||||
|
||||
```xml
|
||||
<named-stored-procedure-query name="simpleScalar" procedure-name="simpleScalar">
|
||||
<parameter class="java.lang.Integer" mode="IN" name="number"/>
|
||||
<result-set-mapping>simpleScalar</result-set-mapping>
|
||||
<hint name="org.hibernate.callableFunction" value="true"/>
|
||||
</named-stored-procedure-query>
|
||||
<sql-result-set-mapping name="simpleScalar">
|
||||
<column-result name="name" class="java.lang.String"/>
|
||||
<column-result name="value" class="java.lang.Long"/>
|
||||
</sql-result-set-mapping>
|
||||
```
|
||||
|
||||
TIP: To ease the migration, `<sql-query callable="true"/>` and `@NamedNativeQuery(callable = true)` queries
|
||||
will be translated and registered as named stored procedure in 6.0, but future versions will drop this automatic translation.
|
||||
|
||||
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( "simpleScalar" )
|
||||
.setParameter( 1, 1L )
|
||||
.getResultList();
|
||||
|
||||
// Use ProcedureCall
|
||||
final List<Object[]> personAndPhones = entityManager
|
||||
.unwrap( Session.class )
|
||||
.getNamedProcedureCall( "simpleScalar" )
|
||||
.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();
|
||||
```
|
||||
|
||||
|
||||
=== HQL fetch all properties clause
|
||||
|
||||
The `fetch all properties` clause was removed from the HQL language without a replacement.
|
||||
A similar behavior can be achieved by constructing an entity graph and applying that as load graph:
|
||||
|
||||
```java
|
||||
EntityGraph<Document> entityGraph = entityManager.createEntityGraph( Document.class );
|
||||
for ( Attribute<Document, ?> attr : entityManager.getMetamodel().entity( Document.class ).getAttributes() ) {
|
||||
entityGraph.addAttributeNodes( attr.getName() );
|
||||
}
|
||||
List<Document> documents = s.createQuery( "from Document", Document.class )
|
||||
.setHint( "jakarta.persistence.loadgraph", entityGraph )
|
||||
.getResultList();
|
||||
```
|
||||
|
||||
=== 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...
|
||||
|
|
Loading…
Reference in New Issue