1.MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
2.MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
3.默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)。二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
4.。为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也叫本地缓存:
1.与数据库同一次会话期间查询到的数据会放在本地缓存中。
2.以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库。
测试步骤
1.开启日志
a.导入jar包
<!-- Log4j测试工具 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
b.在全局配置文件中
<setting name="logImpl" value="LOG4J"/>
c.创建一个资源文件
log4j.rootLogger = ERROR,stdout
log4j.logger.com.mapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd HH:mm:ss,SSS} %-5p] [%t] - %m%n
2.测试在一个Sqlsession中查询两次相同的数据
public class StudentMapperTest1 {
@Test
public void test(){
SqlSession sqlSession = DBTools.getMysqlSession();
StudentMapper s1 = sqlSession.getMapper(StudentMapper.class);
StudentMapper s2 = sqlSession.getMapper(StudentMapper.class);
Student student1 = s1.findById("s_1001");
System.out.println(student1);
Student student2 = s1.findById("s_1001");
System.out.println(student2);
}
}
3.查看日志的输出,可以看出sql语句只执行了一次,也就代表只去查询了一次数据库
[01 10:12:40,471 DEBUG] [main] - ==> Preparing: select * from stu where sid=?
[01 10:12:40,494 DEBUG] [main] - ==> Parameters: s_1001(String)
[01 10:12:40,529 TRACE] [main] - <== Columns: sid, sname, sage, sgender, dormitory_id
[01 10:12:40,530 TRACE] [main] - <== Row: S_1001, 贾宝玉, 21, 男, 1
[01 10:12:40,532 DEBUG] [main] - <== Total: 1
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
Process finished with exit code 0
4.缓存和缓存失效
a.。映射语句文件中的所有select语句的结果将会被缓存。
b.映射语句文件中的所有insert、update和delete语句会刷新缓存。
c.缓存会使用最近最少使用算法 (LRU, Least Recently Used)算法来清除不需要的缓存。缓存不会定时进行刷新(也就是说,没有刷新间隔)。
d.缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
//sqLsession.cLearcache();//手动清理缓存
发现如果放入修改语句,缓存会更新,再次执行对数据库的查询
public class StudentMapperTest1 {
@Test
public void test(){
SqlSession sqlSession = DBTools.getMysqlSession();
StudentMapper s1 = sqlSession.getMapper(StudentMapper.class);
StudentMapper s2 = sqlSession.getMapper(StudentMapper.class);
StudentMapper s3 = sqlSession.getMapper(StudentMapper.class);
//查询数据
Student student1 = s1.findById("s_1001");
System.out.println(student1);
//修改数据
Student student = new Student("s_1011","猴子",45,"男");
s2.modifyStudent(student);
//查询数据
Student student3 = s1.findById("s_1001");
System.out.println(student3);
}
}
[01 10:34:05,127 DEBUG] [main] - ==> Preparing: select * from stu where sid=?
[01 10:34:05,152 DEBUG] [main] - ==> Parameters: s_1001(String)
[01 10:34:05,165 TRACE] [main] - <== Columns: sid, sname, sage, sgender, dormitory_id
[01 10:34:05,165 TRACE] [main] - <== Row: S_1001, 贾宝玉, 21, 男, 1
[01 10:34:05,167 DEBUG] [main] - <== Total: 1
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
[01 10:34:05,170 DEBUG] [main] - ==> Preparing: update stu set sname=?,sage=?,sgender=? where sid=?
[01 10:34:05,170 DEBUG] [main] - ==> Parameters: 猴子(String), 45(Integer), 男(String), s_1011(String)
[01 10:34:05,173 DEBUG] [main] - <== Updates: 1
[01 10:34:05,174 DEBUG] [main] - ==> Preparing: select * from stu where sid=?
[01 10:34:05,174 DEBUG] [main] - ==> Parameters: s_1001(String)
[01 10:34:05,175 TRACE] [main] - <== Columns: sid, sname, sage, sgender, dormitory_id
[01 10:34:05,175 TRACE] [main] - <== Row: S_1001, 贾宝玉, 21, 男, 1
[01 10:34:05,175 DEBUG] [main] - <== Total: 1
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
总结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段,一级缓存相当于一个Map。
二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存,基于namespace级别的缓存,一个名称空间,对应一个二级缓存。
工作机制
a.一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中。
b.如果当前会话关闭了(sqlsession.close方法),这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,。如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,—级缓存中的数据被保存到二级缓存中;。新的会话查询信息,就可以从二级缓存中获取内容;。不同的mapper查出的数据会放在自己对应的缓存(map)中。
开启二级缓存步骤
1.开启全局缓存
在接口的xml文件中
< ! --在当前Mapper.xmL中使用二级缓存-->
<cache/>
在配置文件中
<!-- 开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
2.执行代码,把定义的类给序列化(implements Serializable)程序运行的时候。会产生很多对象。而对象信息也只是在程序运行的时候才在内存中保持其状态。一旦程序停止,内存释放。对象也就内存释放,对象也就不存在了。使用序列化可以把对象永久缓存下来。
public class StudentMapperTest1 {
@Test
public void test(){
SqlSession sqlSession1 = DBTools.getMysqlSession();
SqlSession sqlSession2 = DBTools.getMysqlSession();
StudentMapper s1 = sqlSession1.getMapper(StudentMapper.class);
StudentMapper s2 = sqlSession2.getMapper(StudentMapper.class);
//查询数据
Student student1 = s1.findById("s_1001");
System.out.println(student1);
sqlSession1.close();
//查询数据
Student student2 = s2.findById("s_1001");
System.out.println(student2);
}
}
3.显示结果,可见关闭了sqlsession1,用sqlsession2的时候 也可以到缓存中拿数据,而不是去执行sql语句。
[01 10:56:00,201 DEBUG] [main] - Cache Hit Ratio [com.mapper.StudentMapper]: 0.0
[01 10:56:00,453 DEBUG] [main] - ==> Preparing: select * from stu where sid=?
[01 10:56:00,479 DEBUG] [main] - ==> Parameters: s_1001(String)
[01 10:56:00,494 TRACE] [main] - <== Columns: sid, sname, sage, sgender, dormitory_id
[01 10:56:00,494 TRACE] [main] - <== Row: S_1001, 贾宝玉, 21, 男, 1
[01 10:56:00,496 DEBUG] [main] - <== Total: 1
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
[01 10:56:00,506 DEBUG] [main] - Cache Hit Ratio [com.mapper.StudentMapper]: 0.5
Student(sid=S_1001, sname=贾宝玉, sage=21, sgender=男)
小结:1.只要开启了二级缓存,在同一个Mapper下就有效·所有的数据都会先放在一级缓存中。
2.只有当会话提交,或者关闭的时候,才会提交到二级缓存中。