spring集成ibatis使用声明式事务原理

使用声明式事务模板例子:



     
        
    



        
            
        
    
//代码调用
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    XXXDAO.insert(XXDO);
});

这里有一个疑问,DAO是通过ibatis操作的,为何能用spring的事务,不会和ibatis的事务产生冲突么?

首先看下TransactionTemplate的execute源码:

@Override
    public  T execute(TransactionCallback 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

image.png

然后查看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给执行批量操作的方法加事务操作,那么实际上就相当于这段代码没有使用事务

完整的事务执行时序图:

image.png

参考:https://www.cnblogs.com/jdluojing/p/4201832.html

你可能感兴趣的:(spring集成ibatis使用声明式事务原理)