一级缓存
相同的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之间共享
- 多表操作时,不建议使用二级缓存,容易产生脏数据