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

3.0.6

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

saveBatch方法会导致一级缓存无法被清除,第二次查询不会发出sql查询

重现步骤

如下代码,在一个事务中:

 //查询列表,能查到1条数据
List<TaskEo> taskEoList = taskMapper.selectList(new QueryWrapper<TaskEo>().eq("farm_id", -1025));
Assert.assertEquals(1, taskEoList.size());

//插入数据
taskService.saveBatch(taskEoList);

//同样条件再次查询,只能查到1条数据,日志中也没有打印sql语句
taskEoList = taskMapper.selectList(new QueryWrapper<TaskEo>().eq("farm_id", -1025));
Assert.assertEquals(2, taskEoList.size()) ; //这里断言异常:java.lang.AssertionError: Expected :2 Actual   :1

如上代码,数据库本来有一条数据,调用saveBatch方法后(taskService实现了IService接口),再次查询,发现只有一条数据,而实际数据库是有两条数据的。

在第二次查询前手动清除sqlSession的缓存,则能正常查出两条数据,代码如下:

//查询列表,能查到1条数据
List<TaskEo> taskEoList = taskMapper.selectList(new QueryWrapper<TaskEo>().eq("farm_id", -1025));
Assert.assertEquals(1, taskEoList.size());  

//插入数据
taskService.saveBatch(taskEoList);

//手动清除sqlSession缓存
sqlSession.clearCache();

//同样条件再次查询,能查到2条数据,日志中有打印sql语句
taskEoList = taskMapper.selectList(new QueryWrapper<TaskEo>().eq("farm_id", -1025));
Assert.assertEquals(2, taskEoList.size()) ; //正常

原因分析

在saveBatch方法中会重新创建SqlSession对象,和外部的SqlSession不是同一个实例,从而导致外部的SqlSession缓存无法被清除,再次查询时会使用缓存而不去查数据库。

Comment From: Cat7373

分页的时候也会触发类似的 bug,不过分页的这个从 MP 这层来说不是太容易解决

Comment From: Cat7373

如果插入的SQL能做成下面这样这个问题可能就会好处理一些:

INSERT INTO table VALUES
  (1, 2, 3),
  (4, 5, 6),
  (7, 8, 9);

因为这样就不需要一个执行类型是BATCHSqlSession

Comment From: zhenyuT

目前我这边的解决方法是重写saveBatch方法,通过@Autowire注解获取到sqlSession对象,手动调用sqlSession.clear()方法来清除缓存

Comment From: zhenyuT

分页的时候也会触发类似的 bug,不过分页的这个从 MP 这层来说不是太容易解决

没有用到MP的分页,很好奇分页的时候触发的是什么bug?

Comment From: Cat7373

分页的时候也会触发类似的 bug,不过分页的这个从 MP 这层来说不是太容易解决

没有用到MP的分页,很好奇分页的时候触发的是什么bug?

第一次请求,分页流程:

  1. 查询记录
  2. 查询总记录数

第二次请求,分页流程:

  1. 查询记录(命中一级缓存,成功查到数据)
  2. 不会再查询总记录数,因为查询记录压根就没执行SQL,拦截器大概是没拦到,于是总记录数为 0

目前我的解决方案是:每次请求开始的时候由拦截器做清空缓存

这大概是2.x时遇到的问题,目前项目一直带着拦截器,因此并不清楚新版本是否解决了这个问题

Comment From: Cat7373

或者在批量处理的最后面加一行,清空主 SqlSession 的一级缓存也可以

虽然不是个完美的解决方案

Comment From: chenzhenjia

我也遇到了这个问题,请问有其它解决方案吗。

Comment From: Cat7373

可以尝试手动清除一级缓存

Comment From: liwei0903nn

可以尝试手动清除一级缓存

老哥,能看下cat邮箱的邮件吗?我有一个关于sqlSession的疑问还望请教。

Comment From: linux2014linux

总结一下,符合如下结构均会触发改bug: `

@Transactional returnType funciton() { getBy(condition1); saveBatch(data1); getBy(condtion1); }

` 由于事务的存在,两个getBy共用一个sqlSession,第一次为空被缓存,第二次查到也为空。中间的saveBatch是独立的sqlSession。