A Brief Description of Spring Transactions

/**
For transactions configured with <tx:annotation-driven transaction-manager="txManager" /> (mark @Transaction on the method)
or with AOP-configured events:
   <tx:advice id="txAdvice" transaction-manager="transactionManager">  
    <tx:attributes>  
        <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" />  
        <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
    </tx:attributes>  
</tx:advice>  
<aop:config>  
        <aop:advisor pointcut="execution(* *..*Service*.*(..))" advice-ref="txAdvice" />  
</aop:config>  
 
Spring can call TransactionInterceptor on an aspect (when a method that needs to enable transactions is called) through AOP (CGLIB).
 
  TransactionInterceptor implements method interceptors: implements org.aopalliance.intercept.MethodInterceptor
  Indicative logic:
  
    TransactionStatus  transactionStatus = platformTransactionManager.getTransaction();
try{
//call business logic here
}catch(Throwable ex){
platformTransactionManager.rollback(transactionStatus);
throw ex;
}
platformTransactionManager.commit(transactionStatus);
 
DataSourceTransactionManager, a subclass of platformTransactionManager, obtains a ConnectionHolder from the current thread when a transaction is enabled, creates one if it does not exist, and binds it to the thread.
(Get ConnectionHolder from thread: TransactionSynchronizationManager.getResource(this.dataSource)
The object that binds the ConnectionHolder to the thread is: TransactionSynchronizationManager.bindResource(this.dataSource,ConnectionHolder)
ConnectionHolder is stored in TransactionStatus (the actual subclass is: DefaultTransactionStatus), so that commit or rollback can get the same Connection.
 
 
In executing sql, how does JdbcTemplate get the same Connection?
   Connection con = DataSourceUtils.getConnection(getDataSource());
DataSourceUtils will obtain the ConnectionHolder from the current thread, if there is a direct access to use,
 If it does not exist, get a Connection from the DataSource,
 Determine whether there is a transaction (TransactionSynchronizationManager.isSynchronizationActive()),
    If there is, create a ConnectionHolder and store it in the current thread line (TransactionSynchronizationManager.bindResource(dataSource, ConnectionHolder);) for next use
If not, return Connection directly. (When there is no transaction, a new connection will be created every time a Connection is used)
 
 
Description: When the transaction is enabled, the Connection (ConnctionHolder) is obtained, and it is bound to the current thread. Each operation of JdbcTemplate can obtain the same Connection operation in the thread.
 **/

/**
Relevant code:
 
TransactionInterceptor method:
**/
  @Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
 
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();//Actual business logic
}
});
}
/**
*
*
*/
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
 
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
 
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {//不是CallbackPreferringPlatformTransactionManager
// Standard transaction demarcation with getTransaction and commit/rollback calls.
 
//Enable transactions -- call the getTransaction method of PlatformTransactionManager (actual subclasses such as DataSourceTransactionManager) to enable transactions
//Create TransactionInfo (holding PlatformTransactionManager internally) and bind to the current thread
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation(); call the actual business logic
}
catch (Throwable ex) {
// target invocation exception
//Exception rollback
//txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);//Clear information
}
// commit the transaction
//txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
commitTransactionAfterReturning(txInfo);//Submit the database
return retVal;
}
 
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
//If it is CallbackPreferringPlatformTransactionManager. . . .
// ignore part of the code
}
}
 
 
 
DataSourceTransactionManager is a subclass of PlatformTransactionManager that provides transaction processing capabilities.
 
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager{
private DataSource dataSource;//Internally holds the data source object
 
/**
*
*
*/
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();//Create a DataSourceTransactionObject object that holds ConnectionHolder
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);//Get in the current thread
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
 
try {
if (txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);//Get the Connection and store it in the ConnectionHolder object, which holds the Connection inside,
}
 
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
 
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
 
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);//Set auto commit to false
}
txObject.getConnectionHolder().setTransactionActive(true);//Mark the transaction start status
 
// Bind the session holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());//Bind ConnectionHolder to the current thread
}
}
 
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
 
 
//Enable transaction, internal call
  public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//Non-actual code, ignoring part of the code
Object transaction = doGetTransaction();
 
 
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(//Create a TransactionStatus object and hold DataSourceTransactionObject inside
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
 
}
 
 
    /**
*
* DefaultTransactionStatus--hold-->DataSourceTransactionObject--hold-->ConnectionHolder--hold-"Connection-->commit()
*/
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
 
try {
con.commit();//Submit
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
 
/**
*
* DefaultTransactionStatus--hold-->DataSourceTransactionObject--hold-->ConnectionHolder--hold-"Connection-->rollback()
*/
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
 
try {
con.rollback();//Transaction rollback
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
 
 
   JdbcTemplate gets Connction through DataSourceUtils:
   
  public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
 
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);//当线
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
 
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
 
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
 
return con;
}
 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326460406&siteId=291194637