Mybatis作为持久化框架,提供了非常强大的缓存特性。一般在提到Mybatis缓存的时候,指的都是二级缓存。一级缓存,即本地缓存,默认会启用并且不能控制,可能会导致一些难以发现的错误。
一、一级缓存
public void testL1Cache() {
SqlSession sqlSession = getSqlSession();
SysUser user1 = null;
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
user1 = userMapper.selectById(1L);
user1.setUserName("New Name");
SysUser user2 = userMapper.selectById(1L);
// 虽然没有更新数据库,但是这个用户名和user1重新复制的名字相同
Assert.assertEquals("New Name", user2.getUserName());
// 无论如何,user2和user1完全就是同一个实例
Assert.assertEquals(user1, user2);
} finally {
sqlSession.close();
}
System.out.println("开启新的sqlSession");
sqlSession = getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
SysUser user2 = userMapper.selectById(1L);
Assert.assertNotSame("New Name", user2.getUserName());
Assert.assertNotSame(user1, user2);
// 执行删除操作
userMapper.deleteById(2L);
//获取user3
SysUser user3 = userMapper.selectById(1L);
Assert.assertNotSame(user3, user2);
} finally {
sqlSession.close();
}
}
在如上的测试代码中,获取到user1之后,重新设置了username的值,但并未更新到数据库中;当再次执行该对象的查询之后,发现得到的user2对象的username属性和user1之前重新设置的值一样,并且此时user1和user2是同一个对象。这就是一级缓存导致的。
Mybatis的一级缓存存在于SqlSession的生命周期当中,意思就是说,在同一个SqlSession中执行的查询操作,且中间不包括增删改的操作时,如果该对象在之前已经查询过,新查询获取到的都将是保存在缓存中的这个对象。当二级缓存没有开启时,我们重新开启一个SqlSession,这时再查询,就不再是之前的缓存的那个对象了。这是因为在SqlSession关闭时,缓存已经被清空了。
如果不想让该方法使用一级缓存,可以在该方法的xml文件中增加 flushCache="true" 的属性。这样配置之后,在该方法的每次查询之前都会清空当前的一级缓存,重新从数据库中查询对象,可以避免上面的问题。但是这个方法由于清空了一级缓存,会影响当前SqlSession缓存的所有查询,会增加数据库的查询次数,导致性能的下降。
二、二级缓存
不同于一级缓存,二级缓存的生命周期可以理解为SqlSessionFactory的生命周期。二级缓存的相关配置在这里就不叙述了。需要注意的是,xml配置方式和注解配置方式不能同时使用,会因为命名空间的冲突而导致异常,可以将其中一个改为参照缓存即可。
public void testL2Cache(){
SqlSession sqlSession = getSqlSession();
SysRole role1 = null;
try{
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
role1 = roleMapper.selectById(1L);
role1.setRoleName("New Name");
SysRole role2 = roleMapper.selectById(1L);
Assert.assertEquals("New Name", role2.getRoleName());
Assert.assertEquals(role1, role2);
}finally{
sqlSession.close();
}
System.out.println("开启新的SqlSession");
try{
sqlSession = getSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
SysRole role2 = roleMapper.selectById(1L);
Assert.assertEquals("New Name", role2.getRoleName());
Assert.assertNotEquals(role1, role2);
SysRole role3 = roleMapper.selectById(1L);
Assert.assertNotEquals(role3, role2);
}finally{
sqlSession.close();
}
}
上面的测试代码中,再次获取role2时,即使已经在一个新的SqlSession当中,一级缓存已经清空,role2并不会执行数据库的查询操作,而是到Mybatis的二级缓存中取出对象,此时role2的rolename属性为之前修改后的值。由于在本测试用例中,配置的二级缓存属性为可读写缓存,role2和role3都是反序列化的接口,所以他们并不是相同的实例。如果配置为只读缓存,role2和role3获得的将是缓存中role1的引用,将会是相同的实例。
目前,mybatis支持EhCache缓存框架以及Redis缓存数据库来保存mybatis的二级缓存。