Spring事务实现源码之事务实现以及Connection的绑定与获取

版权声明:本文为博主原创文章,转载必须标明出处.尊重他人就是尊重自己! https://blog.csdn.net/Dax1n/article/details/82497030

PlatformTransactionManager是spring事务的高级抽象,事务的实现需要借助PlatformTransactionManager完成,该管理主要方法如下:

当我们在使用事务的时候,需要调用如下方法获取一个事务状态对象。

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
接下来就看一下AbstractPlatformTransactionManager#getTransaction源码:
  @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        
        //transaction类型:DataSourceTransactionManager.DataSourceTransactionObject
        //注意 DataSourceTransactionObject的数据结构,DataSourceTransactionObject包含一个ConnectionHolder
        // 此时会
        Object transaction = doGetTransaction();

        if (definition == null) {
            // 如果传入的事务定义实例为null的话则创建一个默认的事务定义实例
            definition = new DefaultTransactionDefinition();
        }

        if (isExistingTransaction(transaction)) {
            // 事务传播行为有关的的处理
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                //重重之重的代码,完成创建数据库连接绑定以及设置自动提交为False以及
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            } catch (RuntimeException ex) {
                resume(null, suspendedResources);
                throw ex;
            } catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        } else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }
doGetTransaction方法会尝试获取连接,如果当前线程没有绑定Connection的话则返回null,org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction源码如下:
	@Override
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		// 重点代码,使用TransactionSynchronizationManager获取上下文是否有Connection
        //TransactionSynchronizationManager获取连接是借助ThreadLocal实现的
        ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

在doGetTransaction如果当前没有绑定连接的话,则需要给当前线程绑定一个连接;org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin方法是事务的基础,接下里以DataSourceTransactionManager实现为例,查看一下源码:

@Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        // DataSourceTransactionObject持有数据库连接ConnectionHolder成员
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
            if (txObject.getConnectionHolder() == null ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                // 上下文没有获取到Connection,因此需要在数据库连接池中获取一个Connection 并设置到事务对象中
                // 但是由于该连接并没有和线程绑定因此需要跟线程绑定,下文TransactionSynchronizationManager.bindResourc方法绑定到线程
                Connection newCon = this.dataSource.getConnection();
                if (logger.isDebugEnabled()) {
                    logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
                }
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }

            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            con = txObject.getConnectionHolder().getConnection();

            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            txObject.setPreviousIsolationLevel(previousIsolationLevel);
            
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (logger.isDebugEnabled()) {
                    logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }
                con.setAutoCommit(false);
            }
            txObject.getConnectionHolder().setTransactionActive(true);

            int timeout = determineTimeout(definition);
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
            }

            // 将连接跟当前线程进行绑定
            if (txObject.isNewConnectionHolder()) {
                TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
            }
        } 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);
        }
    }

到此为止就完成了当前线程的Connection绑定,绑定资源时候调用TransactionSynchronizationManager#bindResource方法,传入两个参数:分别是DataSource与ConnechtionHolder,这是为了避免一个线程处理多个连接池的Connection时候出错而设置,这样获取连接时候会根据线程与连接池共同为key获取对应的唯一Connection。在Connection绑定时候TransactionSynchronizationManager是一个重要的工具。

TransactionSynchronizationManager

该类是一个重要的工具类,用于Spring事务中的资源绑定,每一个线程绑定一个属于自己的Connection,这样保证事务有序不乱。底层使用ThreadLocal实现!!!!

当某一线程需要获取连接时候会调用doGetResource方法获取连接:
org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource;
当doGetResource方法获取不到连接时候调用bindResource方法绑定连接:
org.springframework.transaction.support.TransactionSynchronizationManager#bindResource

接下来查看一下数据查询时候如何获取线程绑定的Connection,为了便于debug使用的编程式事务管理,源码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描注解-->
    <context:component-scan base-package="com.javartisan.jdbc.spring"/>

    <bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="minIdle" value="2"/>
        <property name="maxIdle" value="5"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="maxTotal" value="10"/>
        <property name="initialSize" value="5"/>
        <property name="url"
                  value="jdbc:mysql://127.0.0.1:3306/databasebook?serverTimezone=UTC&amp;characterEncoding=utf-8&amp;useSSL=true"/>
    </bean>
    <bean id="platformTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="datasource"/>
    </bean>
</beans>

Service源码:

@Service
public class StudentService {
    
    @Resource
    private PlatformTransactionManager platformTransactionManager;
    @Resource
    private StudentDao studentDao;
    public List<Student> getStudents() {
        TransactionDefinition td = new DefaultTransactionDefinition();
        TransactionStatus status = platformTransactionManager.getTransaction(td);
        List<Student> res = null;
        try {
            res = studentDao.getStudents();
            studentDao.insert();
            // int intRes = 1 / 0;
            platformTransactionManager.commit(status);
        } catch (Exception e) {
            platformTransactionManager.rollback(status);
        }
        return res;
    }

}

Dao源码:

@Repository
public class StudentDao {

    @Resource
    private JdbcTemplate jdbcTemplate;

    public List<Student> getStudents() {
        return jdbcTemplate.query("select * from student", new StudentMapper());
    }

    public void insert() {
        Object[] args = {System.currentTimeMillis() + "", "0907", "1", 18, "0907"};
        int[] types = {Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR};
        jdbcTemplate.update("INSERT INTO databasebook.STUDENT (sno, sname, ssex, sage, sdept) VALUES (?,?,?, ?, ?);", args, types);
    }
}

此处以getStudents方法为例跟进分析如何获取当前绑定的Connection,方法调用流程如下:

jdbcTemplate.query调用JdbcTemplate.execute方法,重点看一下JdbcTemplate.execute源码:
@Override
	public <T> T execute(StatementCallback<T> action) throws DataAccessException {
		Assert.notNull(action, "Callback object must not be null");
        // 重点代码,使用DataSourceUtils工具获取绑定资源
		Connection con = DataSourceUtils.getConnection(getDataSource());
		Statement stmt = null;
		try {
			Connection conToUse = con;
			if (this.nativeJdbcExtractor != null &&
					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
			}
			stmt = conToUse.createStatement();
			applyStatementSettings(stmt);
			Statement stmtToUse = stmt;
			if (this.nativeJdbcExtractor != null) {
				stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
			}
			T result = action.doInStatement(stmtToUse);
			handleWarnings(stmt);
			return result;
		}
		catch (SQLException ex) {
			// Release Connection early, to avoid potential connection pool deadlock
			// in the case when the exception translator hasn't been initialized yet.
			JdbcUtils.closeStatement(stmt);
			stmt = null;
			DataSourceUtils.releaseConnection(con, getDataSource());
			con = null;
			throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
		}
		finally {
			JdbcUtils.closeStatement(stmt);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}

org.springframework.jdbc.datasource.DataSourceUtils#getConnection源码如下:

	public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}
	}

org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection源码:

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
        // 重点:还是TransactionSynchronizationManager获取绑定资源
        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;
    }

到此就即可获取到绑定的Connection资源!!!

重重重要:

TransactionSynchronizationManager是绑定资源与获取绑定资源的核心工具!!!!

猜你喜欢

转载自blog.csdn.net/Dax1n/article/details/82497030
今日推荐