大家好,又见面了,我是你们的朋友全栈君。
上次谈到了 mybatis一级缓存实际上是SqlSession级别的缓存,多个SqlSession并不共享,针对这种情况,我们可以使用mybatis二级缓存来处理。
1.mybatis二级缓存是什么
mybatis二级缓存是mybatis的另一种缓存机制,区别于一级缓存,它是namespace级别,即一个mapper一个缓存,相互独立,互不影响。默认不开启,需要配置开启。同一namespace下的多个sqlSession可以共享缓存,大体结构如下图
2.二级缓存生效的条件
- 同一个namespace下
- 同一个查询方法
- 同一个方法参数
需要注意的是二级缓存需要配置,一共有两种方式,下面分开讲解,举例中涉及到的表结构为用户表user(id,username,age)
1)xxxMapper.java文件上直接配置@CacheNamespace
代码语言:javascript复制@CacheNamespace
public interface UserMappser {
@Select("select * from user where id = #{id}")
User selectById(Integer id);
@Update("update user set username = #{username} where id = #{id}")
int updateById(@Param("id") Integer id, @Param("username") String username);
}
测试用例 这里需要注意下,我们模拟多个SqlSession查询,所以不需要增加事务,否则同一事务下是共用一个SqlSession的
代码语言:javascript复制 @Test
public void test(){
User user = userMappser.selectById(1);
System.out.println(user);
User user1 = userMappser.selectById(1);
System.out.println(user1);
System.out.println(user == user1);
}
结果集
这里可以看到中间的红框提示我们已经命中缓存,只查询了一次,但是看两个对象比较竟然是false,这里是因为二级缓存的配置readWrite默认是true,这里大家可以理解为是否拷贝,如果改为readWrite=false就是引用,那么两个实体比较就会为true,但是如果采用readWrite=false会导致严重的数据错乱,建议还是默认为好
xxxMapper.xml文件中配置
代码语言:javascript复制我们这使用的是spring boot 整合mybatis,只需要在xxxMapper.xml配置文件中增加一个属性cache
<mapper namespace="com.linggong.mybatis.UserMappser">
<cache/>
<select id="selectByIdXml" resultType="com.linggong.mybatis.User">
select * from user where id = #{id}
</select>
</mapper>
测试类
代码语言:javascript复制 @Test
public void test2(){
User user = userMappser.selectByIdXml(1);
System.out.println(user);
User user1 = userMappser.selectByIdXml(1);
System.out.println(user1);
System.out.println(user == user1);
}
结果集
需要注意的是@CacheNamespace或者xml中的cache元素只能二选其一,如果都配置的话启动会报错
单个方法的二级缓存设置可以在使用@CacheNamespace时在方法上配置@Options或者使用xml时配置select元素属性
3.@CacheNamespace注解属性
进入到CacheNamespace中我们可以看到它有如下属性
- implementation —缓存实现 Cache接口 实现类
- eviction —缓存算法
- flushInterval —缓存有效时间(毫秒),默认为0一直有效,不为0的话会在调用链上增加一层ScheduledCache,在putObject和getObject前执行下面的代码
@Override
public void putObject(Object key, Object object) {
clearWhenStale();
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
return clearWhenStale() ? null : delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
clearWhenStale();
return delegate.removeObject(key);
}
@Override
public void clear() {
lastClear = System.currentTimeMillis();
delegate.clear();
}
private boolean clearWhenStale() {
//当前时间-上次清除时间>缓存生效时间,如果大于则需要清空缓存
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
return true;
}
return false;
}
- size —最大缓存引用对象
- readWrite —是否可写,这里默认是true,需要缓存的数据实体需要实现Serializable接口,不然序列化会报错
- blocking —是否阻塞
4.二级缓存获取调用链
org.apache.ibatis.cache.TransactionalCacheManager#getObject org.apache.ibatis.cache.decorators.TransactionalCache#getObject org.apache.ibatis.cache.decorators.SynchronizedCache#getObject org.apache.ibatis.cache.decorators.LoggingCache#getObject org.apache.ibatis.cache.decorators.SerializedCache#getObject org.apache.ibatis.cache.decorators.ScheduledCache#getObject org.apache.ibatis.cache.decorators.LruCache#getObject org.apache.ibatis.cache.impl.PerpetualCache#getObject
5.二级缓存保存调用链
org.apache.ibatis.executor.CachingExecutor#close org.apache.ibatis.cache.TransactionalCacheManager#commit org.apache.ibatis.cache.decorators.TransactionalCache#flushPendingEntries org.apache.ibatis.cache.decorators.SynchronizedCache#putObject org.apache.ibatis.cache.decorators.LoggingCache#putObject org.apache.ibatis.cache.decorators.SerializedCache#putObject org.apache.ibatis.cache.decorators.ScheduledCache#putObject org.apache.ibatis.cache.decorators.LruCache#putObject org.apache.ibatis.cache.impl.PerpetualCache#putObject
6.如何清除二级缓存
代码语言:javascript复制同namespace下执行insert、update、delete方法会清除二级缓存
@Test
public void test1(){
User user = userMappser.selectById(1);
System.out.println(user);
//进行更新,更新后二级缓存失效
userMappser.updateById(1,"张三");
User user1 = userMappser.selectById(1);
System.out.println(user1);
System.out.println(user == user1);
}
结果集 中间增加更新语句导致二级缓存失效
6.两种配置需要注意的地方
先来看一个现象,我们现在采用@CacheNamespace配置,然后连续调用两个查询语句,中间增加一个xml中编写的update语句,代码如下
代码语言:javascript复制 @Test
public void test3(){
User user = userMappser.selectById(1);
System.out.println(user);
//xml里写的update语句
userMappser.updateByIdXml(1,"张三");
//使用@update写的update语句
//userMappser.updateById(1,"张三");
User user1 = userMappser.selectById(1);
System.out.println(user1);
System.out.println(user == user1);
}
代码语言:javascript复制 <update id="updateByIdXml">
update user set username = #{username} where id = #{id}
</update>
结果集
what the fuck?竟然还是命中了二级缓存
再来看一个例子,同样是@CacheNamespace,但是采用两个xml查询方法,看一下是否会用到二级缓存
代码语言:javascript复制 @Test
public void test2(){
User user = userMappser.selectByIdXml(1);
System.out.println(user);
User user1 = userMappser.selectByIdXml(1);
System.out.println(user1);
System.out.println(user == user1);
}
结果集 没有应用到二级缓存
这里我们看出来两种语句写法不能混用
这里需要强调的是,虽然@CacheNamespace和xml配置的方法二选其一,但是他们之间是不能相互支持的,注解方式只能支持@Select、@Update等注解方式方法,而xml只能支持写在xml文件中的select、update等语句,如果两种语句都用的话可能会导致二级缓存无法正常使用,切记!
以上为个人见解,如果表述有误或有所遗漏欢迎大家评论指出,共同学习,共同进步。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/191334.html原文链接:https://javaforall.cn