MyBatis——关于一级缓存 & 二级缓存的案例详解

简介: MyBatis——关于一级缓存 & 二级缓存的案例详解

文章目录:


1.写在前面

2.关于MyBatis中的缓存

3.一级缓存案例详解

3.1 首先写一个实体Bean

3.2 dao接口、对应的mapper映射文件

3.3 mybatis配置文件、工具类

3.4 测试方法

3.4.1 同一个会话对象查询同一个数据

3.4.2 同一个会话对象查询两个不同的数据

3.4.3 insertupdatedelete操作会刷新缓存

3.4.4 手动清理缓存

4.二级缓存案例讲解

4.1 开启二级缓存之后,实体类需要实现序列化

4.2 dao接口、mapper文件、myabtis配置文件

4.3 测试方法

4.3.1 一级缓存面对两个SqlSession会话对象

4.3.2 二级缓存面对两个SqlSession会话对象

5.MyBatis缓存原理

1.写在前面


首先,我们来聊聊什么是缓存Cache


说到数据库啊,无论是MySQLSql ServerOracleRedis等等,它们当中都是有缓存机制的,那么到底什么是缓存呢?


·       存在内存中的临时数据。

·       将用户经常查询的数据放在缓存(相当于从内存中拷贝了一块高速内存区,存放临时数据)中,用户去查询数据就不用从磁盘上(关系型数据库文件中)查询了,只

需要去缓存中查询有无即可,从而提高了查询效率,解决了高并发系统的性能问题。

那么问什么要使用缓存呢?


·       目的是为了减少与数据库的交互次数,减少系统开销,提高系统的执行效率。

什么样的数据可以使用缓存呢?

·       经常查询,并且不经常改变的数据。

2.关于MyBatis中的缓存


MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大的提升查询效率。

我们也可以在官方文档中看到有关缓存的定义说明


MyBatis框架中默认定义了两级缓存:一级缓存和二级缓存

·       默认情况下,MyBatis只开启了一级缓存(SqlSession级别的缓存,也称为本地缓存)

·       二级缓存需要手动开启和配置,它是基于namespace级别的缓存。

·       为了提高扩展性,MyBatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存。

3.一级缓存案例详解


一级缓存也叫本地缓存:SqlSeesion

·       与数据库同一次会话期间查询到的数据会放在本地缓存中(也就是同一个SqlSession对象)。

·       以后如果需要获取相同的数据,就可以直接在缓存中拿,没有必须再去查询数据库了。

·       一级缓存是默认开启的,只在一次SqlSession会话中有效,也就是从当前SqlSession拿到连接到close关闭连接这个区间段。


3.1 首先写一个实体Bean


头三个注解是lombok中的,用来自动生成get/set方法、toString、无参/有参构造方法、hashCodeequals这些方法。

pom文件中的依赖分别添加了:mybatismysql驱动、lombokjunit

package com.szh.mybatis.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 *
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Integer age;
}

3.2 dao接口、对应的mapper映射文件

package com.szh.mybatis.mapper;
import com.szh.mybatis.bean.User;
import org.apache.ibatis.annotations.Param;
/**
 *
 */
public interface UserMapper {
    /**
     * 根据id查询用户信息
     * @param id
     * @return
     */
    User selectUserById(@Param("uid") Integer id);
    /**
     * 修改某个用户的信息
     * @param user
     * @return
     */
    int updateUser(User user);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szh.mybatis.mapper.UserMapper">
    <!-- 使用insert、update、delete、select标签编写sql语句 -->
    <sql id="userInfo">
        id, name, age
    </sql>
    <sql id="userTable">
        user
    </sql>
    <select id="selectUserById" resultType="com.szh.mybatis.bean.User">
        select <include refid="userInfo"/>
        from <include refid="userTable"/>
        where id=#{uid}
    </select>
    <update id="updateUser" parameterType="com.szh.mybatis.bean.User">
        update <include refid="userTable"/>
        set name=#{name},age=#{age}
        where id=#{id}
    </update>
</mapper>

3.3 mybatis配置文件、工具类

这个可以参考这篇文章,这两个代码是一样的。

https://blog.csdn.net/weixin_43823808/article/details/118111961


3.4 测试方法

3.4.1 同一个会话对象查询同一个数据

    @Test
    public void test01() {
        SqlSession session=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        System.out.println("==============================");
        User user1 = userMapper.selectUserById(1001);
        System.out.println(user);
        session.close();
    }

可以看到我们new第一个对象时,它去数据库中查询了一次,执行了一次select语句;而我们new第二个对象时,这个时候,它就没有再进数据库,没有执行select语句,因为面对同一个SqlSeesion会话对象,第一次查询之后,这条数据就已经存在于缓存中了,所以第二次查询直接去缓存中拿就可以了。


3.4.2 同一个会话对象查询两个不同的数据

    @Test
    public void test02() {
        SqlSession session=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        System.out.println("==============================");
        User user1 = userMapper.selectUserById(1002);
        System.out.println(user);
        session.close();
    }


这个肯定是执行两次select语句了,因为虽然是同一个SqlSession会话对象,但这查询的是两条不同的数据,所以自然会执行两次sql语句,可以理解为缓存中此时有10011002这两条数据了。


3.4.3 insertupdatedelete操作会刷新缓存

    @Test
    public void test03() {
        SqlSession session=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        int rows=userMapper.updateUser(new User(1002,"小宋",21));
        System.out.println("==============================");
        User user1 = userMapper.selectUserById(1001);
        System.out.println(user);
        session.close();
    }


这里虽然是面对同一个SqlSession会话对象,然后查询的也是同一个数据,但是中间执行了一次update修改操作,所以这时候会刷新缓存,当你第二次再查询1001数据时,缓存已经被刷新了,就没有1001这条数据了,所以两次查询都会去数据库中查找。


3.4.4 手动清理缓存

