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&characterEncoding=utf-8&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是绑定资源与获取绑定资源的核心工具!!!!