Mybatis两次进入同一个事务,使用同样SQL语句查询获取到脏数据?
2024-06-19 17:58:43 0 举报
Mybatis两次进入同一个事务,使用同样SQL语句查询获取到脏数据?
作者其他创作
大纲/内容
问题复现
一个公共主方法,带Spring 默认事务隔离界别 REQUEST,调用三次SQL和参数都相同的数据查询方法,该方法为 Private
调用顺序
测试前问题修复
Spring事务未生效?
注解形Spring 事务基于AOP实现,只能修饰public方法
需将private方法修改为public
问题分析
方法都是private时,三次查询分别为不同的事务
方法都是public时,事务生效,三次查询共享同一个事务
Mybatis Session?
测试
事务隔离?
Spring 事务
MySQL 事务
Mybatis 缓存?
全局调整一级缓存级别
测试条件
Spring 事务生效
测试方法
调整 Mybatis 一级缓存类型为:session -> statement
结果
一级缓存生效,读到成功的数据
关闭二级缓存
二级缓存未生效,因为这是在同一个事务中对同一个语句进行多次操作,只走了一级缓存,没有二级缓存
全局调整一级缓存和二级缓存
测试条件
Spring 事务生效
调整一级缓存类型,关闭二级缓存
测试方法
配置文件中全局关闭 Mybatis 的一二级缓存
mybatis:
configuration:
cache-enabled: false #禁用二级缓存
local-cache-scope: statement #一级缓存指定为 statement 级别
configuration:
cache-enabled: false #禁用二级缓存
local-cache-scope: statement #一级缓存指定为 statement 级别
localCacheScope属性设置为STATEMENT,则每次查询操作完成后,都会调用clearLocalCache()方法清空缓存。除此之外,MyBatis会在执行完任意更新语句后清空缓存
结果
获取到正确的数据
事务缓存
区别
一级缓存
Cache key 和 value
由 BaseExecutor 通过两个PerpetualCache类型的属性提供
二级缓存
多个命名空间可共享一个缓存
由 CachingExecutor包装类提供
MyBatis两级缓存示意图
其他问题
mybatis中使用statementType="STATEMENT", 后再使用#{ },报语法错误
mybatis中使用statementType="STATEMENT", 后再使用#{ },报语法错误
https://blog.csdn.net/qq_38966984/article/details/84729742
结论
发散
Mybatis一级缓存对数据进行了干扰?
对确实是一级缓存,但是问题出在使用者对缓存数据进行了修改
第二次查询时没有查询数据库,打在缓存上了,而缓存中的数据是脏的?为什么会这样?Mybatis Cache Key 是通过SQL语句+Hash的方式生成的,为什么不同SQL语句查询出来的结果会混在一起?
Cache Key
Mapper ID
偏移量
条数
SQL 语句
具体的SQL语句及SQL语句中需要传递的所有参数
所有参数值
先使用Mybatis再使用Mybatis Mapper 再使用Mybatis,就不会影响?
验证后,发现 ,Mapper Example不会走前一步生成的缓存(猜测是 Mapper ID 发生了变化,所以生成的 Cache Key 不一样),而是再次查询数据,最后一步会走第一步的缓存
结论
根本不是缓存主动造成,而是人操作数据时把缓存改变了
复现
com/cqsaihai/app/service/TmsToolChangeProcessService.java:815
获取缓存引用数据发生修改,是否会直接修改缓存,Mybatis提供序列化缓存(SerializedCache),但是不确定这里是否有用上
看不对获取到的缓存值进行操作,是否会对第二次查询造成影响
复现Demo
https://github.com/ybqdren/bug-snippet
0 条评论
下一页