目录
2.在 SQL 映射文件(mapper)中添加cache标签
二级缓存
二级缓存也叫全局缓存,一级缓存作用域(sqlsession级别)太低了,所以诞生了二级缓存基于namespace级别(mapper)的缓存,一个名称空间,对应一个二级缓存
工作机制:
。一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
。如果当前会话(sqlsession)关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
。新的会话查询信息,就可以从二级缓存中获取内容;
。不同的mapper查出的数据会放在自己对应的缓存(map)中;
如何开启二级缓存?
1.设置全局缓存为true(可省略,因为二级缓存默认开启)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2.在 SQL 映射文件(mapper)中添加cache标签
<mapper namespace="com.kxy.mapper.UserMapper">
<cache/>
<select id="getUserById" resultType="User" useCache="true">
select * from user where id = #{id}
</select>
<update id="updateUserById" parameterType="User">
update user set pwd = #{pwd},name =#{name} where id = #{id};
</update>
</mapper>
默认的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
如果使用默认的cache标签,则(只读属性)readOnly默认值为false。也就是可读写,而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全。如果是false,则需要让实体类实现序列化接口,否则不能正常读写。出现报错。
3.会话关闭或提交后实现二级缓存
在测试类中,我们每创建一个sqlsession,并实现了它的读操作,关闭后立马创建sqlsession1,保证了在一个时间段里只存在一个sqlsession,也就是说sqlsession关闭后,才将数据存入二级缓存中,从而sqlsession1创建后,从全局缓存(二级缓存)取出数据。
@Test
public void Test1(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1);
sqlSession.close();
System.out.println("==============");
SqlSession sqlSession1 = MyUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user2 = mapper1.getUserById(1);
System.out.println(user2);
sqlSession1.close();
System.out.println(user1==user2);
}
可以看到sql只运行了一次,并且第二个会话运行时,是从本地缓存里取得数据,没有执行sql。 false则是因为sqlsession是不同的,因此产生的对象(哈希值)地址值不同。
但是如果像这样:在第一个会话还没关闭之前就运行第二个会话,那么两个会话(线程)都没有关闭,是不会缓存的
@Test
public void Test1(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1);
System.out.println("==============");
SqlSession sqlSession1 = MyUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user2 = mapper1.getUserById(1);
System.out.println(user2);
sqlSession.close();
sqlSession1.close();
System.out.println(user1==user2);
}
所以只需要保证会话关闭后,第二个会话才能从全局缓存中取出数据。
4.增删改会刷新缓存
@Test
public void Test1(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1);
System.out.println(user1);
sqlSession.close();
System.out.println("==============");
SqlSession sqlSession1 = MyUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
mapper1.updateUserById(new User(2,"xx","14213"));
sqlSession1.commit();
System.out.println("==============");
SqlSession sqlSession2 = MyUtils.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.getUserById(1);
System.out.println(user2);
sqlSession2.close();
}
因此增删改无论是对于一级缓存还是二级缓存,都会刷新缓存!
5.一级缓存、二级缓存、与数据库的联系
优先级:二级缓存>一级缓存>数据库
缓存机制流程总结:
先看二级缓存有没有(会话关闭或提交后产生),如果有就拿。如果没有则看一级缓存(在同一个会话里查询后产生)有没有,如果有就拿,没有就去数据库里拿(jdbc预处理对象执行sql)。