MyBatis的缓存机制
MyBatis的缓存机制
一级缓存被称为Sqlssion级别的缓存,二级缓存被称为表级缓存。
- 一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;第二次以后是直接去缓存中取。
- 使用一级缓存
public static void main(String[] args) {
// 创建连接
try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
// 使用动态代理,获取mapper对象
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
Student student = testMapper.selectOne(1);
Student student1 = testMapper.selectOne(1);
System.out.println(student);
// mybatis默认开启一级缓存
System.out.println("student==student1??" + (student == student1));
} catch (Exception e) {
e.printStackTrace();
}
}
执行结果
我被构造了!
Student(sid=1, tid=1, sname=604, sex=女, sage=13, teacher=null)
student==student1??true
- 不使用一级缓存
到XxxMapper.xml配置文件中添加属性:flushCache=”true”,自动刷新缓存
<mapper namespace="Mapper.TestMapper">
<select id="selectAll" resultType="student" flushCache="true">
select * from student
</select>
</mapper>
public static void main(String[] args) {
// 创建连接
try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
// 使用动态代理,获取mapper对象
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
Student student = testMapper.selectOne(1);
Student student1 = testMapper.selectOne(1);
System.out.println(student);
// mybatis默认开启一级缓存
System.out.println("student==student1??" + (student == student1));
} catch (Exception e) {
e.printStackTrace();
}
}
执行结果
我被构造了!
我被构造了!
Student(sid=1, tid=1, sname=604, sex=女, sage=13, teacher=null)
student==student1??false
- Mybatis有一级缓存和二级缓存,底层都是用HashMap实现的key为CacheKey对象,value为从数据库中查出来的值。这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。
- 开启二级缓存
通过mybatisConfig.xml配置文件开启二级缓存
给所有的sqlMapper开启二级缓存
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
<mapper namespace="Mapper.TestMapper">
<cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
</mapper>
cache:缓存开启后回收策略
eviction:缓存回收策略,默认LRU
LRU - 最近最少使用:移除最长时间不被使用的对象
FIFO - 先进显出:按对象进入缓存的顺序来移除他们
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval:刷新间隔,单位毫秒
size:引用数目,正整数
readOnly:只读,true/false
通过XxxMapper.xml配置文件开启二级缓存
给单独指定的sqlMapper开启或关闭二级缓存
<mapper namespace="Mapper.TestMapper">
<cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
<select id="selectOne" resultType="student" useCache="true">
select * from student where sid=#{sid}
</select>
</mapper>
public static void main(String[] args) {
Student student01;
Student student02;
// 第一个SqlSession连接
try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
student01 = testMapper.selectOne(1);
}
// 第二个SqlSession连接
try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
student02 = testMapper.selectOne(1);
}
System.out.println(student01 == student02);
}
运行结果
我被构造了!
true
- 关闭二级缓存
<mapper namespace="Mapper.TestMapper">
<cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="false"/>
<select id="selectOne" resultType="student" useCache="true">
select * from student where sid=#{sid}
</select>
</mapper>
运行结果
我被构造了!
我被构造了!
false
- 缓存一致性
使用mybatis缓存会造成缓存不一致的问题
使用同一个SqlMapper查询数据时,数据保存到缓存中,有其他操作对这个数据进行修改,数据库中的数据发生改变,缓存中数据保持不变。
解决方案
使用redis实现一套mybatis二级缓存插件,将数据从内存转移到redis中,各个项目访问唯一一个redis实例(或集群),这样就保证在任意时刻,缓存的变化都会被所有项目感知,并使用最新的缓存数据;同时,redis的高性能也保证了缓存数据的高速读取。

浙公网安备 33010602011771号