    @Test
    public void test04() {
        SqlSession session=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        session.clearCache();//手动清理缓存
        System.out.println("==============================");
        User user1 = userMapper.selectUserById(1001);
        System.out.println(user);
        session.close();
    }


这个就不再解释了,因为你都手动刷新缓存了,所以select两次,肯定两次都会去数据库中查找,因为缓存被刷新了。

4.二级缓存案例讲解


二级缓存也叫全局缓存,因为之前一级缓存的作用域太低了,所以就诞生了二级缓存。

二级缓存是基于namespace级别的缓存,一个命名空间(namespace)对应一个二级缓存。

工作机制:

·       一个会话对象(SqlSession)查询一条数据,这个数据就会被存放在当前会话的一级缓存中。

·       如果当前会话关闭了,这个会话对应的一级缓存就没了;只有当前会话关闭或者提交了,一级缓存中的数据才会被保存到二级缓存中。那么此时新的会话查询数据,就可以从二级缓存中获取数据。

·       不同的mapper具有不同的namespace,查出的数据自然也会放在自己对应的缓存中。

·       只要开启了二级缓存,在同一个mapper下就有效。


4.1 开启二级缓存之后,实体类需要实现序列化

package com.szh.mybatis.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 *
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    @Serial
    private static final long serialVersionUID = 8349440997686871823L;
    private Integer id;
    private String name;
    private Integer age;
}

4.2 dao接口、mapper文件、myabtis配置文件

dao接口和上面一级缓存案例的一样。

mapper文件中添加了一个 <cache/> 标签,其他都一样。

<cache/>

mybatis配置文件中多了这一行。

 <!--显式的开启缓存-->
 <setting name="cacheEnabled" value="true"/>

4.3 测试方法

4.3.1 一级缓存面对两个SqlSession会话对象

先把mapper文件中的 <cache/> 标签注释掉。

    @Test
    public void test05() {
        SqlSession session=MyBatisUtil.getSqlSession();
        SqlSession session2=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        UserMapper userMapper2=session2.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        session.close();
        System.out.println("==============================");
        User user2 = userMapper2.selectUserById(1001);
        System.out.println(user2);
        session2.close();
    }


可以看到,我们在测试方法中创建了两个会话对象(SqlSeesion),那么在第一个会话对象close关闭之后,一级缓存就没了,所以第二个会话对象肯定还会去数据库中查找数据啊。


4.3.2 二级缓存面对两个SqlSession会话对象

先把mapper文件中的 <cache/> 标签打开。测试方法和上面的一样。

我们来看下面的执行结果。


这个时候,我们开启了二级缓存,那就不一样了啊,你第一个会话对象select查询之后,执行close关闭了,此时二级缓存会发挥作用,它会将你一级缓存(对应第一个会话对象)中查询到的那个数据存放到二级缓存中,这个时候,第二个会话对象再执行select,就不用去数据库中查找了,因为这个数据已经存在在二级缓存中了,直接拿数据就可以了。所以自始至终,只用执行一次select语句。

 

5.MyBatis缓存原理



默认开启一级缓存,之后使用<cache/>开启二级缓存。测试。

    @Test
    public void test06() {
        SqlSession session=MyBatisUtil.getSqlSession();
        SqlSession session2=MyBatisUtil.getSqlSession();
        UserMapper userMapper=session.getMapper(UserMapper.class);
        UserMapper userMapper2=session2.getMapper(UserMapper.class);
        //查询1001,此时数据库缓存中什么都没有,查询1001,直接去数据库中查找,执行sql语句
        User user = userMapper.selectUserById(1001);
        System.out.println(user);
        session.close(); //关闭第一个会话对象,此时1001数据会被转移到二级缓存中
        System.out.println("==============================");
        //查询1001,先去二级缓存中查找,有1001,直接从二级缓存中拿,不用再去数据库中查找,自然不会执行sql语句
        User user2=userMapper2.selectUserById(1001);
        System.out.println(user2);
        //查询1002,二级缓存中没有、一级缓存中也没有,所以直接去数据库中查找,执行sql语句
        User user3=userMapper2.selectUserById(1002);
        System.out.println(user2);
        System.out.println(user3);
        //查询1002,先去二级缓存中查找,有1002,直接从二级缓存中拿,不用再去数据库中查找,自然不会执行sql语句
        User user4=userMapper2.selectUserById(1002);
        System.out.println(user4);
        session2.close();
    }

相关文章
|
1月前
|
存储 缓存 芯片
让星星⭐月亮告诉你,当我们在说CPU一级缓存二级缓存三级缓存的时候,我们到底在说什么?
本文介绍了CPU缓存的基本概念和作用,以及不同级别的缓存(L1、L2、L3)的特点和工作原理。CPU缓存是CPU内部的存储器,用于存储RAM中的数据和指令副本,以提高数据访问速度,减少CPU与RAM之间的速度差异。L1缓存位于处理器内部,速度最快;L2缓存容量更大,但速度稍慢;L3缓存容量最大,由所有CPU内核共享。文章还对比了DRAM和SRAM两种内存类型,解释了它们在计算机系统中的应用。
74 1
|
2月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
5天前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
14天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
15天前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
43 5
|
26天前
|
缓存 JavaScript 前端开发
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
47 5
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
300 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
25天前
|
缓存 JavaScript 前端开发
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
17 1
|
30天前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6