当前使用版本(必须填写清楚,否则不予处理)
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);
因为这样就不需要一个执行类型是BATCH的SqlSession了
Comment From: zhenyuT
目前我这边的解决方法是重写saveBatch方法,通过@Autowire注解获取到sqlSession对象,手动调用sqlSession.clear()方法来清除缓存
Comment From: zhenyuT
分页的时候也会触发类似的 bug,不过分页的这个从 MP 这层来说不是太容易解决
没有用到MP的分页,很好奇分页的时候触发的是什么bug?
Comment From: Cat7373
分页的时候也会触发类似的 bug,不过分页的这个从 MP 这层来说不是太容易解决
没有用到MP的分页,很好奇分页的时候触发的是什么bug?
第一次请求,分页流程:
- 查询记录
- 查询总记录数
第二次请求,分页流程:
- 查询记录(命中一级缓存,成功查到数据)
- 不会再查询总记录数,因为查询记录压根就没执行
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。