mybatis 缓存

一级缓存

相同的SQL语句,会优先命中一级缓存,同一个Sqlsession中共享

一级缓存配置

  • SESSION:默认,当前session有效
  • STATEMENT:只对当前satement有效,每次都查询数据库
<setting name="localCacheScope" value="STATEMENT"/>

设置缓存级别为STATEMENT

		SqlSession sqlSession = sqlSessionFactory.openSession();
		DeviceMapper deviceMapper = sqlSession.getMapper(DeviceMapper.class);
		System.out.println("deviceMapper读取数据:"+ deviceMapper.getDeviceById(1));
		System.out.println("deviceMapper读取数据:"+ deviceMapper.getDeviceById(1)); // 不走一级缓存
		sqlSession.close();

总结

  • 一级缓存只在当前SqlSession中有效
  • 解决一级缓存分布式脏数据问题:设置缓存级别为satement,每次都查询数据库
  • 一级缓存使用HashMap存储缓存数据,当增删改时清除缓存数据

一级缓存失效情况

  • sqlSession不同
  • sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
  • sqlSession相同,手动清除了一级缓存(缓存清空)

二级缓存

数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库

开启二级缓存

mybatis-config.xml

		<!-- 开启二级缓存 -->
		<setting name="cacheEnabled" value="true"/>

mapper.xml

	<mapper namespace="com.hd.mapper.DeviceMapper">
	    <update id="update" parameterType="Device">
	    	update tb_device set device_name = #{device_name} where device_id = #{device_id} limit 1 
	    </update>
	    <cache/> <!--开启此mapper的缓存-->
	</mapper>

测试

  • 提交事务后二级缓存生效
	        SqlSession session1 = sqlSessionFactory.openSession();
		SqlSession session2 = sqlSessionFactory.openSession();
		
		DeviceMapper deviceMapper1 = session1.getMapper(DeviceMapper.class);
		DeviceMapper deviceMapper2 = session2.getMapper(DeviceMapper.class);
		
		System.out.println("deviceMapper1读取数据:"+deviceMapper1.getDeviceById(1));
 		session1.commit();  // 不提交事务,二级缓存无效,提交事务后,相同查询才会从缓存中获取
		System.out.println("deviceMapper2读取数据:"+deviceMapper2.getDeviceById(1));
		
		session1.close();
		session2.close();

缓存刷新

  • 增删改操作一样会清除二级缓存
	        SqlSession session1 = sqlSessionFactory.openSession();
		SqlSession session2 = sqlSessionFactory.openSession();
		SqlSession session3 = sqlSessionFactory.openSession();
		
		DeviceMapper deviceMapper1 = session1.getMapper(DeviceMapper.class);
		DeviceMapper deviceMapper2 = session2.getMapper(DeviceMapper.class);
		DeviceMapper deviceMapper3 = session3.getMapper(DeviceMapper.class);
		
		System.out.println("deviceMapper1读取数据:"+deviceMapper1.getDeviceById(1));
		session1.commit(); // session1提交了事务,deviceMapper2走缓存数据
		System.out.println("deviceMapper2读取数据:"+deviceMapper2.getDeviceById(1));
		
		Device device = new Device();
		device.setDevice_id(1);
		device.setDevice_name("test111");
		deviceMapper3.update(device);
		session3.commit();  // 更新提交后缓存被刷新,相同的查询走数据库
		System.out.println("deviceMapper2读取数据:"+deviceMapper2.getDeviceById(1));
		
		session1.close();
		session2.close();
		session3.close();

二级缓存跨namespace

设置mapper

	<mapper namespace="com.hd.mapper.UserMapper">
		<select id="findRoleByName" parameterType="String" resultType="Role">
			select r.* from tb_role r inner join  tb_user u on r.role_id = u.role where u.name = #{name};
		</select>
		<cache-ref namespace="com.how2java.mapper.RoleMapper"/>  <!--让UserMapper引用RoleMapper命名空间-->
	</mapper>
	<mapper namespace="com.hd.mapper.RoleMapper">
		<select id="findAllRole" resultType="com.how2java.pojo.Role">
			select r.role_name, r.role_desc, r.create_time from tb_role r
		</select>
		
		<update id="update" parameterType="com.how2java.pojo.Role">
	    	update tb_role set role_name = #{role_name} where role_id = #{role_id} limit 1 
	    </update>
	    <cache></cache> <!--开启缓存-->
	</mapper>

通过用户查询角色名称

SqlSession session1 = sqlSessionFactory.openSession();
		SqlSession session2 = sqlSessionFactory.openSession();
		SqlSession session3 = sqlSessionFactory.openSession();
		
		UserMapper userMapper1 = session1.getMapper(UserMapper.class);
		UserMapper userMapper2 = session2.getMapper(UserMapper.class);
		RoleMapper roleMapper = session3.getMapper(RoleMapper.class);
		
		System.out.println("usermapper1读取数据:"+userMapper1.findRoleByName("testuser"));
		session1.close();
		// userMapper2走了二级缓存
		System.out.println("usermapper2读取数据:"+userMapper2.findRoleByName("testuser"));
		Role role = new Role();
		role.setRole_id(3);
		role.setRole_name("test_modify");
		roleMapper.update(role);
		session3.commit();
		// 缓存刷新,userMapper2查询了数据库
		System.out.println("usermapper2读取数据:"+userMapper2.findRoleByName("testuser"));
		
		session2.close();
		session3.close();

总结

  • 二级缓存实现了跨sqlsession的缓存,不同mapper之间共享
  • 多表操作时,不建议使用二级缓存,容易产生脏数据

参考
https://tech.meituan.com/2018/01/19/mybatis-cache.html

原文地址:https://www.cnblogs.com/xiongyungang/p/14012279.html