Java课题笔记~ MyBatis缓存

news/2024/11/24 6:37:51/

为了减少重复查询给数据库带来的压力,MyBatis提供了缓存机制,这种机制能够缓存查询的结果,避免重复的查询。

MyBatis提供了两种缓存方式:

一种为针对于SqlSession的缓存【默认开启】

另一种为针对于全局的缓存【手动开启】

一级缓存存在SqlSession对象中;二级缓存横跨全部的SqlSession,对所有的查询都生效。

 

一、一级缓存(本地缓存)

在没有配置的情况下,MyBatis默认开启一级缓存

在实际开发时,使用同一个SqlSession对象调用同一个Mapper方法,往往只执行一次SQL,这是因为,当开启一级缓存时,第一次查询,MyBatis会将查询结果放在缓存中,当再次使用这个SqlSession进行同一个查询时,如果数据库的数据没有被更改,则直接将缓存中的数据返回,不会再次发送SQL到数据库

 

1.用户发送查询请求给MyBatis,MyBatis接收到请求时创建一个SqlSession对象处理本次请求的数据库操作,每个SqlSession对象有对应的执行器,执行器在执行SQL语句时会查询Local Cache中是否存在此查询的缓存,如果不存在,则执行此次查询,并将缓存放到Local Cache中;如果存在,则直接将此次查询的缓存返回。

2.当会话结束,即调用SqlSession的close()方法时,会释放此SqlSession中的所有缓存,并将此SqlSession禁用。如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存

在MyBatis中,对于两次查询,有以下四个条件来判定它们是否是完全相同的两次查询。 1)传入的statementId是否相同 2)查询时结果集范围是否相同 3)查询的最终SQL语句是否相同 4)传递给Statement的参数是否相同 当这些判断都相同时,认为这两次查询完全相同。

如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存

演示一级缓存的案例,实现步骤:

1、 添加MyBatis的依赖
    <properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><java.version>1.8</java.version></properties><dependencies><!-- mybatis的依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version></dependency><!-- mysql-connector-java 的依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!-- lombok 的依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><!--            <scope>provided</scope>--></dependency><!-- junit的依赖 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*</include></includes></resource></resources></build>
2、添加MyBatis的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--MyBatis的核心配置文件-->
<configuration><properties resource="jdbc.properties" /><settings><!--开启数据库日志检测--><setting name="logImpl" value="STDOUT_LOGGING"/></settings><!--类型别名--><typeAliases><package name="com.ambow.pojo" /></typeAliases><!--配置环境--><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><!--配置映射器--><mappers><!-- 4.将包内的映射器接口实现全部注册为映射器【推荐】 --><package name="com.ambow.dao" /></mappers>
</configuration>

