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());
}
getProperties().put(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta");
// Hibernate 5.1/5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default)
try {
// 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
<<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.
In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your
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
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"]
----
<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"/>
----
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.
Or alternatively, you may pass the `JtaTransactionManager` into your `LocalSessionFactoryBean`
for enforcing the same defaults:
In all these cases, 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.
[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}
</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
interfaces, giving them a clearly defined contract. Given a `Connection` provided by the
`JdbcTemplate` class, the `PreparedStatementCreator`
callback interface creates a prepared statement, providing SQL and any necessary parameters. The same is true for the
`JdbcTemplate` class, the `PreparedStatementCreator` callback interface creates a prepared
statement, providing SQL and any necessary parameters. The same is true for the
`CallableStatementCreator` interface, which creates callable statements. The
`RowCallbackHandler` interface extracts values from each row of a `ResultSet`.
@ -7156,12 +7200,12 @@ conjunction with EJBs.
==== Spurious Application Server Warnings with Hibernate
In some JTA environments with very strict `XADataSource` implementations (currently
only some WebLogic Server and WebSphere versions), when Hibernate is configured without
regard to the JTA `PlatformTransactionManager` object for that environment,
spurious warning or exceptions can show up in the application server log.
These warnings or exceptions indicate that the connection being accessed is no longer
valid or JDBC access is no longer valid, possibly because the transaction is no longer
active. As an example, here is an actual exception from WebLogic:
some WebLogic Server and WebSphere versions), when Hibernate is configured without
regard to the JTA transaction manager for that environment, spurious warnings or
exceptions can show up in the application server log. These warnings or exceptions
indicate that the connection being accessed is no longer valid or JDBC access is no
longer valid, possibly because the transaction is no longer active. As an example,
here is an actual exception from WebLogic:
[literal]
[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.
----
You can resolve this warning by making Hibernate aware of the JTA
`PlatformTransactionManager` instance, to which it synchronizes (along with Spring).
You have two options for doing this:
Another common problem is a connection leak after JTA transactions, with Hibernate
sessions (and potentially underlying JDBC connections) not getting closed properly.
* If, in your application context, you already directly obtain the JTA
`PlatformTransactionManager` object (presumably from JNDI through
`JndiObjectFactoryBean` or `<jee:jndi-lookup>`) and feed it, for example, to
Spring's `JtaTransactionManager`, the easiest way is to specify a reference to
the bean that defines this JTA `PlatformTransactionManager` instance as the value of the
`jtaTransactionManager` property for `LocalSessionFactoryBean.` Spring then makes the
object available to Hibernate.
* More likely, you do not already have the JTA `PlatformTransactionManager` instance,
because Spring's `JtaTransactionManager` can find it itself. Thus, you need to
configure Hibernate to look up JTA `PlatformTransactionManager` directly. You do this
by configuring an application server-specific `TransactionManagerLookup` class in the
Hibernate configuration, as described in the Hibernate manual.
You can resolve such issues by making Hibernate aware of the JTA transaction manager,
to which it synchronizes (along with Spring). You have two options for doing this:
* Pass your Spring `JtaTransactionManager` bean to your Hibernate setup. The easiest
way is a bean reference into the `jtaTransactionManager` property for your
`LocalSessionFactoryBean` bean (see <<transaction-strategies-hibernate>>).
Spring then makes the corresponding JTA strategies available to Hibernate.
* You may also configure Hibernate's JTA-related properties explicitly, in particular
"hibernate.transaction.coordinator_class", "hibernate.connection.handling_mode"
and potentially "hibernate.transaction.jta.platform" in your "hibernateProperties"
on `LocalSessionFactoryBean` (see Hibernate's manual for details on those properties).
The remainder of this section describes the sequence of events that occur with and
without Hibernate's awareness of the JTA `PlatformTransactionManager`.
When Hibernate is not configured with any awareness of the JTA
`PlatformTransactionManager`, the following events occur when a JTA transaction commits:
When Hibernate is not configured with any awareness of the JTA transaction manager,
the following events occur when a JTA transaction commits:
* The JTA transaction commits.
* 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,
because the transaction has already been committed.
When Hibernate is configured with awareness of the JTA `PlatformTransactionManager`, the
following events occur when a JTA transaction commits:
When Hibernate is configured with awareness of the JTA transaction manager,
the following events occur when a JTA transaction commits:
* The JTA transaction is ready to commit.
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the
transaction is called back through a `beforeCompletion` callback by the JTA
transaction manager.
* Spring is aware that Hibernate itself is synchronized to the JTA transaction and
behaves differently than in the previous scenario. Assuming the Hibernate `Session`
needs to be closed at all, Spring closes it now.
behaves differently than in the previous scenario. In particular, it aligns with
Hibernate's transactional resource management.
* The JTA transaction commits.
* Hibernate is synchronized to the JTA transaction, so the transaction is called back
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]]
===== Using `LocalEntityManagerFactoryBean`
You can use this option only in simple deployment environments such as stand-alone applications
and integration tests.
You can use this option only in simple deployment environments such as stand-alone
applications and integration tests.
The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for
simple deployment environments where the application uses only JPA for data access. The
factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according to
JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
simple deployment environments where the application uses only JPA for data access.
The factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according
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:
[source,xml,indent=0,subs="verbatim,quotes"]
@ -7734,7 +7776,7 @@ steps:
your transaction coordinator. This is usually straightforward in a Java EE environment,
exposing a different kind of `DataSource` through JNDI. See your application server
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
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
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
`HibernateJpaVendorAdapter` to begin with or turn off its `prepareConnection` flag.
Alternatively, set Hibernate 5.2's `hibernate.connection.handling_mode` property to
Hibernate 5.0 but not any more in Hibernate 5.1+. For a JTA setup, make sure to declare
your persistence unit transaction type as "JTA". Alternatively, set Hibernate 5.2's
`hibernate.connection.handling_mode` property to
`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
server itself (that is, through a JNDI lookup instead of a locally declared