Mybatis系列第12篇:掌握缓存为查询提速!

本文深入探讨了Mybatis的缓存机制,包括一级缓存和二级缓存的工作原理、优缺点及如何清空。一级缓存为SqlSession级别,二级缓存为mapper级别,两者共同作用于提升数据查询效率。文章详细阐述了一级缓存的三种清空方式,以及二级缓存的开启、使用和清空策略,旨在帮助开发者更好地理解和运用Mybatis缓存提高性能。
摘要由CSDN通过智能技术生成

什么是缓存?

缓存就是存储数据的一个地方(称作:Cache),当程序要读取数据时,会首先从缓存中获取,有则直接返回,否则从其他存储设备中获取,缓存最重要的一点就是从其内部获取数据的速度是非常快的,通过缓存可以加快数据的访问速度。比如我们从db中获取数据,中间需要经过网络传输耗时,db server从磁盘读取数据耗时等,如果这些数据直接放在jvm对应的内存中,访问是不是会快很多。

mybatis中的缓存

通常情况下mybatis会访问数据库获取数据,中间涉及到网络通信,数据库从磁盘中读取数据,然后将数据返回给mybatis,总的来说耗时还是挺长的,mybatis为了加快数据查询的速度,在其内部引入了缓存来加快数据的查询速度。

mybatis中分为一级缓存和二级缓存。

一级缓存是SqlSession级别的缓存,在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据,不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

下面我们详细说一下一级缓存和二级缓存的各种用法和注意点。

一级缓存

一级缓存是SqlSession级别的缓存,每个SqlSession都有自己单独的一级缓存,多个SqlSession之间的一级缓存是相互隔离的,互不影响,mybatis中一级缓存是默认自动开启的。

一级缓存工作原理:在同一个SqlSession中去多次去执行同样的查询,每次执行的时候会先到一级缓存中查找,如果缓存中有就直接返回,如果一级缓存中没有相关数据,mybatis就会去db中进行查找,然后将查找到的数据放入一级缓存中,第二次执行同样的查询的时候,会发现缓存中已经存在了,会直接返回。一级缓存的存储介质是内存,是用一个HashMap来存储数据的,所以访问速度是非常快的。

一级缓存案例

案例sql脚本

DROP DATABASE IF EXISTS `javacode2018`;
CREATE DATABASE `javacode2018`;

USE `javacode2018`;

DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '用户id',
  name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '用户名',
  age SMALLINT NOT NULL DEFAULT 1 COMMENT '年龄'
) COMMENT '用户表';
INSERT INTO t_user VALUES (1,'路人甲Java',30),(2,'张学友',50),(3,'刘德华',50);

下面是查询用户信息,返回一个list

<select id="getList1" resultType="com.javacode2018.chat05.demo9.model.UserModel" parameterType="map">
    SELECT id,name,age FROM t_user
    <where>
        <if test="id!=null">
            AND id = #{id}
        </if>
        <if test="name!=null and name.toString()!=''">
            AND name = #{name}
        </if>
        <if test="age!=null">
            AND age = #{age}
        </if>
    </where>
</select>

对应的mapper接口方法

List<UserModel> getList1(Map<String, Object> paramMap);

测试用例

com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest1

/**
 * 一级缓存测试
 *
 * @throws IOException
 */
@Test
public void level1CacheTest1() throws IOException {
    String mybatisConfig = "demo9/mybatis-config.xml";
    this.before(mybatisConfig);
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //第一次查询
        List<UserModel> userModelList1 = mapper.getList1(null);
        log.info("{}", userModelList1);
        //第二次查询
        List<UserModel> userModelList2 = mapper.getList1(null);
        log.info("{}", userModelList2);
        log.info("{}", userModelList1 == userModelList2);
    }
}

上面的代码在同一个SqlSession中去执行了2次获取用户列表信息,2次查询结果分别放在userModelList1userModelList2,最终代码中也会判断这两个集合是否相等,下面我们运行一下看看会访问几次db?

运行输出

01:15.312 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==>  Preparing: SELECT id,name,age FROM t_user 
01:15.340 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters: 
01:15.364 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <==      Total: 3
01:15.364 [main] INFO  c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=张学友, age=50), UserModel(id=3, name=刘德华, age=50)]
01:15.367 [main] INFO  c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=张学友, age=50), UserModel(id=3, name=刘德华, age=50)]
01:15.367 [main] INFO  c.j.chat05.demo9.Demo9Test - true

从输出中可以看出看到,sql只输出了一次,说明第一次会访问数据库,第二次直接从缓存中获取的,最后输出了一个true,也说明两次返回结果是同一个对象,第二次直接从缓存中获取数据的,加快了查询的速度。

清空一级缓存的3种方式

同一个SqlSession中查询同样的数据,mybatis默认会从一级缓存中获取,如果缓存中没有,才会访问db,那么我们如何去情况一级缓存呢,强制让查询去访问db呢?

让一级缓存失效有3种方式:

  1. SqlSession中执行增、删、改操作,此时sqlsession会自动清理其内部的一级缓存

  2. 调用SqlSession中的clearCache方法清理其内部的一级缓存

  3. 设置Mapper xml中select元素的flushCache属性值为true,那么执行查询的时候会先清空一级缓存中的所有数据,然后去db中获取数据

上面方式任何一种都会让当前SqlSession中的以及缓存失效,进而去db中获取数据,下面我们来分别演示这3种情况。

方式1:增删改让一级缓存失效

当执行增删改操时,mybatis会将当前SqlSession一级缓存中的所有数据都清除。

案例代码:

com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest2

/**
 * 增删改使一级缓存失效
 *
 * @throws IOException
 */
@Test
public void level1CacheTest2() throws IOException {
    String mybatisConfig = "demo9/mybatis-config.xml";
    this.before(mybatisConfig);
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //第一次查询
        List<UserModel> userModelList1 = mapper.getList1(null);
        log.info("{}", userModelList1);
        //新增一条数据
        mapper.insert1(UserModel.builder().id(100).name("路人").age(30).build());
        //第二次查询
        List<UserModel> userModelList2 = mapper.getList1(null);
        log.info("{}", userModelList2);
        log.info("{}", userModelList1 == userModelList2);
    }
}

上面同一个SqlSession中执行了3个操作,同样的查询执行了2次,2次查询中间夹了一个插入操作。

运行输出

21:55.097 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==>  Preparing: SELECT id,name,age FROM t_user 
21:55.135 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters: 
21:55.159 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <==      Total: 3
21:55.159 [main] INFO  c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=张学友, age=50), UserModel(id=3, name=刘德华, age=50)]
21:55.161 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==>  Preparing: INSERT INTO t_user (id,name,age) VALUES (?,?,?) 
21:55.162 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Parameters: 100(Integer), 路人(String), 30(Integer)
21:55.165 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - <==    Updates: 1
21:55.166 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==>  Preparing: SELECT id,name,age FROM t_user 
21:55.166 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters: 
21:55.167 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <==      Total: 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值