ToB企服应用市场:ToB评测及商务社交产业平台

标题: MyBatis 的缓存机制 [打印本页]

作者: 卖不甜枣    时间: 2024-6-16 14:19
标题: MyBatis 的缓存机制
1. MyBatis 的缓存机制

@
目录

缓存(Cache)
缓存的作用:通过减少 IO 的方式,来进步程序的实行效率
MyBatis 的缓存:将 Select 语句的查询结果放到缓存(内存)当中,下一次还是这条  Select 语句的话,直接就从缓存当中取了,不再查询数据库。这样一方面减少了 IO,另一方面不再实行繁琐的查找算法。效率大大提拔。
MyBatis 缓存包括:
留意:缓存只针对于 DQL(查询)语句,也就是说缓存机制只对应 Select 语句。
一旦你实行了,insert 或者delete或者 update 更新语句,无论是否是更新修改删除那个数据表中的记录,都会清空缓存,所以,这样就不会导致 缓存当中的 select 语句的数据是:旧的无用的数据了。
2. 准备工作

数据表结构的设计,数据表名为:t_car

t_car 表中的数据信息:

在pom.xml 文件当中设置相干的依赖的 jar 包如下:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <modelVersion>4.0.0</modelVersion>
  6.     <groupId>com.rainbowsea</groupId>
  7.     <artifactId>mybatis-005-crud-blog</artifactId>
  8.     <version>1.0-SNAPSHOT</version>
  9.     <properties>
  10.         <maven.compiler.source>17</maven.compiler.source>
  11.         <maven.compiler.target>17</maven.compiler.target>
  12.     </properties>
  13.     <dependencies>
  14.         
  15.         <dependency>
  16.             <groupId>org.mybatis</groupId>
  17.             <artifactId>mybatis</artifactId>
  18.             <version>3.5.10</version>
  19.         </dependency>
  20.         
  21.         <dependency>
  22.             <groupId>mysql</groupId>
  23.             <artifactId>mysql-connector-java</artifactId>
  24.             <version>8.0.30</version>
  25.         </dependency>
  26.         <dependency>
  27.             <groupId>junit</groupId>
  28.             <artifactId>junit</artifactId>
  29.             <version>4.13.2</version>
  30.             <scope>test</scope>
  31.         </dependency>
  32.         
  33.         <dependency>
  34.             <groupId>ch.qos.logback</groupId>
  35.             <artifactId>logback-classic</artifactId>
  36.             <version>1.2.11</version>
  37.         </dependency>
  38.     </dependencies>
  39. </project>
复制代码
设置 logback 的设置文件,用于打印显示,我们的日志信息,方便我们查看我们的运行过程,结果。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration debug="false">
  3.    
  4.     <appender name="STDOUT" >
  5.         <encoder >
  6.             
  7.             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  8.         </encoder>
  9.     </appender>
  10.    
  11.     <logger name="com.apache.ibatis" level="TRACE"/>
  12.     <logger name="java.sql.Connection" level="DEBUG"/>
  13.     <logger name="java.sql.Statement" level="DEBUG"/>
  14.     <logger name="java.sql.PreparedStatement" level="DEBUG"/>
  15.    
  16.     <root level="DEBUG">
  17.         <appender-ref ref="STDOUT"/>
  18.         <appender-ref ref="FILE"/>
  19.     </root>
  20. </configuration>
复制代码
设置 MyBatis 的核心设置文件,
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.         
  7.         <package name="com.rainbowsea.mybatis.pojo"/>
  8.     </typeAliases>
  9.     <environments default="mybatis">
  10.         <environment id="mybatis">
  11.             
  12.             <transactionManager type="JDBC"/>
  13.             <dataSource type="POOLED">
  14.                 <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  15.                 <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
  16.                 <property name="username" value="root"/>
  17.                 <property name="password" value="MySQL123"/>
  18.             </dataSource>
  19.         </environment>
  20.     </environments>
  21.     <mappers>
  22.         
  23.         <package name="com.rainbowsea.mybatis.mapper"></package>
  24.     </mappers>
  25. </configuration>
