• (十二)Mybatis的缓存机制


    Mybatis学习目录

    上一篇:(十一)MyBatis的高级映射及延迟加载
    下一篇:(十三)MyBatis的逆向工程

    环境

    数据库:汽车表t_car、班级表t_clazz
    引⼊依赖:mysql驱动依赖、mybatis依赖、logback依赖、junit依赖。
    引入配置文件:jdbc.properties、mybatis-config.xml、logback.xml
    pojo类:Car
    SqlSession工具类:SqlSessionUtil
    都可以复制之前的

    Mybatis的缓存

    缓存的作用:通过减少IO的方式,来提⾼程序的执行效率。
    Mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO。另一方面不再执行繁琐的查找算法。效率大大提升。
    Mybatis缓存包括:

    • ⼀级缓存:将查询到的数据存储到SqlSession中。
    • ⼆级缓存:将查询到的数据存储到SqlSessionFactory中。

    或者集成其它第三方的缓存:比如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】等。
    缓存只针对于DQL语句,也就是说缓存机制只对应select语句。

    一级缓存

    ⼀级缓存默认是开启的。不需要做任何配置。
    原理:只要使用同⼀个SqlSession对象执行同⼀条SQL语句,就会走缓存。
    创建CarMapper接口,添加方法:

    	Car selectById(Long id);
    
    • 1

    创建CarMapper.xml配置:

    	<select id="selectById" resultType="car">
    
            select *
            from t_car
            where id = #{id}
                
        </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试同一个SqlSession:

        @Test
        public void testSelectByIdCache1() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session = sqlSessionFactory.openSession();
    		//同一个session情况下
            CarMapper mapper = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
            Car car1 = mapper.selectById(1L);
            System.out.println(car);
            System.out.println(car1);
            session.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行发现走一级缓存,同一条查询语句不会执行两次
    请添加图片描述
    测试不同SqlSession:

        @Test
        public void testSelectByIdCache1() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session = sqlSessionFactory.openSession();
    
            CarMapper mapper = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
            //Car car1 = mapper.selectById(1L);
            SqlSession session1 = sqlSessionFactory.openSession();
            CarMapper mapper1 = session1.getMapper(CarMapper.class);
            Car car1 = mapper1.selectById(1L);
            System.out.println(car);
            System.out.println(car1);
            session.close();
            session1.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行发现,执行两次查询语句,可见一级缓存是存储在SqlSession当中
    请添加图片描述

    一级缓存失效

    什么情况下不走缓存?
    第⼀种:不同的SqlSession对象。
    第⼆种:查询条件变化了。
    一级缓存失效:

    • 方式一,两条查询语句之间执行清空缓存sqlSession.clearCache()
    • 方式二:两条查询语句之间对任意表执行增删改操作,
    方式一

    测试程序:
    两条查询语句之间执行清空缓存

    @Test
        public void testSelectByIdCache1() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session = sqlSessionFactory.openSession();
    
            CarMapper mapper = session.getMapper(CarMapper.class);
            //CarMapper mapper1 = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
    
            //缓存失效
            //方式一,两条查询语句之间执行sqlSession.clearCache()
            session.clearCache();
            
    
            Car car1 = mapper.selectById(1L);
    
    
            /*SqlSession session1 = sqlSessionFactory.openSession();
            CarMapper mapper1 = session1.getMapper(CarMapper.class);
            Car car1 = mapper1.selectById(1L);*/
            System.out.println(car);
            System.out.println(car1);
            session.close();
            //session1.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行发现不走缓存
    请添加图片描述

    方式二

    两条查询语句之间对任意表执行增删改操作
    CarMapper接口新增方法:

    	int insertClazz(@Param("cid") Integer cid, @Param("cname")String cname);
    
    • 1

    CarMapper.xml配置:

        <insert id="insertClazz">
            insert into t_clazz values(#{cid},#{cname})
        </insert>
    
    • 1
    • 2
    • 3

    测试程序:

        @Test
        public void testSelectByIdCache1() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session = sqlSessionFactory.openSession();
    
            CarMapper mapper = session.getMapper(CarMapper.class);
            //CarMapper mapper1 = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
    
            //缓存失效
            //方式一,两条查询语句之间执行sqlSession.clearCache()
            //session.clearCache();
            //方式二:两条查询语句之间对任意表执行增删改操作,
            mapper.insertClazz(2000,"高三三班");
    
            Car car1 = mapper.selectById(1L);
    
    
            /*SqlSession session1 = sqlSessionFactory.openSession();
            CarMapper mapper1 = session1.getMapper(CarMapper.class);
            Car car1 = mapper1.selectById(1L);*/
            System.out.println(car);
            System.out.println(car1);
            session.close();
            //session1.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    请添加图片描述

    二级缓存

    ⼆级缓存的范围是SqlSessionFactory。
    使用二级缓存必须具备三个条件:

    1. 默认情况下,二级缓存是开启的,只要在对应的映射文件加上cache标签,就表示这个映射使用二级缓存

      • eviction属性:表示指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。

        • a. LRU:Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有⼀种淘汰算法LFU,最不常⽤。)

        • b. FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊二级缓存的对象最先被淘汰。

        • c. SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。

        • d. WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。

      • flushInterval属性:二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存足够大,⼀直会向二级缓存中缓存数据。除⾮执⾏了增删改。

      • readOnly属性:默认值是 false

        • true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能会存在安全问题。

        • false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安全。

      • size属性:设置二级缓存中最多可存储的java对象数量。默认值1024。

      • type属性:集成第三方缓存

    2. 使用⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口

    /**
     * 封装汽车信息的pojo类,普通的java类
     * 使用二级缓存的实体类对象必须是可序列化的,必须实现Serializable
     */
    public class Car implements Serializable {
    
        private Long id;
        private String carNum;
        private String brand;
        private Double guidePrice;
        private String produceTime;
        private String carType;
    
        public Car() {
        }
    
        public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
            this.id = id;
            this.carNum = carNum;
            this.brand = brand;
            this.guidePrice = guidePrice;
            this.produceTime = produceTime;
            this.carType = carType;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getCarNum() {
            return carNum;
        }
    
        public void setCarNum(String carNum) {
            this.carNum = carNum;
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public Double getGuidePrice() {
            return guidePrice;
        }
    
        public void setGuidePrice(Double guidePrice) {
            this.guidePrice = guidePrice;
        }
    
        public String getProduceTime() {
            return produceTime;
        }
    
        public void setProduceTime(String produceTime) {
            this.produceTime = produceTime;
        }
    
        public String getCarType() {
            return carType;
        }
    
        public void setCarType(String carType) {
            this.carType = carType;
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "id=" + id +
                    ", carNum='" + carNum + '\'' +
                    ", brand='" + brand + '\'' +
                    ", guidePrice=" + guidePrice +
                    ", produceTime='" + produceTime + '\'' +
                    ", carType='" + carType + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    1. SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写入到⼆级缓存当中。

    使用一级缓存的需求测试程序:

    	@Test
        public void testSelectByIdCache2() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    
            SqlSession session = sqlSessionFactory.openSession();
    
            CarMapper mapper = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
            System.out.println(car);
            //关键一步
            session.close();
    
            SqlSession session1 = sqlSessionFactory.openSession();
            CarMapper mapper1 = session1.getMapper(CarMapper.class);
            Car car1 = mapper1.selectById(1L);
            System.out.println(car1);
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    请添加图片描述

    MyBatis集成EhCache缓存

    集成EhCache是为了代替mybatis⾃带的⼆级缓存。⼀级缓存是无法替代的。
    mybatis对外提供了接⼝,也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。
    EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常见,按照以下步骤操作,就可以完成集成:

    第⼀步:引入Mybatis整合ehcache的依赖。

    	<!--mybatis集成ehcache的组件-->
        <dependency>
          <groupId>org.mybatis.caches</groupId>
          <artifactId>mybatis-ehcache</artifactId>
          <version>1.2.2</version>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第⼆步:在类的根路径下新建echcache.xml文件,并提供以下配置信息。

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
        <!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
        <diskStore path="e:/ehcache"/>
    
        <!--defaultCache:默认的管理策略-->
        <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有
       效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
        <!--maxElementsInMemory:在内存中缓存的element的最⼤数⽬-->
        <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
        <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-
       ->
        <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多⻓时间没有被访问就会失
       效。只对eternal为false的有效。默认值0,表示⼀直可以访问-->
        <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。
       只对eternal为false的有效。默认值0,表示⼀直可以访问-->
        <!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
        <!--FIFO:first in first out (先进先出)-->
        <!--LFULess Frequently Used (最少使⽤).意思是⼀直以来最少被使⽤的。缓存的元
       素有⼀个hit 属性,hit 值最⼩的将会被清出缓存-->
        <!--LRULeast Recently Used(最近最少使⽤). (ehcache 默认值).缓存的元素有⼀个时间戳,当缓存容量满了,⽽⼜需要腾出地⽅来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存-->
        <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                      timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
    </ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    第三步:修改SqlMapper.xml⽂件中的标签,添加type属性。

    <cache  type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
    
    • 1

    第四步:编写测试程序使用。

        @Test
        public void testSelectByIdEhcache() throws Exception{
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    
            SqlSession session = sqlSessionFactory.openSession();
    
            CarMapper mapper = session.getMapper(CarMapper.class);
            Car car = mapper.selectById(1L);
            System.out.println(car);
            session.close();
    
            SqlSession session1 = sqlSessionFactory.openSession();
            CarMapper mapper1 = session1.getMapper(CarMapper.class);
            Car car1 = mapper1.selectById(1L);
            System.out.println(car1);
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    请添加图片描述

    请添加图片描述

  • 相关阅读:
    使用React-Query解决接口请求
    linux服务端c++开发工具介绍(vscode版)
    Jira - create project
    【ARM-0】基本概念
    docker 安装 logstash
    source 命令的用法(与 sh Filename、./Filename的区别)
    1km栅格数据重采样成30m,为什么感觉仍不清晰#GIS
    SpringMVC入门
    【数据结构】堆(万字详解)
    中间件(三)- Kafka(二)
  • 原文地址:https://blog.csdn.net/weixin_45832694/article/details/127730300