MyBatis中的缓存

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的二级缓存。

你可能感兴趣的:(java,Mybatis)