MyBatis缓存机制详解

发布于:2023-02-04 ⋅ 阅读:(591) ⋅ 点赞:(0)

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.只有当会话提交,或者关闭的时候,才会提交到二级缓存中。

MyBatis缓存原理

本文含有隐藏内容,请 开通VIP 后查看