复制代码
对照 t_car 创建的ORM 映射的 Car 类
留意:在MyBatis 当中对应的ORM ,一样平常在框架里对应的 Bean实体类,一定要实现该 set 和 get 方法以及无参数构造方法,无法框架无法使用反射机制,举行操作
建议用包装类,这样可以防止 Null的问题,因为(简单类型 int num = null ,是不可以赋值为 null)的编译无法通过
  1. package com.rainbowsea.mybatis.pojo;
  2. public class Car {
  3.     // 数据库表当中的字段应该和pojo类的属性一一对应
  4.     // 建议使用包装类,这样可以防止null的问题
  5.     private Long id;
  6.     private String carNum;
  7.     private String brand;
  8.     private Double guidePrice;
  9.     private String produceTime;
  10.     private String carType;
  11.     public Car() {
  12.     }
  13.     public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
  14.         this.id = id;
  15.         this.carNum = carNum;
  16.         this.brand = brand;
  17.         this.guidePrice = guidePrice;
  18.         this.produceTime = produceTime;
  19.         this.carType = carType;
  20.     }
  21.     @Override
  22.     public String toString() {
  23.         return "Car{" +
  24.                 "id=" + id +
  25.                 ", carNum='" + carNum + '\'' +
  26.                 ", brand='" + brand + '\'' +
  27.                 ", guidePrice=" + guidePrice +
  28.                 ", produceTime='" + produceTime + '\'' +
  29.                 ", catType='" + carType + '\'' +
  30.                 '}';
  31.     }
  32.     public Long getId() {
  33.         return id;
  34.     }
  35.     public void setId(Long id) {
  36.         this.id = id;
  37.     }
  38.     public String getCarNum() {
  39.         return carNum;
  40.     }
  41.     public void setCarNum(String carNum) {
  42.         this.carNum = carNum;
  43.     }
  44.     public String getBrand() {
  45.         return brand;
  46.     }
  47.     public void setBrand(String brand) {
  48.         this.brand = brand;
  49.     }
  50.     public Double getGuidePrice() {
  51.         return guidePrice;
  52.     }
  53.     public void setGuidePrice(Double guidePrice) {
  54.         this.guidePrice = guidePrice;
  55.     }
  56.     public String getProduceTime() {
  57.         return produceTime;
  58.     }
  59.     public void setProduceTime(String produceTime) {
  60.         this.produceTime = produceTime;
  61.     }
  62.     public String getcarType() {
  63.         return carType;
  64.     }
  65.     public void setcarType(String catType) {
  66.         this.carType = catType;
  67.     }
  68. }
复制代码
3. MyBatis 的一级缓存

一级缓存默认是开启的。不需要做任何设置。
原理:只要使用同一个SqlSession对象实行同一条SQL语句,就会走缓存。
一级缓存的内容是:将查询到的数据存储到 SqlSession 当中的。留意:其缓存的作用域
  1. package com.rainbowsea.mybatis.mapper;
  2. import com.rainbowsea.mybatis.pojo.Car;
  3. import com.rainbowsea.mybatis.pojo.Clazz;
  4. public interface CarMapper {
  5.     Car selectById(Long id);
  6. }
复制代码
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
  6.    
  7.     <select id="selectById" resultType="Car">
  8.         select id, car_num, brand, guide_price, produce_time, car_type
  9.         from t_car
  10.         where id = #{id}
  11.     </select>
  12. </mapper>
复制代码
运行测试:

  1.     @Test
  2.     public void testSelectById() throws IOException {
  3.         SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  4.         SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
  5.         SqlSession sqlSession = sqlSessionFactory.openSession();
  6.         CarMapper mapper = sqlSession.getMapper(CarMapper.class);
  7.         Car car = mapper.selectById(118L);
  8.         System.out.println(car);
  9.         CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
  10.         Car car1 = mapper1.selectById(118L);
  11.         System.out.println(car1);
  12.     }
复制代码


实行 Select 查询语句的时间,起首从对应的这个 Select 语句的 SqlSession 对象(一级缓存)当中查询是否有对应该Select 查询语句的缓存有的话,就不实行该 查询的 SQL 语句了,而是直接从一级缓存当中取出这个 Select 查询语句的数据结果。第一次实行  Select 语句(因为MyBatis 一级缓存默认是开启的)就会将存入到 sqlSession 对象(一级缓存)当中,方便后续的查询。
3.1 一级缓存失效环境/条件

