Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
为什么要使用一级缓存,不用多说也知道个大概。但是还有几个问题我们要注意一下。
1、一级缓存的生命周期有多长?
a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。
d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
下面演示下mybatis-spring整合,触发一级缓存sql打印的变化。
@Override
public User doSomeBusinessStuff(String userId) throws Exception {
userMapper.getUser("3");
userMapper.getUser("3");
return null;
}
为什么会查询两次,难道没有缓存有问题?上面说了,一级缓存是SqlSession级别的,2次执行getUser方法,都创建了一个新的SqlSession,其实是userMapper的这个接口被spring代理了,里面有个很关键的代码。
当执行完getUser方法后,会调用Commit(Commit会清空整个SqlSession的一级缓存),和close方法,导致第一次和第二次执行getUser得到的SqlSession不是同一个,所以执行了2次sql查询。如果避免commit和close,就要开启事务(不晓得怎么开启事务请查看这边文章:开启事务)
@Transactional
@Override
public User doSomeBusinessStuff(String userId) throws Exception {
userMapper.getUser("3");
userMapper.getUser("3");
return null;
}
打印sql
这次只创建了一次SqlSession,且只打印了一次sql。
先看下二级缓存的工作机制,
二级缓存默认是不开启的,如果需要开启二级缓存。开启二级缓存有2种方式,这里介绍使用注解的方式
@CacheNamespace(blocking = true)
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(@Param("userId") String userId);
}
在你的Mapper上加上@CacheNamespace(blocking = true)注解就可以了。看下使用二级缓存后打赢出来的日志。
@Override
public User doSomeBusinessStuff(String userId) throws Exception {
userMapper.getUser("3");
userMapper.getUser("3");
return null;
}
主要未避免一级缓存,先把事务关了。
可以发现也是打印了一次sql,但是创建了两次SqlSession,说明第二次是从二级缓存里面取得。二级缓存的select语句将会被缓存,insuret,update,deleted语句会刷新缓存。
针对二级缓存,其他博主说需要实现Serializable接口接口,但在测试中发现可以不用实现也会触发二级缓存。
二级缓存有个坑:那就是当你在2个mapper里面都引用了同一张表,就比如,mapper1里面有个User表查询操作,mapper2有个user表更新操作,当再次在mapper1里面在执行查询操作,发现拿到的还更新前的数据。这就是缓存Key生成原则问题,缓存key是通过mapper进行划分的,相同的mapper里面所有方法,使用的是同一个缓存区域,所以不同的mapper里面操作同一张表就会出现上面那种问题。在现实中,线上的一个应用最少2个实例,这个时候,这个问题就暴露出来了,所以缓存最好还是使用第三方缓存插件。
博客:https://www.cnblogs.com/happyflyingpig/p/7739749.html