Revise native Hibernate 5 bootstrapping with JTA transaction manager

Closes gh-25858
This commit is contained in:
Juergen Hoeller 2020-10-06 15:31:19 +02:00
parent 2533ba5253
commit e417318915
2 changed files with 91 additions and 47 deletions

View File

@ -225,6 +225,8 @@ public class LocalSessionFactoryBuilder extends Configuration {
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName()); "Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
} }
getProperties().put(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta");
// Hibernate 5.1/5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default) // Hibernate 5.1/5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default)
try { try {
// Try Hibernate 5.2 // Try Hibernate 5.2

View File

@ -381,6 +381,19 @@ NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>
from the `jee` namespace. For more information see from the `jee` namespace. For more information see
<<integration.adoc#xsd-schemas-jee, The JEE Schema>>. <<integration.adoc#xsd-schemas-jee, The JEE Schema>>.
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
technology. This is due to the fact that JTA transactions are global transactions, which
can enlist any transactional resource.
In all Spring transaction setups, application code does not need to change. You can change
how transactions are managed merely by changing configuration, even if that change means
moving from local to global transactions or vice versa.
[[transaction-strategies-hibernate]]
==== Hibernate Transaction Setup
You can also easily use Hibernate local transactions, as shown in the following examples. You can also easily use Hibernate local transactions, as shown in the following examples.
In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your
application code can use to obtain Hibernate `Session` instances. application code can use to obtain Hibernate `Session` instances.
@ -420,21 +433,52 @@ example declares `sessionFactory` and `txManager` beans:
If you use Hibernate and Java EE container-managed JTA transactions, you should use the If you use Hibernate and Java EE container-managed JTA transactions, you should use the
same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following
example shows: example shows. Also, it is recommended to make Hibernate aware of JTA through its
transaction coordinator and possibly also its connection release mode configuration:
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.transaction.coordinator_class=jta
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
---- ----
NOTE: If you use JTA, your transaction manager definition should look the same, regardless Or alternatively, you may pass the `JtaTransactionManager` into your `LocalSessionFactoryBean`
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported for enforcing the same defaults:
technology. This is due to the fact that JTA transactions are global transactions, which
can enlist any transactional resource.
In all these cases, application code does not need to change. You can change how [source,xml,indent=0,subs="verbatim,quotes"]
transactions are managed merely by changing configuration, even if that change means ----
moving from local to global transactions or vice versa. <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
<property name="jtaTransactionManager" ref="txManager"/>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
@ -3115,8 +3159,8 @@ hierarchy defined in the `org.springframework.dao` package. (See <<dao-exception
When you use the `JdbcTemplate` for your code, you need only to implement callback When you use the `JdbcTemplate` for your code, you need only to implement callback
interfaces, giving them a clearly defined contract. Given a `Connection` provided by the interfaces, giving them a clearly defined contract. Given a `Connection` provided by the
`JdbcTemplate` class, the `PreparedStatementCreator` `JdbcTemplate` class, the `PreparedStatementCreator` callback interface creates a prepared
callback interface creates a prepared statement, providing SQL and any necessary parameters. The same is true for the statement, providing SQL and any necessary parameters. The same is true for the
`CallableStatementCreator` interface, which creates callable statements. The `CallableStatementCreator` interface, which creates callable statements. The
`RowCallbackHandler` interface extracts values from each row of a `ResultSet`. `RowCallbackHandler` interface extracts values from each row of a `ResultSet`.
@ -7156,12 +7200,12 @@ conjunction with EJBs.
==== Spurious Application Server Warnings with Hibernate ==== Spurious Application Server Warnings with Hibernate
In some JTA environments with very strict `XADataSource` implementations (currently In some JTA environments with very strict `XADataSource` implementations (currently
only some WebLogic Server and WebSphere versions), when Hibernate is configured without some WebLogic Server and WebSphere versions), when Hibernate is configured without
regard to the JTA `PlatformTransactionManager` object for that environment, regard to the JTA transaction manager for that environment, spurious warnings or
spurious warning or exceptions can show up in the application server log. exceptions can show up in the application server log. These warnings or exceptions
These warnings or exceptions indicate that the connection being accessed is no longer indicate that the connection being accessed is no longer valid or JDBC access is no
valid or JDBC access is no longer valid, possibly because the transaction is no longer longer valid, possibly because the transaction is no longer active. As an example,
active. As an example, here is an actual exception from WebLogic: here is an actual exception from WebLogic:
[literal] [literal]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -7170,28 +7214,26 @@ java.sql.SQLException: The transaction is no longer active - status: 'Committed'
further JDBC access is allowed within this transaction. further JDBC access is allowed within this transaction.
---- ----
You can resolve this warning by making Hibernate aware of the JTA Another common problem is a connection leak after JTA transactions, with Hibernate
`PlatformTransactionManager` instance, to which it synchronizes (along with Spring). sessions (and potentially underlying JDBC connections) not getting closed properly.
You have two options for doing this:
* If, in your application context, you already directly obtain the JTA You can resolve such issues by making Hibernate aware of the JTA transaction manager,
`PlatformTransactionManager` object (presumably from JNDI through to which it synchronizes (along with Spring). You have two options for doing this:
`JndiObjectFactoryBean` or `<jee:jndi-lookup>`) and feed it, for example, to
Spring's `JtaTransactionManager`, the easiest way is to specify a reference to * Pass your Spring `JtaTransactionManager` bean to your Hibernate setup. The easiest
the bean that defines this JTA `PlatformTransactionManager` instance as the value of the way is a bean reference into the `jtaTransactionManager` property for your
`jtaTransactionManager` property for `LocalSessionFactoryBean.` Spring then makes the `LocalSessionFactoryBean` bean (see <<transaction-strategies-hibernate>>).
object available to Hibernate. Spring then makes the corresponding JTA strategies available to Hibernate.
* More likely, you do not already have the JTA `PlatformTransactionManager` instance, * You may also configure Hibernate's JTA-related properties explicitly, in particular
because Spring's `JtaTransactionManager` can find it itself. Thus, you need to "hibernate.transaction.coordinator_class", "hibernate.connection.handling_mode"
configure Hibernate to look up JTA `PlatformTransactionManager` directly. You do this and potentially "hibernate.transaction.jta.platform" in your "hibernateProperties"
by configuring an application server-specific `TransactionManagerLookup` class in the on `LocalSessionFactoryBean` (see Hibernate's manual for details on those properties).
Hibernate configuration, as described in the Hibernate manual.
The remainder of this section describes the sequence of events that occur with and The remainder of this section describes the sequence of events that occur with and
without Hibernate's awareness of the JTA `PlatformTransactionManager`. without Hibernate's awareness of the JTA `PlatformTransactionManager`.
When Hibernate is not configured with any awareness of the JTA When Hibernate is not configured with any awareness of the JTA transaction manager,
`PlatformTransactionManager`, the following events occur when a JTA transaction commits: the following events occur when a JTA transaction commits:
* The JTA transaction commits. * The JTA transaction commits.
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so it is * Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so it is
@ -7204,16 +7246,16 @@ When Hibernate is not configured with any awareness of the JTA
error, as the application server no longer considers the `Connection` to be usable, error, as the application server no longer considers the `Connection` to be usable,
because the transaction has already been committed. because the transaction has already been committed.
When Hibernate is configured with awareness of the JTA `PlatformTransactionManager`, the When Hibernate is configured with awareness of the JTA transaction manager,
following events occur when a JTA transaction commits: the following events occur when a JTA transaction commits:
* The JTA transaction is ready to commit. * The JTA transaction is ready to commit.
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the * Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the
transaction is called back through a `beforeCompletion` callback by the JTA transaction is called back through a `beforeCompletion` callback by the JTA
transaction manager. transaction manager.
* Spring is aware that Hibernate itself is synchronized to the JTA transaction and * Spring is aware that Hibernate itself is synchronized to the JTA transaction and
behaves differently than in the previous scenario. Assuming the Hibernate `Session` behaves differently than in the previous scenario. In particular, it aligns with
needs to be closed at all, Spring closes it now. Hibernate's transactional resource management.
* The JTA transaction commits. * The JTA transaction commits.
* Hibernate is synchronized to the JTA transaction, so the transaction is called back * Hibernate is synchronized to the JTA transaction, so the transaction is called back
through an `afterCompletion` callback by the JTA transaction manager and can through an `afterCompletion` callback by the JTA transaction manager and can
@ -7244,13 +7286,13 @@ that is used by the application to obtain an entity manager.
[[orm-jpa-setup-lemfb]] [[orm-jpa-setup-lemfb]]
===== Using `LocalEntityManagerFactoryBean` ===== Using `LocalEntityManagerFactoryBean`
You can use this option only in simple deployment environments such as stand-alone applications You can use this option only in simple deployment environments such as stand-alone
and integration tests. applications and integration tests.
The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for
simple deployment environments where the application uses only JPA for data access. The simple deployment environments where the application uses only JPA for data access.
factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according to The factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according
JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the to JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
persistence unit name. The following XML example configures such a bean: persistence unit name. The following XML example configures such a bean:
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
@ -7734,7 +7776,7 @@ steps:
your transaction coordinator. This is usually straightforward in a Java EE environment, your transaction coordinator. This is usually straightforward in a Java EE environment,
exposing a different kind of `DataSource` through JNDI. See your application server exposing a different kind of `DataSource` through JNDI. See your application server
documentation for details. Analogously, a standalone transaction coordinator usually documentation for details. Analogously, a standalone transaction coordinator usually
comes with special XA-integrated `DataSource` implementations. Again, check its documentation. comes with special XA-integrated `DataSource` variants. Again, check its documentation.
* The JPA `EntityManagerFactory` setup needs to be configured for JTA. This is * The JPA `EntityManagerFactory` setup needs to be configured for JTA. This is
provider-specific, typically through special properties to be specified as `jpaProperties` provider-specific, typically through special properties to be specified as `jpaProperties`
@ -7743,11 +7785,11 @@ are even version-specific. See your Hibernate documentation for details.
* Spring's `HibernateJpaVendorAdapter` enforces certain Spring-oriented defaults, such * Spring's `HibernateJpaVendorAdapter` enforces certain Spring-oriented defaults, such
as the connection release mode, `on-close`, which matches Hibernate's own default in as the connection release mode, `on-close`, which matches Hibernate's own default in
Hibernate 5.0 but not any more in 5.1/5.2. For a JTA setup, either do not declare Hibernate 5.0 but not any more in Hibernate 5.1+. For a JTA setup, make sure to declare
`HibernateJpaVendorAdapter` to begin with or turn off its `prepareConnection` flag. your persistence unit transaction type as "JTA". Alternatively, set Hibernate 5.2's
Alternatively, set Hibernate 5.2's `hibernate.connection.handling_mode` property to `hibernate.connection.handling_mode` property to
`DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT` to restore Hibernate's own default. `DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT` to restore Hibernate's own default.
See <<orm-hibernate-invalid-jdbc-access-error>> for a related note about WebLogic. See <<orm-hibernate-invalid-jdbc-access-error>> for related notes.
* Alternatively, consider obtaining the `EntityManagerFactory` from your application * Alternatively, consider obtaining the `EntityManagerFactory` from your application
server itself (that is, through a JNDI lookup instead of a locally declared server itself (that is, through a JNDI lookup instead of a locally declared