一级缓存失效了,二级缓存同样也是失效的了,所以一级缓存失效的条件也是二级缓存失效的条件,他们的条件都是一样的。
思考:什么时间不走缓存?
一级缓存失效环境包括两种:
无论你是,你做了以上两件事的恣意一种,都会让一级缓存清空
第一种:第一次查询和第二次查询之间,手动清空了一级缓存。实行:实行了 sqlSession.clearCache()方法,这是手动环境缓存。
测试:

  1. package com.rainbowsea.mybatis.test;
  2. import com.rainbowsea.mybatis.mapper.CarMapper;
  3. import com.rainbowsea.mybatis.pojo.Car;
  4. import com.rainbowsea.mybatis.pojo.Clazz;
  5. import org.apache.ibatis.io.Resources;
  6. import org.apache.ibatis.session.SqlSession;
  7. import org.apache.ibatis.session.SqlSessionFactory;
  8. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  9. import org.junit.Test;
  10. import java.io.IOException;
  11. public class CarMapperTest {
  12.     /**
  13.      * 思考:什么时候不走缓存?
  14.      * sqlsession 对象不是同一个,肯定不走缓存
  15.      * 查询条件不一样,肯定不走缓存
  16.      * <p>
  17.      * 思考什么时候一级缓存失败?
  18.      * 第一次DQL和第二次DQL之间你做了一下两件事的任意一种,都会让一级缓存清空
  19.      * 1. 执行了 sqlSession.clearCache()方法,这是手动情况缓存
  20.      * 2. 执行了INSERT 或 DELETE 或UPDATE语句,不管你是操作那张表,都会清空一级缓存
  21.      *
  22.      * @throws IOException
  23.      */
  24.     @Test
  25.     public void testSelectById3() throws IOException {
  26.         SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  27.         SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
  28.         SqlSession sqlSession = sqlSessionFactory.openSession();
  29.         CarMapper mapper = sqlSession.getMapper(CarMapper.class);
  30.         Car car = mapper.selectById(118L);
  31.         System.out.println(car);
  32.         // 手动清空一级缓存
  33.         sqlSession.clearCache();
  34.         CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
  35.         Car car1 = mapper1.selectById(118L);
  36.         System.out.println(car1);
  37.         sqlSession.close();
  38.     }
  39. }
复制代码

  1. @Test
  2.     public void testSelectById3() throws IOException {
  3.         SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  4.         SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
  5.         SqlSession sqlSession = sqlSessionFactory.openSession();
  6.         CarMapper mapper = sqlSession.getMapper(CarMapper.class);
  7.         Car car = mapper.selectById(118L);
  8.         System.out.println(car);
  9. // 在这里执行 insert或者 delete 或者 update 中的任意一个语句,并且和表没有关系
  10.         CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
  11.         mapper2.insertClazz(new Clazz(2000,"高三三班"));
  12.         CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
  13.         Car car1 = mapper1.selectById(118L);
  14.         System.out.println(car1);
  15.         sqlSession.close();
  16.     }
复制代码
4. MyBatis 的二级缓存

