在学习mybatis缓存之前我们先来看三个问题:
- 什么是缓存:
存在内存的临时数据
将用户经常查询的数据放在缓存(内存)中,用户查询数据就不会用磁盘(关系型数据库数据文件)查询,从缓存中查询,从而提高了查询效率,解决高并发系统的性能问题
- 为什么要使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统的效率
- 什么样的数据能使用缓存?
经常查询,并且不经常修改的数据
Mybatis缓存:
MyBatis包含一个非常强大的查询缓存特性,可以非常方便得定制和配置缓存,缓存可以极大的提升查询效率
MyBatis系统中默认定义了两级缓存: 一级缓存和二级缓存
默认情况下只有一级缓存开启,(sqlsession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,是基于namespace级别的缓存
为了提高扩展性,MyBatis定义了缓存接口Cache
一级缓存:
特点:
- 一级缓存也叫本地缓存:
- · 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- · 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
//根据id查询用户
User queryUserById(@Param("id") int id);
<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
---------------------------------------------------------------------------------
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
--------------------------------------------------------------------
session.close();
}
结果分析:
我们可以看到是同一个sqlSession 执行了两次相同的查询,但是通过日志我们只能看到执行了一条SQL语句,说明第一条是通过数据库查询的,第二条是通过缓存查询的
一级缓存失效的四种情况:
1.sqlSession不同:
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
session2.close();
}
结果: 发现发送了两条sql语句,每个sqlSession中的缓存相互独立
2.sqlSession相同,查询条件不同
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(2);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
3.sqlSession相同,两次查询之间执行了增删改操作:
增加方法
//修改用户
int updateUser(Map map);
编写SQL
<update id="updateUser" parameterType="map">
update user set name = #{name} where id = #{id}
</update>
测试
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
HashMap map = new HashMap();
map.put("name","kuangshen");
map.put("id",4);
mapper.updateUser(map);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
观察结果:查询在中间执行了增删改操作后,重新执行了
结论:因为增删改操作可能会对当前数据产生影响
4.sqlSession相同,手动清除一级缓存:
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.clearCache();//手动清除缓存
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
二级缓存:
二级缓存又称为全局缓存,由于一级缓存的作用域太低,所以诞生了二级缓存,基于namespace级别的缓存,一个名称控件,对应一个二级缓存:
工作机制:
一个会话查询一条数据-----> 这个数据被放在当前会话的一级缓存中------->如果当前会话关闭了------->这个会话的一级缓存就没了-------> 这个时候一级缓存的数据就会被保存到二级缓存中,新的会话内容就可以从二级缓存中获取内容
如何使用?
在mybatis-config.xml中开启全局缓存
<setting name="cacheEnabled" value="true"/>
在每个mapper.xml配置中使用二级缓存:
<cache/>
代码测试:
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.close();
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session2.close();
}
结论
· 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
· 查出的数据都会被默认先放在一级缓存中
· 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中