当前使用版本(必须填写清楚,否则不予处理)

3.0.3

该问题是怎么引起的?(最新版上已修复的会直接close掉)

当在同一个事务中执行了两段方法,一段执行save。另一段执行saveBatch就会出现问题。报错Cannot change the ExecutorType when there is an existing transaction。 跟踪了报错原因是因为当调用save方法的时候使用的ExecutorType是默认的simple,但是当调用saveBatch方法的时候,mybatisplus是指定了ExecutorType为Batch.这就导致两次的ExecutorType不一致,而在同一个事务中是不能去修改ExecutorType的,故报错。

重现步骤

在方法外面开启一个事务,事务里面执行一段save,再执行一段saveBatch。典型的应用场景如插入订单主表以及插入订单明细表,希望任何一个明细插入失败的时候都可以使整个事务回滚。

报错信息

Cannot change the ExecutorType when there is an existing transaction。

Comment From: lehug

why close?...

Comment From: zhongguangxi

why close?... I checked the latest version and the BUG was fixed in the latest version

Comment From: lehug

我这边有发现,我在外层指定了isolation为read_commit,但是saveBatch依然为默认的repeatable_read,我这有个gradle跑测试出错的问题,和这个有关,所以关注了这个问题。

Comment From: zhongguangxi

我这边有发现,我在外层指定了isolation为read_commit,但是saveBatch依然为默认的repeatable_read,我这有个gradle跑测试出错的问题,和这个有关,所以关注了这个问题。

更新到最新版的mbp可以解决这个问题,但更新到最新版可能对项目改动太大。 我大概看了下mbp的解决方案是如果是批量插入就另外再open一个session。这样就不存在ExecutorType切换的问题了,也就解决了这个问题。我没有很细致的看mbp的源码,但我猜想这样可能存在问题,因为是两个session,那么也就没法让单次插入和批量插入处在同一个事务中。

Comment From: lehug

dataLocks.zip

@miemieYaho 老大,求助下这个SELECT * FROM performance_schema.data_locks;的结果。我的问题是,gradle test会出错lock超时。 伪代码:

@Transaction
serviceA {
  for(Obj o : objs) {
    serviceB.method(o);
  }
}

@Transaction
serviceB.method{
  save(entity1);
  batchSave(entity2s);
}

数据库mysql 8.0,请帮忙分析下,可能的问题点在哪里?先谢谢了

Comment From: wcpaxx

我这边有发现,我在外层指定了isolation为read_commit,但是saveBatch依然为默认的repeatable_read,我这有个gradle跑测试出错的问题,和这个有关,所以关注了这个问题。

更新到最新版的mbp可以解决这个问题,但更新到最新版可能对项目改动太大。 我大概看了下mbp的解决方案是如果是批量插入就另外再open一个session。这样就不存在ExecutorType切换的问题了,也就解决了这个问题。我没有很细致的看mbp的源码,但我猜想这样可能存在问题,因为是两个session,那么也就没法让单次插入和批量插入处在同一个事务中。

目前我这边就是这个问题,源码如下: protected boolean executeBatch(Consumer consumer) { SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(entityClass); SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory); boolean transaction = TransactionSynchronizationManager.isSynchronizationActive(); if (sqlSessionHolder != null) { SqlSession sqlSession = sqlSessionHolder.getSqlSession(); //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session //按道理来说,这里的值应该一直为false。 sqlSession.commit(!transaction); } ... } 用例:二级缓存情况下,先表A.save(记录1),表A查询记录1,再表B.saveBatch,最后异常回滚,表A的缓存中有记录1,但数据库中并没有这条记录。