前言
最近在应用 mybatis 的时候发现了一个问题:当我进行更新操作时,通过 id 查问条件查出一个 User 对象,并批改 user 的姓名,在进行 update 函数前,通过切面去记录他的变更信息到变更记录表中。
public User update(User user) {User oldUser = new User();
oldUser.setId(user.getId());
oldUser = this.daoSupport.selectOne(oldUser);
oldUser.setName(user.getName());
return this.daoSupport.update(oldUser);
}
在切面中,通过这个对象的主键去数据库查问到变更前的的值,和变更后的值做比照,不雷同的属性即为理论变更的属性。然而发现,在切面里通过主键查问进去的对象的各个属性值为变更后的值,这明明是在 update 之前的切面,变更内容并没有保留到数据库中,为何查问进去的值为变更后的值呢?
为了排除切面的影响,我在查问并批改 user 的姓名后,在 update 语句前,再次查问 user。
public User update(User user) {User oldUser = new User();
oldUser.setId(user.getId());
oldUser = this.daoSupport.selectOne(oldUser);
oldUser.setName(user.getName());
User oldUser1 = new User();
oldUser1.setId(user.getId());
oldUser1 = this.daoSupport.selectOne(oldUser1);
return this.daoSupport.update(oldUser);
}
此时,也的确是变更后的值,并且通过断点发现两个 user 为同一个对象,也就是说对象指针地址一样,那必定是变更后的值。猜想是存在着一种缓存机制,使得第二次查问应用了第一次查问的后果,然而这个后果是一个对象指针。
MyBatis 缓存
再网上查问得悉,mybatis 的确有一种缓存机制,并且分为一级缓存和二级缓存。
在理解一级缓存前,先理解一下 mybatis 的 SqlSession。
在 mybatis 的根底写法中,咱们都是先获取 SqlSession,而后通过 SqlSession 再去进行我的定义的 mapper 操作,当然 DaoSupport 定义了一些根本 mapper 操作且不须要手动定义 SqlSession,然而实质上也是通过 SqlSession 对数据库进行操作。
通过浏览源码得悉,mybatis 的一级缓存,就是在一个 SqlSession 的生命周期内,在进行一次查问时,将查问后果和查问条件存起来,在进行下一次查问时,先去匹配查问条件,若查问条件雷同,便不在去查询数据库,而是将上一次的查问后果返回,当然这里返回的是对象指针。那么就不难理解呈现的问题了。
解决
解决很好解决。
一种办法是全局敞开缓存,设置 mybatis.configuration.local-cache-scope=statement
一种办法时是第二次查问前清空缓存。
SqlSession sqlSession = SqlSessionUtils.getSqlSession(sqlSessionFactory);
sqlSession.clearCache();
其中 sqlSessionFactory 时通过依赖注入的。
源码浏览
对于具体机制浏览,举荐两篇文章
【不懂就问】MyBatis 的一级缓存居然还会引来麻烦?
聊聊 MyBatis 缓存机制
(另外举荐一下美团技术团队的文章,写的都很好,大家能够浏览一下,下面的第二篇文章和上次的雪花算法都来自于那里。美团技术团队)