Skip to content

Commit e417318

Browse files
committed
Revise native Hibernate 5 bootstrapping with JTA transaction manager
Closes gh-25858
1 parent 2533ba5 commit e417318

File tree

2 files changed

+91
-47
lines changed

2 files changed

+91
-47
lines changed

spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ else if (jtaTransactionManager instanceof TransactionManager) {
225225
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
226226
}
227227

228+
getProperties().put(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta");
229+
228230
// Hibernate 5.1/5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default)
229231
try {
230232
// Try Hibernate 5.2

src/docs/asciidoc/data-access.adoc

Lines changed: 89 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,19 @@ NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>
381381
from the `jee` namespace. For more information see
382382
<<integration.adoc#xsd-schemas-jee, The JEE Schema>>.
383383

384+
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
385+
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
386+
technology. This is due to the fact that JTA transactions are global transactions, which
387+
can enlist any transactional resource.
388+
389+
In all Spring transaction setups, application code does not need to change. You can change
390+
how transactions are managed merely by changing configuration, even if that change means
391+
moving from local to global transactions or vice versa.
392+
393+
394+
[[transaction-strategies-hibernate]]
395+
==== Hibernate Transaction Setup
396+
384397
You can also easily use Hibernate local transactions, as shown in the following examples.
385398
In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your
386399
application code can use to obtain Hibernate `Session` instances.
@@ -420,21 +433,52 @@ example declares `sessionFactory` and `txManager` beans:
420433

421434
If you use Hibernate and Java EE container-managed JTA transactions, you should use the
422435
same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following
423-
example shows:
436+
example shows. Also, it is recommended to make Hibernate aware of JTA through its
437+
transaction coordinator and possibly also its connection release mode configuration:
424438

425439
[source,xml,indent=0,subs="verbatim,quotes"]
426440
----
441+
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
442+
<property name="dataSource" ref="dataSource"/>
443+
<property name="mappingResources">
444+
<list>
445+
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
446+
</list>
447+
</property>
448+
<property name="hibernateProperties">
449+
<value>
450+
hibernate.dialect=${hibernate.dialect}
451+
hibernate.transaction.coordinator_class=jta
452+
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
453+
</value>
454+
</property>
455+
</bean>
456+
427457
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
428458
----
429459

430-
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
431-
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
432-
technology. This is due to the fact that JTA transactions are global transactions, which
433-
can enlist any transactional resource.
460+
Or alternatively, you may pass the `JtaTransactionManager` into your `LocalSessionFactoryBean`
461+
for enforcing the same defaults:
434462

435-
In all these cases, application code does not need to change. You can change how
436-
transactions are managed merely by changing configuration, even if that change means
437-
moving from local to global transactions or vice versa.
463+
[source,xml,indent=0,subs="verbatim,quotes"]
464+
----
465+
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
466+
<property name="dataSource" ref="dataSource"/>
467+
<property name="mappingResources">
468+
<list>
469+
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
470+
</list>
471+
</property>
472+
<property name="hibernateProperties">
473+
<value>
474+
hibernate.dialect=${hibernate.dialect}
475+
</value>
476+
</property>
477+
<property name="jtaTransactionManager" ref="txManager"/>
478+
</bean>
479+
480+
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
481+
----
438482

439483

440484

@@ -3115,8 +3159,8 @@ hierarchy defined in the `org.springframework.dao` package. (See <<dao-exception
31153159

31163160
When you use the `JdbcTemplate` for your code, you need only to implement callback
31173161
interfaces, giving them a clearly defined contract. Given a `Connection` provided by the
3118-
`JdbcTemplate` class, the `PreparedStatementCreator`
3119-
callback interface creates a prepared statement, providing SQL and any necessary parameters. The same is true for the
3162+
`JdbcTemplate` class, the `PreparedStatementCreator` callback interface creates a prepared
3163+
statement, providing SQL and any necessary parameters. The same is true for the
31203164
`CallableStatementCreator` interface, which creates callable statements. The
31213165
`RowCallbackHandler` interface extracts values from each row of a `ResultSet`.
31223166

@@ -7156,12 +7200,12 @@ conjunction with EJBs.
71567200
==== Spurious Application Server Warnings with Hibernate
71577201

71587202
In some JTA environments with very strict `XADataSource` implementations (currently
7159-
only some WebLogic Server and WebSphere versions), when Hibernate is configured without
7160-
regard to the JTA `PlatformTransactionManager` object for that environment,
7161-
spurious warning or exceptions can show up in the application server log.
7162-
These warnings or exceptions indicate that the connection being accessed is no longer
7163-
valid or JDBC access is no longer valid, possibly because the transaction is no longer
7164-
active. As an example, here is an actual exception from WebLogic:
7203+
some WebLogic Server and WebSphere versions), when Hibernate is configured without
7204+
regard to the JTA transaction manager for that environment, spurious warnings or
7205+
exceptions can show up in the application server log. These warnings or exceptions
7206+
indicate that the connection being accessed is no longer valid or JDBC access is no
7207+
longer valid, possibly because the transaction is no longer active. As an example,
7208+
here is an actual exception from WebLogic:
71657209

71667210
[literal]
71677211
[subs="verbatim,quotes"]
@@ -7170,28 +7214,26 @@ java.sql.SQLException: The transaction is no longer active - status: 'Committed'
71707214
further JDBC access is allowed within this transaction.
71717215
----
71727216

7173-
You can resolve this warning by making Hibernate aware of the JTA
7174-
`PlatformTransactionManager` instance, to which it synchronizes (along with Spring).
7175-
You have two options for doing this:
7217+
Another common problem is a connection leak after JTA transactions, with Hibernate
7218+
sessions (and potentially underlying JDBC connections) not getting closed properly.
7219+
7220+
You can resolve such issues by making Hibernate aware of the JTA transaction manager,
7221+
to which it synchronizes (along with Spring). You have two options for doing this:
71767222

7177-
* If, in your application context, you already directly obtain the JTA
7178-
`PlatformTransactionManager` object (presumably from JNDI through
7179-
`JndiObjectFactoryBean` or `<jee:jndi-lookup>`) and feed it, for example, to
7180-
Spring's `JtaTransactionManager`, the easiest way is to specify a reference to
7181-
the bean that defines this JTA `PlatformTransactionManager` instance as the value of the
7182-
`jtaTransactionManager` property for `LocalSessionFactoryBean.` Spring then makes the
7183-
object available to Hibernate.
7184-
* More likely, you do not already have the JTA `PlatformTransactionManager` instance,
7185-
because Spring's `JtaTransactionManager` can find it itself. Thus, you need to
7186-
configure Hibernate to look up JTA `PlatformTransactionManager` directly. You do this
7187-
by configuring an application server-specific `TransactionManagerLookup` class in the
7188-
Hibernate configuration, as described in the Hibernate manual.
7223+
* Pass your Spring `JtaTransactionManager` bean to your Hibernate setup. The easiest
7224+
way is a bean reference into the `jtaTransactionManager` property for your
7225+
`LocalSessionFactoryBean` bean (see <<transaction-strategies-hibernate>>).
7226+
Spring then makes the corresponding JTA strategies available to Hibernate.
7227+
* You may also configure Hibernate's JTA-related properties explicitly, in particular
7228+
"hibernate.transaction.coordinator_class", "hibernate.connection.handling_mode"
7229+
and potentially "hibernate.transaction.jta.platform" in your "hibernateProperties"
7230+
on `LocalSessionFactoryBean` (see Hibernate's manual for details on those properties).
71897231

71907232
The remainder of this section describes the sequence of events that occur with and
71917233
without Hibernate's awareness of the JTA `PlatformTransactionManager`.
71927234

7193-
When Hibernate is not configured with any awareness of the JTA
7194-
`PlatformTransactionManager`, the following events occur when a JTA transaction commits:
7235+
When Hibernate is not configured with any awareness of the JTA transaction manager,
7236+
the following events occur when a JTA transaction commits:
71957237

71967238
* The JTA transaction commits.
71977239
* 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
72047246
error, as the application server no longer considers the `Connection` to be usable,
72057247
because the transaction has already been committed.
72067248

7207-
When Hibernate is configured with awareness of the JTA `PlatformTransactionManager`, the
7208-
following events occur when a JTA transaction commits:
7249+
When Hibernate is configured with awareness of the JTA transaction manager,
7250+
the following events occur when a JTA transaction commits:
72097251

72107252
* The JTA transaction is ready to commit.
72117253
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the
72127254
transaction is called back through a `beforeCompletion` callback by the JTA
72137255
transaction manager.
72147256
* Spring is aware that Hibernate itself is synchronized to the JTA transaction and
7215-
behaves differently than in the previous scenario. Assuming the Hibernate `Session`
7216-
needs to be closed at all, Spring closes it now.
7257+
behaves differently than in the previous scenario. In particular, it aligns with
7258+
Hibernate's transactional resource management.
72177259
* The JTA transaction commits.
72187260
* Hibernate is synchronized to the JTA transaction, so the transaction is called back
72197261
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.
72447286
[[orm-jpa-setup-lemfb]]
72457287
===== Using `LocalEntityManagerFactoryBean`
72467288

7247-
You can use this option only in simple deployment environments such as stand-alone applications
7248-
and integration tests.
7289+
You can use this option only in simple deployment environments such as stand-alone
7290+
applications and integration tests.
72497291

72507292
The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for
7251-
simple deployment environments where the application uses only JPA for data access. The
7252-
factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according to
7253-
JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
7293+
simple deployment environments where the application uses only JPA for data access.
7294+
The factory bean uses the JPA `PersistenceProvider` auto-detection mechanism (according
7295+
to JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the
72547296
persistence unit name. The following XML example configures such a bean:
72557297

72567298
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -7734,7 +7776,7 @@ steps:
77347776
your transaction coordinator. This is usually straightforward in a Java EE environment,
77357777
exposing a different kind of `DataSource` through JNDI. See your application server
77367778
documentation for details. Analogously, a standalone transaction coordinator usually
7737-
comes with special XA-integrated `DataSource` implementations. Again, check its documentation.
7779+
comes with special XA-integrated `DataSource` variants. Again, check its documentation.
77387780

77397781
* The JPA `EntityManagerFactory` setup needs to be configured for JTA. This is
77407782
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.
77437785

77447786
* Spring's `HibernateJpaVendorAdapter` enforces certain Spring-oriented defaults, such
77457787
as the connection release mode, `on-close`, which matches Hibernate's own default in
7746-
Hibernate 5.0 but not any more in 5.1/5.2. For a JTA setup, either do not declare
7747-
`HibernateJpaVendorAdapter` to begin with or turn off its `prepareConnection` flag.
7748-
Alternatively, set Hibernate 5.2's `hibernate.connection.handling_mode` property to
7788+
Hibernate 5.0 but not any more in Hibernate 5.1+. For a JTA setup, make sure to declare
7789+
your persistence unit transaction type as "JTA". Alternatively, set Hibernate 5.2's
7790+
`hibernate.connection.handling_mode` property to
77497791
`DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT` to restore Hibernate's own default.
7750-
See <<orm-hibernate-invalid-jdbc-access-error>> for a related note about WebLogic.
7792+
See <<orm-hibernate-invalid-jdbc-access-error>> for related notes.
77517793

77527794
* Alternatively, consider obtaining the `EntityManagerFactory` from your application
77537795
server itself (that is, through a JNDI lookup instead of a locally declared

0 commit comments

Comments
 (0)