使用声明式事务模板例子:
<!--配置事务管理器 TransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="XXXDataSource"/>
</property>
</bean>
<!--配置事务模板 TransactionTemplate-->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
</bean>
//代码调用
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
XXXDAO.insert(XXDO);
});
这里有一个疑问,DAO是通过ibatis操作的,为何能用spring的事务,不会和ibatis的事务产生冲突么?
首先看下TransactionTemplate的execute源码:
@Override
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
// Transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
action.doInTransaction(status)
执行的是上面DAO的insert操作。但实际是调用spring中SqlMapClientTemplate的insert
然后查看SqlMapClientTemplate的execute方法
public Object execute(SqlMapClientCallback action) throws DataAccessException {
SqlMapSession session = this.sqlMapClient.openSession(); //这里打开了session,后续操作使用这个SqlMapSession对象完成
Connection ibatisCon = null;
try {
Connection springCon = null;
DataSource dataSource = getDataSource();
boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
// Obtain JDBC Connection to operate on...
try {
ibatisCon = session.getCurrentConnection();
if (ibatisCon == null) {
springCon = (transactionAware ?
dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
session.setUserConnection(springCon);//这一步操作很关键
}
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
// Execute given callback...
try {
return action.doInSqlMapClient(session);//传入了打开的SqlSession对象
}
}
首先创建了一个SqlMapSession,并且获取了一个Spring可管理的Connection,设置到了SqlMapSession中,正是这一操作使得ibatis的自动事务关闭了。我们看SqlMapSession的setUserConnection方法,调用了delegate的setUserProviededTransaction方法:
public void setUserProvidedTransaction(SessionScope sessionScope, Connection userConnection)
{
if (sessionScope.getTransactionState() == TransactionState.STATE_USER_PROVIDED) {
sessionScope.recallTransactionState();
}
if (userConnection != null) {
Connection conn = userConnection;
sessionScope.saveTransactionState();
sessionScope.setTransaction(new UserProvidedTransaction(conn));
sessionScope.setTransactionState(TransactionState.STATE_USER_PROVIDED);
} else {
sessionScope.setTransaction(null);
sessionScope.closePreparedStatements();
sessionScope.cleanup();
}
}
这里就给SessionScope设置了一个UserProvidedTransactiond对象。
真正判断使用哪个事务的在这,ibatis的SqlMapExecutorDelegate的insert方法
:
public Object insert(SessionScope sessionScope, String id, Object param)
throws SQLException
{
Object generatedKey = null;
MappedStatement ms = getMappedStatement(id); //获取映射的sql配置信息
Transaction trans = getTransaction(sessionScope);//获取当前session的事务(SessionScope在一个session中唯一)
boolean autoStart = trans == null;//判断事务是否为空
try
{
trans = autoStartTransaction(sessionScope, autoStart, trans);//为空则使用自动事务
SelectKeyStatement selectKeyStatement = null;
if (ms instanceof InsertStatement) {
selectKeyStatement = ((InsertStatement)ms).getSelectKeyStatement();
}
Object oldKeyValue = null;
String keyProperty = null;
boolean resetKeyValueOnFailure = false;
if ((selectKeyStatement != null) && (!(selectKeyStatement.isRunAfterSQL()))) {
keyProperty = selectKeyStatement.getKeyProperty();
oldKeyValue = PROBE.getObject(param, keyProperty);
generatedKey = executeSelectKey(sessionScope, trans, ms, param);
resetKeyValueOnFailure = true;
}
StatementScope statementScope = beginStatementScope(sessionScope, ms);//生成StatementScope信息,其中包含sessionScope对象
try {
ms.executeUpdate(statementScope, trans, param);//使用MappedStatement对象执行,批量操作处理在这里实现
}
catch (SQLException e)
{
if (resetKeyValueOnFailure);
throw e;
} finally {
endStatementScope(statementScope);
}
if ((selectKeyStatement != null) && (selectKeyStatement.isRunAfterSQL())) {
generatedKey = executeSelectKey(sessionScope, trans, ms, param);
}
//注意这里,相当关键。如果是使用自动事务,那么会自动提交事务
autoCommitTransaction(sessionScope, autoStart);
} finally {
autoEndTransaction(sessionScope, autoStart);
}
return generatedKey;
}
MappedStatement中的executeUpdate方法中实现正常操作或者批处理,如果是自动事务那么会自动提交。
到这里答案明了:
上述代码段中
Transaction trans = getTransaction(sessionScope);
可知事务是从sessionScope获取,而前面已经在创建SqlMapSession时已经Transaction放在socpe了,所以ibatis就不会再去关注事务了,由用户自己去管理事务了,这里相当于就是把事务交给了spring来管理了。如果我们没用通过spring给执行批量操作的方法加事务操作,那么实际上就相当于这段代码没有使用事务
网友评论