Skip to content

Commit 76ab5b9

Browse files
committed
Consistent reset of resource holders on doBegin failure
Issue: SPR-12280 (cherry picked from commit 9758bc7)
1 parent 6183e83 commit 76ab5b9

File tree

7 files changed

+72
-59
lines changed

7 files changed

+72
-59
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,10 +33,9 @@
3333
* {@link org.springframework.transaction.PlatformTransactionManager}
3434
* implementation for a single JDBC {@link javax.sql.DataSource}. This class is
3535
* capable of working in any environment with any JDBC driver, as long as the setup
36-
* uses a JDBC 2.0 Standard Extensions / JDBC 3.0 {@code javax.sql.DataSource}
37-
* as its Connection factory mechanism. Binds a JDBC Connection from the specified
38-
* DataSource to the current thread, potentially allowing for one thread-bound
39-
* Connection per DataSource.
36+
* uses a {@code javax.sql.DataSource} as its {@code Connection} factory mechanism.
37+
* Binds a JDBC Connection from the specified DataSource to the current thread,
38+
* potentially allowing for one thread-bound Connection per DataSource.
4039
*
4140
* <p><b>Note: The DataSource that this transaction manager operates on needs
4241
* to return independent Connections.</b> The Connections may come from a pool
@@ -75,8 +74,8 @@
7574
* an actual JDBC Connection from the target DataSource until a Statement gets executed,
7675
* lazily applying the specified transaction settings to the target Connection.
7776
*
78-
* <p>On JDBC 3.0, this transaction manager supports nested transactions via the
79-
* JDBC 3.0 {@link java.sql.Savepoint} mechanism. The
77+
* <p>This transaction manager supports nested transactions via the JDBC 3.0
78+
* {@link java.sql.Savepoint} mechanism. The
8079
* {@link #setNestedTransactionAllowed "nestedTransactionAllowed"} flag defaults
8180
* to "true", since nested transactions will work without restrictions on JDBC
8281
* drivers that support savepoints (such as the Oracle JDBC driver).
@@ -178,7 +177,7 @@ protected Object doGetTransaction() {
178177
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
179178
txObject.setSavepointAllowed(isNestedTransactionAllowed());
180179
ConnectionHolder conHolder =
181-
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
180+
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
182181
txObject.setConnectionHolder(conHolder, false);
183182
return txObject;
184183
}
@@ -237,7 +236,10 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
237236
}
238237

239238
catch (Throwable ex) {
240-
DataSourceUtils.releaseConnection(con, this.dataSource);
239+
if (txObject.isNewConnectionHolder()) {
240+
DataSourceUtils.releaseConnection(con, this.dataSource);
241+
txObject.setConnectionHolder(null, false);
242+
}
241243
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
242244
}
243245
}

