前言
使用缓存可以提高获取数据的速度,避免频繁的数据库交互,尤其是在查询越多,缓存命中率越高的情况下,使用缓存的作用就更加明显了。一般提到Mybatis缓存的时候都是指二级缓存,一级缓存默认会启用。
一级缓存
1.先写一个基本的mapper来创建sqlsession,类名为BaseMapperTest
public class BaseMapperTest{
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init(){
try{
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
}catch (IOException ignore){
ignore.printStackTrace();
}
}
public SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.创建一个缓存的测试类,CacheTest
public class CacheTest extends BaseMapperTest {
@Test
public void testL1Cache(){
//获取SqlSession
SqlSession sqlSession = getSqlSession();
SysUser user1 = null;
try {
//获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用selectById方法
user1 = userMapper.selectById(1L);
//对当前获取的对象重新赋值
user1.setUserName("New Name");
//再次查询获取id相同的用户
SysUser user2 = userMapper.selectById(1L);
//虽然没有更新数据库,但是user1和user2的名字相同
Assert.assertEquals("New Name",user2.getUserName());
//无论如何user1和user2是同一个实例
Assert.assertEquals(user1,user2);
}finally {
sqlSession.close();
}
System.out.println("开启新的SqlSession");
//开启一个新的sqlsession
sqlSession = getSqlSession();
try {
//获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
SysUser user2 = userMapper.selectById(1L);
Assert.assertNotEquals("New Name",user2.getUserName());
//这里的user2和前一个session查询的结果是两个不同的实例
Assert.assertNotEquals(user1,user2);
//执行删除操作
userMapper.deleteById(2L);
//获取user3
SysUser user3 = userMapper.selectById(1L);
//这里的user2和user3是两个不同的实例
Assert.assertNotEquals(user2,user3);
}finally {
sqlSession.close();
}
}
}
运行结果如下,在第一个try语句块中,我们写了两次查询数据库的语句,但是打印出的sql只有一条,证明第二条没有查询数据库,而是查询了缓存,并且user1和user2是同一个实例,先从数据库中获取user1的值,user1重新对userName赋值,第二次查询user2发现user2的userName是user1修改后的值。
Mybatis一级缓存 存在于SqlSession的生命周期中,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果放到一个Map中,如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法生成相同的键值,当Map缓存对象中已经 存在该键值时,则会返回缓存中的对象.
如果不想使用缓存怎么办呢?可以在Mapper的方法上添加一个flushCache="true"的配置,他会在执行查询之前将缓存清除。
<select id="selectById" flushCache="true" resultMap="userMap">
SELECT * FROM sys_user WHERE id=#{id}
</select>
第二个try语句块中我们重新获取了一个新的sqlsession,查询结果显示,user2和第一个语句块中的user1没有任何关系,当我们执行删除操作后,用同一个sqlsession执行相同的查询,结果赋值给user3,结果表示user2和user3是不同的实例,原因是因为所有的insert,update,delete操作都会清空一级缓存。
二级缓存
Mybatis的二级缓存不同于一级缓存,一级缓存存在于sqlsession的生命周期中,而二级缓存可以理解为存在sqlsessionFactory的生命周期中。
1.Mybatis配置文件的简单配置
在Mybatis的全局配置settings中有一个参数cacheEnabled,这个参数是二级缓存的全局开关.默认为true,所以可以不配置,如果将其设置为false,则在后边所有缓存的配置都不起作用了。
2.Mapper.xml中配置二级缓存
在mapper中加一个cache标签即可。
3.相关的实体类bean对象需要实现序列号接口Serializable
默认的二级缓存的效果:
1.所有的Select语句将会被缓存
2.映射语句中所有的insert,update,delete操作会刷新缓存
3.缓存会使用LRU算法来回收
可用的收回策略有, 默认的是 LRU:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
4.缓存会存储集合或对象的1024个引用
5.缓存会被视为read/write的
可通过下列属性进行修改:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
3.Mapper接口中配置二级缓存
在接口上加上@CacheNamespace属性,如果在接口和XML同时配置了缓存,则需要用到参照缓存,否则会报错。写法是在接口上写@CacheNamespaceRef(XXXMapper.Class)
总结
一级缓存是mybatis默认使用的缓存,无需手动配置,二级缓存需要手动配置.
一级缓存失效条件:
1)sqlSession不同,由于一级缓存是基于sqlSession级别的,所以当使用不同sqlSession进行查询时缓存也不同;
2)sqlSession相同,手动清空一级缓存;
3)sqlSession相同,两次查询之间执行了增删改操作;
4)sqlSession相同,查询条件不同;
Mybatis默认提供的缓存是基于map实现的内存缓存,可以满足基本使用,当数据量大的时候可以借助一些缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
集成Redis缓存
第一步:添加项目依赖
<dependency>
<groupId>org.mybatis.caches</groudId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
第二步:配置redis参数:redis.properties
host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0
clientName=
第三步:在Mybatis 配置文件中设置缓存类型
<mapper namespace="com.test.MybatisMappings.UserMapper.xml" >
<cache type="org.mybatis.caches.redis.RedisCache"/>
</mapper>
来源:oschina
链接:https://my.oschina.net/langwanghuangshifu/blog/3194879