Consistent reset of resource holders on doBegin failure

Issue: SPR-12280
(cherry picked from commit 9758bc7)
This commit is contained in:
Juergen Hoeller 2014-10-07 00:06:49 +02:00
parent 6183e83b6a
commit 76ab5b9de2
7 changed files with 72 additions and 59 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,10 +33,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* {@link org.springframework.transaction.PlatformTransactionManager}
* implementation for a single JDBC {@link javax.sql.DataSource}. This class is
* capable of working in any environment with any JDBC driver, as long as the setup
* uses a JDBC 2.0 Standard Extensions / JDBC 3.0 {@code javax.sql.DataSource}
* as its Connection factory mechanism. Binds a JDBC Connection from the specified
* DataSource to the current thread, potentially allowing for one thread-bound
* Connection per DataSource.
* uses a {@code javax.sql.DataSource} as its {@code Connection} factory mechanism.
* Binds a JDBC Connection from the specified DataSource to the current thread,
* potentially allowing for one thread-bound Connection per DataSource.
*
* <p><b>Note: The DataSource that this transaction manager operates on needs
* to return independent Connections.</b> The Connections may come from a pool
@ -75,8 +74,8 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* an actual JDBC Connection from the target DataSource until a Statement gets executed,
* lazily applying the specified transaction settings to the target Connection.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via the
* JDBC 3.0 {@link java.sql.Savepoint} mechanism. The
* <p>This transaction manager supports nested transactions via the JDBC 3.0
* {@link java.sql.Savepoint} mechanism. The
* {@link #setNestedTransactionAllowed "nestedTransactionAllowed"} flag defaults
* to "true", since nested transactions will work without restrictions on JDBC
* drivers that support savepoints (such as the Oracle JDBC driver).
@ -178,7 +177,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
@ -237,7 +236,10 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
}
catch (Throwable ex) {
DataSourceUtils.releaseConnection(con, this.dataSource);
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -173,6 +173,7 @@ public class JmsTransactionManager extends AbstractPlatformTransactionManager
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
throw new InvalidIsolationLevelException("JMS does not support an isolation level concept");
}
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
Connection con = null;
Session session = null;
@ -188,10 +189,17 @@ public class JmsTransactionManager extends AbstractPlatformTransactionManager
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getResourceHolder().setTimeoutInSeconds(timeout);
}
TransactionSynchronizationManager.bindResource(
getConnectionFactory(), txObject.getResourceHolder());
TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getResourceHolder());
}
catch (Throwable ex) {
if (session != null) {
try {
session.close();
}
catch (Throwable ex2) {
// ignore
}
}
if (con != null) {
try {
con.close();

View File

@ -81,12 +81,12 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
* such a scenario (see container setup).
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
* manually set the flag to "true" if you want to use nested transactions for
* JDBC access code which participates in Hibernate transactions (provided that
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
* to "false", though, as nested transactions will just apply to the JDBC Connection,
* not to the Hibernate Session and its cached entity objects and related context.
* You can manually set the flag to "true" if you want to use nested transactions
* for JDBC access code which participates in Hibernate transactions (provided that
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
* support nested transactions! Hence, do not expect Hibernate access code to
* semantically participate in a nested transaction.</i>
@ -510,6 +510,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
finally {
SessionFactoryUtils.closeSession(session);
txObject.setSessionHolder(null);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -100,12 +100,12 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* special restrictions with EJB CMT and restrictive JTA subsystems: See
* {@link org.springframework.transaction.jta.JtaTransactionManager}'s javadoc for details.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
* manually set the flag to "true" if you want to use nested transactions for
* JDBC access code which participates in Hibernate transactions (provided that
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
* to "false", though, as nested transactions will just apply to the JDBC Connection,
* not to the Hibernate Session and its cached entity objects and related context.
* You can manually set the flag to "true" if you want to use nested transactions
* for JDBC access code which participates in Hibernate transactions (provided that
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
* support nested transactions! Hence, do not expect Hibernate access code to
* semantically participate in a nested transaction.</i>
@ -592,6 +592,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
finally {
SessionFactoryUtils.closeSession(session);
txObject.setSessionHolder(null);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -40,12 +40,12 @@ import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
* for a single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO
* PersistenceManager from the specified factory to the thread, potentially allowing
* for one thread-bound PersistenceManager per factory.
* {@link PersistenceManagerFactoryUtils} and {@link JdoTemplate} are aware of
* thread-bound persistence managers and participate in such transactions automatically.
* {@link org.springframework.transaction.PlatformTransactionManager} implementation for a
* single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO PersistenceManager
* from the specified factory to the thread, potentially allowing for one thread-bound
* PersistenceManager per factory. {@link PersistenceManagerFactoryUtils} and
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean} are aware
* of thread-bound persistence managers and participate in such transactions automatically.
* Using either of those (or going through a {@link TransactionAwarePersistenceManagerFactoryProxy}
* is required for JDO access code supporting this transaction management mechanism.
*
@ -73,10 +73,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* that acts as "connectionFactory" of the PersistenceManagerFactory, so you usually
* don't need to explicitly specify the "dataSource" property.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the JDO PersistenceManager and its cached objects.
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
* to "false", though, as nested transactions will just apply to the JDBC Connection,
* not to the JDO PersistenceManager and its cached entity objects and related context.
* You can manually set the flag to "true" if you want to use nested transactions
* for JDBC access code which participates in JDO transactions (provided that your
* JDBC driver supports Savepoints). <i>Note that JDO itself does not support
@ -91,7 +91,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* @see LocalPersistenceManagerFactoryBean
* @see PersistenceManagerFactoryUtils#getPersistenceManager
* @see PersistenceManagerFactoryUtils#releasePersistenceManager
* @see JdoTemplate
* @see TransactionAwarePersistenceManagerFactoryProxy
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
@ -155,7 +154,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
* The DataSource should match the one used by the JDO PersistenceManagerFactory:
* for example, you could specify the same JNDI DataSource for both.
* <p>If the PersistenceManagerFactory uses a DataSource as connection factory,
* the DataSource will be autodetected: You can still explictly specify the
* the DataSource will be autodetected: You can still explicitly specify the
* DataSource, but you don't need to in this case.
* <p>A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
@ -340,15 +339,16 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
conHolder.setTimeoutInSeconds(timeoutToUse);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing JDO transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
logger.debug("Exposing JDO transaction as JDBC transaction [" +
conHolder.getConnectionHandle() + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because JdoDialect [" +
getJdoDialect() + "] does not support JDBC Connection retrieval");
logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because " +
"JdoDialect [" + getJdoDialect() + "] does not support JDBC Connection retrieval");
}
}
}
@ -390,6 +390,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
finally {
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
txObject.setPersistenceManagerHolder(null, false);
}
}
@ -418,7 +419,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
}
/**
* This implementation returns "true": a JDO2 commit will properly handle
* This implementation returns "true": a JDO commit will properly handle
* transactions that have been marked rollback-only at a global level.
*/
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -55,8 +55,8 @@ import org.springframework.util.CollectionUtils;
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
* for a single JPA {@link javax.persistence.EntityManagerFactory}. Binds a JPA
* EntityManager from the specified factory to the thread, potentially allowing for
* one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator}
* and {@link JpaTemplate} are aware of thread-bound entity managers and participate
* one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator} and
* {@code @PersistenceContext} are aware of thread-bound entity managers and participate
* in such transactions automatically. Using either is required for JPA access code
* supporting this transaction management mechanism.
*
@ -85,10 +85,10 @@ import org.springframework.util.CollectionUtils;
* used as known connection factory of the EntityManagerFactory, so you usually
* don't need to explicitly specify the "dataSource" property.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the JPA EntityManager and its cached objects.
* <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints.
* The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults
* to "false", though, as nested transactions will just apply to the JDBC Connection,
* not to the JPA EntityManager and its cached entity objects and related context.
* You can manually set the flag to "true" if you want to use nested transactions
* for JDBC access code which participates in JPA transactions (provided that your
* JDBC driver supports Savepoints). <i>Note that JPA itself does not support
@ -100,7 +100,6 @@ import org.springframework.util.CollectionUtils;
* @see #setEntityManagerFactory
* @see #setDataSource
* @see LocalEntityManagerFactoryBean
* @see JpaTemplate#execute
* @see org.springframework.orm.jpa.support.SharedEntityManagerBean
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
@ -329,8 +328,8 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
TransactionSynchronizationManager.getResource(getEntityManagerFactory());
if (emHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound EntityManager [" +
emHolder.getEntityManager() + "] for JPA transaction");
logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() +
"] for JPA transaction");
}
txObject.setEntityManagerHolder(emHolder, false);
}
@ -398,15 +397,16 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
conHolder.setTimeoutInSeconds(timeoutToUse);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
logger.debug("Exposing JPA transaction as JDBC transaction [" +
conHolder.getConnectionHandle() + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because JpaDialect [" +
getJpaDialect() + "] does not support JDBC Connection retrieval");
logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because " +
"JpaDialect [" + getJpaDialect() + "] does not support JDBC Connection retrieval");
}
}
}
@ -465,6 +465,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
finally {
EntityManagerFactoryUtils.closeEntityManager(em);
}
txObject.setEntityManagerHolder(null, false);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -124,7 +124,7 @@ public class CciLocalTransactionManager extends AbstractPlatformTransactionManag
protected Object doGetTransaction() {
CciLocalTransactionObject txObject = new CciLocalTransactionObject();
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
(ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
txObject.setConnectionHolder(conHolder);
return txObject;
}
@ -157,7 +157,6 @@ public class CciLocalTransactionManager extends AbstractPlatformTransactionManag
}
TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getConnectionHolder());
}
catch (NotSupportedException ex) {
ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex);
@ -266,7 +265,7 @@ public class CciLocalTransactionManager extends AbstractPlatformTransactionManag
}
public ConnectionHolder getConnectionHolder() {
return connectionHolder;
return this.connectionHolder;
}
}