spring-jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -173,6 +173,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
173173
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
174174
throw new InvalidIsolationLevelException("JMS does not support an isolation level concept");
175175
}
176+
176177
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
177178
Connection con = null;
178179
Session session = null;
@@ -188,10 +189,17 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
188189
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
189190
txObject.getResourceHolder().setTimeoutInSeconds(timeout);
190191
}
191-
TransactionSynchronizationManager.bindResource(
192-
getConnectionFactory(), txObject.getResourceHolder());
192+
TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getResourceHolder());
193193
}
194194
catch (Throwable ex) {
195+
if (session != null) {
196+
try {
197+
session.close();
198+
}
199+
catch (Throwable ex2) {
200+
// ignore
201+
}
202+
}
195203
if (con != null) {
196204
try {
197205
con.close();

spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@
8181
* transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
8282
* such a scenario (see container setup).
8383
*
84-
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
85-
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
86-
* flag defaults to "false", though, as nested transactions will just apply to the
87-
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
88-
* manually set the flag to "true" if you want to use nested transactions for
89-
* JDBC access code which participates in Hibernate transactions (provided that
84+
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
85+
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
86+
* to "false", though, as nested transactions will just apply to the JDBC Connection,
87+
* not to the Hibernate Session and its cached entity objects and related context.
88+
* You can manually set the flag to "true" if you want to use nested transactions
89+
* for JDBC access code which participates in Hibernate transactions (provided that
9090
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
9191
* support nested transactions! Hence, do not expect Hibernate access code to
9292
* semantically participate in a nested transaction.</i>
@@ -510,6 +510,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
510510
}
511511
finally {
512512
SessionFactoryUtils.closeSession(session);
513+
txObject.setSessionHolder(null);
513514
}
514515
}
515516
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);

spring-orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -100,12 +100,12 @@
100100
* special restrictions with EJB CMT and restrictive JTA subsystems: See
101101
* {@link org.springframework.transaction.jta.JtaTransactionManager}'s javadoc for details.
102102
*
103-
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
104-
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
105-
* flag defaults to "false", though, as nested transactions will just apply to the
106-
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
107-
* manually set the flag to "true" if you want to use nested transactions for
108-
* JDBC access code which participates in Hibernate transactions (provided that
103+
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
104+
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
105+
* to "false", though, as nested transactions will just apply to the JDBC Connection,
106+
* not to the Hibernate Session and its cached entity objects and related context.
107+
* You can manually set the flag to "true" if you want to use nested transactions
108+
* for JDBC access code which participates in Hibernate transactions (provided that
109109
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
110110
* support nested transactions! Hence, do not expect Hibernate access code to
111111
* semantically participate in a nested transaction.</i>
@@ -592,6 +592,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
592592
}
593593
finally {
594594
SessionFactoryUtils.closeSession(session);
595+
txObject.setSessionHolder(null);
595596
}
596597
}
597598
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);

spring-orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,12 +40,12 @@
4040
import org.springframework.transaction.support.TransactionSynchronizationManager;
4141

4242
/**
43-
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
44-
* for a single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO
45-
* PersistenceManager from the specified factory to the thread, potentially allowing
46-
* for one thread-bound PersistenceManager per factory.
47-
* {@link PersistenceManagerFactoryUtils} and {@link JdoTemplate} are aware of
48-
* thread-bound persistence managers and participate in such transactions automatically.
43+
* {@link org.springframework.transaction.PlatformTransactionManager} implementation for a
44+
* single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO PersistenceManager
45+
* from the specified factory to the thread, potentially allowing for one thread-bound
46+
* PersistenceManager per factory. {@link PersistenceManagerFactoryUtils} and
47+
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean} are aware
48+
* of thread-bound persistence managers and participate in such transactions automatically.
4949
* Using either of those (or going through a {@link TransactionAwarePersistenceManagerFactoryProxy}
5050
* is required for JDO access code supporting this transaction management mechanism.
5151
*
@@ -73,10 +73,10 @@
7373
* that acts as "connectionFactory" of the PersistenceManagerFactory, so you usually
7474
* don't need to explicitly specify the "dataSource" property.
7575
*
76-
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
77-
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
78-
* flag defaults to "false", though, as nested transactions will just apply to the
79-
* JDBC Connection, not to the JDO PersistenceManager and its cached objects.
76+
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
77+
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
78+
* to "false", though, as nested transactions will just apply to the JDBC Connection,
79+
* not to the JDO PersistenceManager and its cached entity objects and related context.
8080
* You can manually set the flag to "true" if you want to use nested transactions
8181
* for JDBC access code which participates in JDO transactions (provided that your
8282
* JDBC driver supports Savepoints). <i>Note that JDO itself does not support
@@ -91,7 +91,6 @@
9191
* @see LocalPersistenceManagerFactoryBean
9292
* @see PersistenceManagerFactoryUtils#getPersistenceManager
9393
* @see PersistenceManagerFactoryUtils#releasePersistenceManager
94-
* @see JdoTemplate
9594
* @see TransactionAwarePersistenceManagerFactoryProxy
9695
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
9796
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
@@ -155,7 +154,7 @@ public PersistenceManagerFactory getPersistenceManagerFactory() {
155154
* The DataSource should match the one used by the JDO PersistenceManagerFactory:
156155
* for example, you could specify the same JNDI DataSource for both.
157156
* <p>If the PersistenceManagerFactory uses a DataSource as connection factory,
158-
* the DataSource will be autodetected: You can still explictly specify the
157+
* the DataSource will be autodetected: You can still explicitly specify the
159158
* DataSource, but you don't need to in this case.
160159
* <p>A transactional JDBC Connection for this DataSource will be provided to
161160
* application code accessing this DataSource directly via DataSourceUtils
@@ -340,15 +339,16 @@ public int getTimeout() {
340339
conHolder.setTimeoutInSeconds(timeoutToUse);
341340
}
342341
if (logger.isDebugEnabled()) {
343-
logger.debug("Exposing JDO transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
342+
logger.debug("Exposing JDO transaction as JDBC transaction [" +
343+
conHolder.getConnectionHandle() + "]");
344344
}
345345
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
346346
txObject.setConnectionHolder(conHolder);
347347
}
348348
else {
349349
if (logger.isDebugEnabled()) {
350-
logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because JdoDialect [" +
351-
getJdoDialect() + "] does not support JDBC Connection retrieval");
350+
logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because " +
351+
"JdoDialect [" + getJdoDialect() + "] does not support JDBC Connection retrieval");
352352
}
353353
}
354354
}
@@ -390,6 +390,7 @@ protected void closePersistenceManagerAfterFailedBegin(JdoTransactionObject txOb
390390
finally {
391391
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
392392
}
393+
txObject.setPersistenceManagerHolder(null, false);
393394
}
394395
}
395396

@@ -418,7 +419,7 @@ protected void doResume(Object transaction, Object suspendedResources) {
418419
}
419420

420421
/**
421-
* This implementation returns "true": a JDO2 commit will properly handle
422+
* This implementation returns "true": a JDO commit will properly handle
422423
* transactions that have been marked rollback-only at a global level.
423424
*/
424425
@Override

spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -55,8 +55,8 @@
5555
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
5656
* for a single JPA {@link javax.persistence.EntityManagerFactory}. Binds a JPA
5757
* EntityManager from the specified factory to the thread, potentially allowing for
58-
* one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator}
59-
* and {@link JpaTemplate} are aware of thread-bound entity managers and participate
58+
* one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator} and
59+
* {@code @PersistenceContext} are aware of thread-bound entity managers and participate
6060
* in such transactions automatically. Using either is required for JPA access code
6161
* supporting this transaction management mechanism.
6262
*
@@ -85,10 +85,10 @@
8585
* used as known connection factory of the EntityManagerFactory, so you usually
8686
* don't need to explicitly specify the "dataSource" property.
8787
*
88-
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
89-
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
90-
* flag defaults to "false", though, as nested transactions will just apply to the
91-
* JDBC Connection, not to the JPA EntityManager and its cached objects.
88+
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
89+
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
90+
* to "false", though, as nested transactions will just apply to the JDBC Connection,
91+
* not to the JPA EntityManager and its cached entity objects and related context.
9292
* You can manually set the flag to "true" if you want to use nested transactions
9393
* for JDBC access code which participates in JPA transactions (provided that your
9494
* JDBC driver supports Savepoints). <i>Note that JPA itself does not support
@@ -100,7 +100,6 @@
100100
* @see #setEntityManagerFactory
101101
* @see #setDataSource
102102
* @see LocalEntityManagerFactoryBean
103-
* @see JpaTemplate#execute
104103
* @see org.springframework.orm.jpa.support.SharedEntityManagerBean
105104
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
106105
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
@@ -329,8 +328,8 @@ protected Object doGetTransaction() {
329328
TransactionSynchronizationManager.getResource(getEntityManagerFactory());
330329
if (emHolder != null) {
331330
if (logger.isDebugEnabled()) {
332-
logger.debug("Found thread-bound EntityManager [" +
333-
emHolder.getEntityManager() + "] for JPA transaction");
331+
logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() +
332+
"] for JPA transaction");
334333
}
335334
txObject.setEntityManagerHolder(emHolder, false);
336335
}
@@ -398,15 +397,16 @@ public int getTimeout() {
398397
conHolder.setTimeoutInSeconds(timeoutToUse);
399398
}
400399
if (logger.isDebugEnabled()) {
401-
logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
400+
logger.debug("Exposing JPA transaction as JDBC transaction [" +
401+
conHolder.getConnectionHandle() + "]");
402402
}
403403
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
404404
txObject.setConnectionHolder(conHolder);
405405
}
406406
else {
407407
if (logger.isDebugEnabled()) {
408-
logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because JpaDialect [" +
409-
getJpaDialect() + "] does not support JDBC Connection retrieval");
408+
logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because " +
409+
"JpaDialect [" + getJpaDialect() + "] does not support JDBC Connection retrieval");
410410
}
411411
}
412412
}
@@ -465,6 +465,7 @@ protected void closeEntityManagerAfterFailedBegin(JpaTransactionObject txObject)
465465
finally {
466466
EntityManagerFactoryUtils.closeEntityManager(em);
467467
}
468+
txObject.setEntityManagerHolder(null, false);
468469
}
469470
}
470471

spring-tx/src/main/java/org/springframework/jca/cci/connection/CciLocalTransactionManager.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ public Object getResourceFactory() {
124124
protected Object doGetTransaction() {
125125
CciLocalTransactionObject txObject = new CciLocalTransactionObject();
126126
ConnectionHolder conHolder =
127-
(ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
127+
(ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
128128
txObject.setConnectionHolder(conHolder);
129129
return txObject;
130130
}
@@ -157,7 +157,6 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
157157
}
158158
TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getConnectionHolder());
159159
}
160-
161160
catch (NotSupportedException ex) {
162161
ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
163162
throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex);
@@ -266,7 +265,7 @@ public void setConnectionHolder(ConnectionHolder connectionHolder) {
266265
}
267266

268267
public ConnectionHolder getConnectionHolder() {
269-
return connectionHolder;
268+
return this.connectionHolder;
270269
}
271270
}
272271

0 commit comments

Comments
 (0)