美文网首页
spring集成ibatis使用声明式事务原理

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

作者: Ray昱成 | 来源:发表于2018-12-14 18:28 被阅读0次

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

    <!--配置事务管理器 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

    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使用声明式事务原理

          本文链接:https://www.haomeiwen.com/subject/rqoghqtx.html