核心配置文件,需要读取的jdbc.properties文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=root
3、创建POJO - 基于Lombok
package com.ambow.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {private int id;private String name;private int age;}
4、创建Mapper接口
//DogMapper.java
package com.ambow.dao;import com.ambow.pojo.Dog;public interface DogMapper {Dog selectDog();
}
5、创建Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--DogMapper.xml-->
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--MyBatis接口开发,需要修改namespace-->
<mapper namespace="com.ambow.dao.DogMapper"><select id="selectDog" resultType="dog">SELECT * from dog where id = 1</select></mapper>
6、创建MybatisUtil工具类
package com.ambow.util;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 java.io.IOException;
import java.io.InputStream;public class MyBatisUtil {//获取SqlSessionpublic static SqlSession getSqlSesssion(){//获取SqlSessionString resource = "mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}//获取SqlSessionFactory - 工厂对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        System.out.println(sqlSessionFactory);//获取SqlSession - 连接对象SqlSession sqlSession = sqlSessionFactory.openSession();return sqlSession;}//关闭SqlSessionpublic static void closeSqlSession(SqlSession session){if (session != null) {session.close();}}}
7、测试一级缓存
package com.ambow.test;import com.ambow.dao.DogMapper;
import com.ambow.pojo.Dog;
import com.ambow.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;public class CacheTest {/*一级缓存:SqlSession级别的缓存,也就是说,同一个SqlSession共用一个缓存对象*/@Testpublic void test01(){//获取SqlSessionSqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();//第一次查询 - id为1DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);Dog dog1 = dogMapper01.selectDog();System.out.println(dog1);//第二次查询 - id为1DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);Dog dog2 = dogMapper02.selectDog();System.out.println(dog2);}/*一级缓存:两个SqlSession对象,不会共用一个缓存对象*/@Testpublic void test02(){//获取SqlSessionSqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();SqlSession sqlSesssion02 = MyBatisUtil.getSqlSesssion();//第一次查询 - id为1DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);Dog dog1 = dogMapper01.selectDog();System.out.println(dog1);//第二次查询 - id为1DogMapper dogMapper02 = sqlSesssion02.getMapper(DogMapper.class);Dog dog2 = dogMapper02.selectDog();System.out.println(dog2);}/*一级缓存:SqlSession调用close()方法,缓存会被释放*/@Testpublic void test03(){//获取SqlSessionSqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();//第一次查询 - id为1DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);Dog dog1 = dogMapper01.selectDog();System.out.println(dog1);sqlSesssion01.close();//第二次查询 - id为1DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);Dog dog2 = dogMapper02.selectDog();System.out.println(dog2);}/*一级缓存:调用SqlSession的clearCache()方法,可以释放缓存*/@Testpublic void test04(){//获取SqlSessionSqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();//第一次查询 - id为1DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);Dog dog1 = dogMapper01.selectDog();System.out.println(dog1);//清除缓存sqlSesssion01.clearCache();//第二次查询 - id为1DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);Dog dog2 = dogMapper02.selectDog();System.out.println(dog2);}/*一级缓存:当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存*/@Testpublic void test05(){//获取SqlSessionSqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();//第一次查询 - id为1DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);Dog dog1 = dogMapper01.selectDog();System.out.println(dog1);//执行DML操作 - 数据更新int row = dogMapper01.updateDog();System.out.println("执行了更新语句");//第二次查询 - id为1DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);Dog dog2 = dogMapper02.selectDog();System.out.println(dog2);}
}

二、二级缓存(全局缓存)

MyBatis的二级缓存是Application级别的缓存,与一级缓存的原理类似。

不同的是,二级缓存的作用域扩大到了每个命名空间,在同一个命名空间中的所有查询都将被缓存。

MyBatis二级缓存的执行流程:

1、MyBatis中的二级缓存默认关闭,需要手动开启,当开启后,用户发送有关数据库操作的请求会被CacheExecutor拦截。

2、CacheExecutor拦截数据库操作后,到Configuration对象中查看对应命名空间中的缓存,如果发现存在相同查询的缓存,则直接返回该缓存;如果不存在,则进入一级缓存中查找。

即先经过二级缓存查找后,再从一级缓存中寻找。

MyBatis在执行到DML语句时,会清空当前命名空间中所有的缓存。此外,MyBatis开启二级缓存后可能会有脏读问题:按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。  

演示案例

演示1:不开启二级缓存,一级缓存无法实现跨SqlSession之间的缓存。

演示2:开启二级缓存,可以实现跨SqlSession的缓存。

<!--设置 --><settings><!--缓存二级配置的全局开关--><setting name="cacheEnabled" value="true" /><!--开启数据库日志检测--><setting name="logImpl" value="STDOUT_LOGGING"/></settings>

使用MyBatis的二级缓存,需要以下几步:

  1. 在主配置文件中开启全局二级缓存配置

    <setting name="cacheEnabled" value="true">

  2. 在映射文件中加入<cache />标签

  3. 对应的pojo需要实现序列化

  4. 注意:测试二级缓存需要commit提交,如果不提交是不会保存到二级缓存的

演示3:执行DML操作后,二级缓存会清空。

演示4:在不规范开发时,二级缓存会出现脏读情况。

  • 按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。


http://www.ppmy.cn/news/998756.html

相关文章

C++ float计算精度和输出为string

文章目录 float 精度和转换成str float 精度和转换成str 控制float 进度输出string #include <iostream> #include <iomanip> #include <sstream>int main() {uint32_t value_1 6458;uint64_t value_2 127788;float result static_cast<float>(va…

ELK高级搜索(一)

文章目录 ELK搜索1&#xff0e;简介1.1 内容1.2 面向 2&#xff0e;Elastic Stack2.1 简介2.2 特色2.3 组件介绍 3&#xff0e;Elasticsearch3.1 搜索是什么3.2 数据库搜索3.3 全文检索3.4 倒排索引3.5 Lucene3.6 Elasticsearch3.6.1 Elasticsearch的功能3.6.2 Elasticsearch使…

青少年软件编程(Python六级)等级考试试卷(2022年9月)

青少年软件编程&#xff08;Python六级&#xff09;等级考试试卷&#xff08;2022年9月&#xff09; 第 1 题 单选题 以下关于Python二维数据的描述中&#xff0c;错误的是&#xff1f;&#xff08; &#xff09; A. 表格数据属于二维数据&#xff0c;由整数索引的数据构成 …

# Windows 环境下载 Android 12源码

前言 Android 官网&#xff08;该方式不适合 Windows 平台&#xff09;&#xff1a;https://source.android.com/source/downloading.html (备注自 2021 年 6 月 22 日起&#xff0c;安卓操作系统不再支持在 Windows 或 MacOS 上进行构建&#xff0c;如果要编译源码推荐先安装…

性能测试/负载测试/压力测试之间的区别

做测试一年多来&#xff0c;虽然平时的工作都能很好的完成&#xff0c;但最近突然发现自己在关于测试的整体知识体系上面的了解很是欠缺&#xff0c;所以&#xff0c;在工作之余也做了一些测试方面的知识的补充。不足之处&#xff0c;还请大家多多交流&#xff0c;互相学习。 …

AI 3D结构光技术加持,小米引领智能门锁新标准

一直以来&#xff0c;小米智能门锁系列产品让更多家庭走进了安全便捷的智能生活&#xff0c;安全至上的设计让很多家庭都轻松告别了随身钥匙。 7月27日&#xff0c;小米正式推出小米智能门锁M20 Pro&#xff0c;再一次引领智能门锁产品的发展潮流。该款门锁采用AI 3D结构光技术…

“从零开始学习Spring Boot:构建高效的Java应用程序“

标题&#xff1a;从零开始学习Spring Boot&#xff1a;构建高效的Java应用程序 摘要&#xff1a;本篇博客将带你从零开始学习如何使用Spring Boot构建高效的Java应用程序。我们将讨论Spring Boot的基本概念和特性&#xff0c;并提供一个简单的示例代码来帮助你入门。 正文&am…

【java安全】CommonsBeanUtils1

文章目录 【java安全】CommonsBeanUtils1前言Apache Commons BeanutilsBeanComparator如何调用BeanComparator#compare()方法&#xff1f;构造POC完整POC 调用链 【java安全】CommonsBeanUtils1 前言 在之前我们学习了java.util.PriorityQueue&#xff0c;它是java中的一个优…