被mybatis一级缓存坑了
2024-10-11 02:00:09
背景
项目中出现了这样一个问题,就是select
出来的数据和数据库里的数据不一样,就非常的奇怪,发现原来是mybatis
的缓存导致的,经过查询资料发现这是mybatis
的一级缓存。
下面介绍了问题出现的场景以及解决办法
场景
Mapper类:
@Mapper
public interface UserMapper {
@Results(value = {
@Result(property = "id", column = "id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
@Result(property = "age", column = "age", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
@Result(property = "name", column = "name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
@Result(property = "createTime", column = "create_time", javaType = Date.class, jdbcType = JdbcType.DATE)
})
@Select("SELECT id, age, name, create_time FROM user WHERE id = #{id}")
User selectUser(Long id);
}
一个普通的bean
@Slf4j
@Component
public class MyBean {
@Autowired
private UserMapper userMapper;
public void test1() {
User user = userMapper.selectUser(1L);
log.info("user:{}", user);
user.setAge(3); // 更新其中一个属性
user = userMapper.selectUser(1L);
log.info("user:{}", user);
}
@Transactional
public void test2() {
test1();
}
}
配置类:
@Configuration
@MapperScan("cn.eagle.li.mybatis.cache.session")
@EnableTransactionManagement
public class Config {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean(name = "sqlSessionFactory")
@ConditionalOnMissingBean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public DataSource dataSource() {
MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8");
return dataSource;
}
}
测试类
@Slf4j
public class Main {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
MyBean myBean = context.getBean(MyBean.class);
myBean.test1();
log.info("==================");
myBean.test2();
}
}
运行结果:
1966 [main] INFO c.e.li.mybatis.cache.session.MyBean - user:User(age=2, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021)
1996 [main] INFO c.e.li.mybatis.cache.session.MyBean - user:User(age=2, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021)
1996 [main] INFO c.e.l.m.c.session.DataSourceMain - ==================
2046 [main] INFO c.e.li.mybatis.cache.session.MyBean - user:User(age=2, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021)
2047 [main] INFO c.e.li.mybatis.cache.session.MyBean - user:User(age=3, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021)
可以看到两个方法的代码内容是一样的,只不过第二个方法上加了一个事务
第一个方法中,两次从数据库选出的结果是一样的;而在第二个方法中,两次从数据库选出的结果是不一样的(age
的值)
可以猜测是@Transactional
+user.setAge(3);
导致的结果不一样
原因
经过调试,是下面的这行代码的原因,BaseExecutor.query
如下:
上面两张图片分别是不带事务
和带事务
执行到第二个查询的时候经过的地方,可以看出带事务
的方法,到这里的时候,直接从localCache
取出来了,这就是原因所在。
大家可以去看一下localCache
是什么时候被清理掉了,其实带事务的
等到事务结束之后才会清理掉;而不带事务
的把每一个查询当成一个事务,所以每个查询后就被清理到了。
解法
- 就是不要修改查询出来的类,如下:
public void test1() {
User user = userMapper.selectUser(1L);
log.info("user:{}", user);
User user2 = User.builder().name(user.getName()).age(3).id(user.getId()).build();
log.info("user:{}", user);
}
- 当然,也可以把一级缓存关掉,如下配置:
mybatis-spring-config.xml 文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
</configuration>
@Bean(name = "sqlSessionFactory")
@ConditionalOnMissingBean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setConfigLocation(new ClassPathResource("mybatis-spring-config.xml")); // 这里加载配置文件
return sessionFactory.getObject();
}
其实就是每次查询后,都把localCache
给清理掉了,原理如下:
参考
最新文章
- HTTPS原理详解
- Android中View绘制流程以及invalidate()等相关方法分析
- oracle删除表以及清理表空间
- javaweb--上传文件UploadServlet1.java
- javascript优化--02高质量编码
- CSS自定义文件上传按钮
- bzoj3931: [CQOI2015]网络吞吐量
- OCA读书笔记(6) - 配置Oracle网络环境
- Lampda或Linq中的SqlFunctions.StringConvert()
- ie7,8下__flash__addCallback报错原因及解决方案
- JavaScript(8)——JSON
- jquery动态加载 去除js
- Bootstrap-datepicker3官方文档中文翻译---概述(原版翻译 http://bootstrap-datepicker.readthedocs.io/en/latest/index.html)
- LindDotNetCore~入门基础
- CSS深入理解学习笔记之z-index
- My97 DatePicker图标触发
- Asp.net(C#)年月日时分秒毫秒
- Python全栈之路----hash函数
- 【并发编程】IO模型
- PLMN概念和应用设置
热门文章
- 【LeetCode】402. Remove K Digits 解题报告(Python)
- 【LeetCode】692. Top K Frequent Words 解题报告(Python)
- 51Nod 1279:扔盘子(二分||单调栈)
- 【计算机组成】 Quartus II 关于总线data[][]转换多个总线data[]时不成功的问题
- 第十六个知识点:描述DSA,Schnorr,RSA-FDH的密钥生成,签名和验证
- Variational Inference with Normalizing Flow
- Sentry 开发者贡献指南 - SDK 开发(性能监控:Sentry SDK API 演进)
- 【MySQL作业】MySQL函数——美和易思日期和时间函数应用习题
- linux 设置开机自动启动应用
- Android 摄像头预览悬浮窗