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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354