二级缓存的范围是SqlSessionFactory。
二级缓存:将查询到的数据存储到 SqlSessionFactory 中,范围比一级缓存中更大一些。
使用二级缓存步调/条件
第一步: 要在 MyBatis 的核心设置文件当中,设置 全局性地开启或关闭所有映射器设置文件中已设置的任何缓存。默认就是true,无需设置
第二步: 在需要使用二级缓存的 对应的 SqlMapper.xml  文件中添加设置:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
  6.     <cache></cache>
  7.     <insert id="insertClazz">
  8.         insert into t_clazz values (#{cid},#{cname})
  9.     </insert>
  10. </mapper>
复制代码
第三步: 使用二级缓存的实体类对象必须是可序列化的,也就是对应的POJO实体类,必须实现java.io.Serializable 接口

第四步: 只有 当 SqlSession对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用,否则没有提交/关闭,二级缓存是没有存储到数据信息的,是无效的。
  1. import com.rainbowsea.mybatis.mapper.CarMapper;
  2. import com.rainbowsea.mybatis.pojo.Car;
  3. import com.rainbowsea.mybatis.pojo.Clazz;
  4. import org.apache.ibatis.io.Resources;
  5. import org.apache.ibatis.session.SqlSession;
  6. import org.apache.ibatis.session.SqlSessionFactory;
  7. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  8. import org.junit.Test;
  9. import java.io.IOException;
  10. public class CarMapperTest {
  11.     @Test
  12.     public void testSelectById4() throws IOException {
  13.         // 这里只有一个SqlSessionFactory 对象,二级缓存对应的就是SqlSessionFactory
  14.         SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  15.         SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
  16.         SqlSession sqlSession1 = sqlSessionFactory.openSession();
  17.         SqlSession sqlSession2 = sqlSessionFactory.openSession();
  18.         CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
  19.         CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
  20.         // 这行代码执行结束之后,时间上数据缓存到一级缓存当中了,(sqlSession是一级缓存)
  21.         Car car = mapper1.selectById(118L);
  22.         System.out.println(car);
  23.         // 如果这里不关闭sqlSession对象的话,二级缓存中还是没有数据的
  24.         // 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中
  25.         sqlSession1.close();
  26.         // 这行代码执行结束之后,实际上数据会缓存到一级缓存当中。(sqlSession2 是一级缓存)
  27.         Car car1 = mapper2.selectById(118L);
  28.         System.out.println(car1);
  29.         // 程序执行到这里的时候,会有SqlSession1这个一级缓存中的数据写入到二级缓存当中
  30.         // sqlSession1.close()
  31.         // 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中
  32.         sqlSession2.close();
  33.     }
  34. }
复制代码
运行测试:

二级缓存的失效:只要两次查询之间出现了增删改操作。二级缓存就会失效。【一级缓存也会失效】
二级缓存的相干设置:

5. MyBatis 集成 EhCache 第三方缓存

集成EhCache是为了代替mybatis自带的二级缓存。一级缓存是无法替代的。
mybatis对外提供了接口,也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。
EhCache是Java写的。Memcache是C语言写的。所以mybatis集成EhCache较为常见,按照以下步调操作,就可以完成集成:
第一步: 引入mybatis 整合 ehcache 的依赖。
  1. <dependency>
  2.   <groupId>org.mybatis.caches</groupId>
  3.   <artifactId>mybatis-ehcache</artifactId>
  4.   <version>1.2.2</version>
  5. </dependency>
复制代码

第二步: 在类的根路径下新建 echcache.xml (文件名必须是:echcache.xml 不可以修改)文件,并提供以下设置信息。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
  4.          updateCheck="false">
  5.    
  6.     <diskStore path="e:/ehcache"/>
  7.    
  8.    
  9.    
  10.    
  11.    
  12.    
  13.    
  14.    
  15.    
  16.    
  17.    
  18.     <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
  19.                   timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
  20. </ehcache>
复制代码

第三步: 修改对应的 SqlMapper.xml文件中的标签,添加type属性。
  1. [/code][align=center][img]https://img2024.cnblogs.com/blog/3084824/202406/3084824-20240616145607862-1020905241.png[/img][/align]
  2. [code]    <?xml version="1.0" encoding="UTF-8" ?>
  3. <!DOCTYPE mapper
  4.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  5.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  6. <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
  7.    
  8.     <select id="selectById" resultType="Car">
  9.         select id, car_num, brand, guide_price, produce_time, car_type
  10.         from t_car
  11.         where id = #{id}
  12.     </select>
  13. </mapper>
复制代码
第四步:编写测试程序使用。

  1. import com.rainbowsea.mybatis.mapper.CarMapper;
  2. import com.rainbowsea.mybatis.pojo.Car;
  3. import com.rainbowsea.mybatis.pojo.Clazz;
  4. import org.apache.ibatis.io.Resources;
  5. import org.apache.ibatis.session.SqlSession;
  6. import org.apache.ibatis.session.SqlSessionFactory;
  7. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  8. import org.junit.Test;
  9. import java.io.IOException;
  10. public class CarMapperTest {
  11.     @Test
  12.     public void testSelectById5() throws Exception {
  13.         // 这里只有一个SqlSessionFactory 对象,二级缓存对应的就是SqlSessionFactory
  14.         SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  15.         SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
  16.         SqlSession sqlSession1 = sqlSessionFactory.openSession();
  17.         CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
  18.         Car car1 = mapper1.selectById(118L);
  19.         System.out.println(car1);
  20.         sqlSession1.close();
  21.         SqlSession sqlSession2 = sqlSessionFactory.openSession();
  22.         CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
  23.         Car car2 = mapper2.selectById(118L);
  24.         System.out.println(car2);
  25.         
  26.         sqlSession2.close();
  27.     }
  28. }
复制代码
6. 总结:

7. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和复兴是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4