IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Mybatis缓存机制 -> 正文阅读

[大数据]Mybatis缓存机制

Mybatis 提供一级缓存和二级缓存的机制。
一级缓存是 SQLSession 级别的缓存在操作数据库时,每个 SqlSession 类的实例对象中有一个数据结构(HashMap)可以用于存储缓存数据。不同的 SqlSession 类的实例对象的数据区域(HashMap)是互不影响的。
二级缓存是 Mapper 级别的缓存,多个 SqlSession 类的实例对象操作同一个 Mapper 配置文件中的 SQL 语句,多个 SqlSession 类的实例对象可以共用二级缓存,二级缓存是跨 SqlSession 的。
Mybatis 的缓存模式如下图所示:
在这里插入图片描述
每个 SqlSession 类的实例对象自身有一个一级缓存,而查询同一个 Mapper 映射文件的 SqlSession 类的实例对象之间又共享同一个二级缓存。

一、一级查询缓存

一级查询缓存存在于每一个 SqlSession 类的实例对象中,当第一次查询某一个数据时,SqlSession 类的实例对象会将该数据存入一级缓存区域,在没有收到改变数据的请求之前,用户再次查询该数据,都会从缓存中获取该数据,而不是再次连接数据库进行查询。

1.1 一级缓存原理阐述

在这里插入图片描述
上图阐述了一个 SqlSession 类的实例对象下的一级缓存的工作原理。当第一次查询 id 为 1 的用户信息时,SqlSession 首先到一级缓存区域查询,如果没有相关数据,则从数据库查询。然后 SqlSession 将查询结果保存到一级缓存区域。在下一次查询时,如果 SqlSession 执行了 commit 操作(即执行了修改、添加和删除),则会清空它的一级缓存区域,以此来保证缓存中的信息是最新的,避免脏读现象发生。如果在这期间 SqlSession 一直没有执行 commit 操作修改数据,那么下一次查询 id 为 1 的用户信息时,SqlSession 在一级缓存中就会发现该信息,然后从缓存中获取用户信息。

1.2 一级缓存测试示例

Mybatis 默认支持一级缓存,不需要在配置文件中配置一级缓存的相关数据。
使用同一个 SqlSession 对象,对 id 为 1 的员工查询两次。

EmployeeCacheMapper.xml

<?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.example.mapper.EmployeeCacheMapper">

    <select id="getEmpById" resultType="com.example.pojo.Employee">
        select * from tbl_employee where id = #{id}
    </select>

</mapper>

EmployeeCacheMapper.java

package com.example.mapper;

import com.example.pojo.Employee;

public interface EmployeeCacheMapper {

    public Employee getEmpById(Integer id);
}

测试方法:

package com.example;

import com.example.mapper.EmployeeCacheMapper;
import com.example.pojo.Employee;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.InputStream;

public class MybatisCacheTest {

    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

    @Test
    public void test01() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
            Employee emp01 = mapper.getEmpById(1);
            System.out.println(emp01);
            Employee emp02 = mapper.getEmpById(1);
            System.out.println(emp02);
            System.out.println(emp01 == emp02);
        } finally {
            sqlSession.close();
        }
    }
}

控制台结果:

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
true

当第一次查询 id 为 1 的员工信息时,执行了 SQL 语句,而第二次查询 id 为 1 的员工信息时,没有任何的日志输出。而对于两次取出的员工对象的比对,也证明了第二次数据不是从数据库查询出来的,是从一级缓存中获取的。

1.3 一级缓存失效的四种情况

1、SqlSession 不同

测试语句如下:

@Test
public void test02() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try{
        EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        EmployeeCacheMapper mapper1 = sqlSession1.getMapper(EmployeeCacheMapper.class);
        Employee emp02 = mapper1.getEmpById(1);
        System.out.println(emp02);
        System.out.println(emp01 == emp02);
    } finally {
        sqlSession.close();
    }
}

控制台结果:

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
false

2、SqlSession 相同,查询条件不同

分别查询 id 为 1 和 id 为 2 个员工信息
测试方法:

 @Test
 public void test03() throws IOException {
     SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
     SqlSession sqlSession = sqlSessionFactory.openSession();
     try{
         EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
         Employee emp01 = mapper.getEmpById(1);
         System.out.println(emp01);
         Employee emp02 = mapper.getEmpById(2);
         System.out.println(emp02);
         System.out.println(emp01 == emp02);
     } finally {
         sqlSession.close();
     }
 }

控制台结果:

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 2(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=2, lastName='jerry', email='jerry@123.com', gender='0', department=null}
false

3、SqlSession 相同,两次查询之间执行了增删改操作

增删改操作可能会对数据有影响
EmployeeCacheMapper.xml:

<insert id="addEmp" databaseId="mysql" parameterType="employee" useGeneratedKeys="true" keyProperty="id">
    insert into tbl_employee (id, last_name, email, gender)
    values (#{id}, #{lastName}, #{email}, #{gender})
</insert>

EmployeeCacheMapper.java

package com.example.mapper;

import com.example.pojo.Employee;

public interface EmployeeCacheMapper {
    public void addEmp(Employee employee);
}

测试方法:

@Test
public void test04() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try{
        EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        Employee employee = new Employee();
        employee.setLastName("meichaofeng");
        employee.setEmail("meichaofeng@123.com");
        employee.setGender("0");
        mapper.addEmp(employee);
        Employee emp02 = mapper.getEmpById(1);
        System.out.println(emp02);
        System.out.println(emp01 == emp02);
    } finally {
        sqlSession.close();
    }
}

控制台结果:

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
[main] [com.example.mapper.EmployeeCacheMapper.addEmp]-[DEBUG] ==>  Preparing: insert into tbl_employee (id, last_name, email, gender) values (?, ?, ?, ?) 
[main] [com.example.mapper.EmployeeCacheMapper.addEmp]-[DEBUG] ==> Parameters: null, meichaofeng(String), meichaofeng@123.com(String), 0(String)
[main] [com.example.mapper.EmployeeCacheMapper.addEmp]-[DEBUG] <==    Updates: 1
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
false

4、SqlSession 相同,但手动清除了一级缓存

测试方法:

 @Test
 public void test05() throws IOException {
     SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
     SqlSession sqlSession = sqlSessionFactory.openSession();
     try{
         EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
         Employee emp01 = mapper.getEmpById(1);
         System.out.println(emp01);
         sqlSession.clearCache(); // 清空一级缓存
         Employee emp02 = mapper.getEmpById(1);
         System.out.println(emp02);
         System.out.println(emp01 == emp02);
     } finally {
         sqlSession.close();
     }
 }

控制台结果:

[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==>  Preparing: select * from tbl_employee where id = ? 
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] ==> Parameters: 1(Integer)
[main] [com.example.mapper.EmployeeCacheMapper.getEmpById]-[DEBUG] <==      Total: 1
Employee{id=1, lastName='tomcat', email='tom@123.com', gender='0', department=null}
false
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:37:32  更:2022-02-26 11:39:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:33:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码