• Mybatis缓存



    前言

    内存:临时存储数据的空间,断电后消失
    硬盘:持久化的数据在硬盘当中。文件也存储在硬盘当中。关机然后重启硬盘中的文件还在。这是一个持久化的设备
    缓存(cache):提前把数据放到内存中,下一次用的时候,直接从缓存中拿。效率高
    实际上各大关系型数据库的数据,都是存储在文件当中
    缓存通常是程序开发中优化程序的重要手段:字符串常量池、线程池、连接池、整数型常量池……


    一、Mybatis缓存机制

    执行DQL(Data Query Language)–select语句的时候,将查询到的结果存放在缓存(内存)当中,如果下一次还是执行同样的dql语句,直接从缓存中拿数据,不再从硬盘上找数据了。
    目的:提高执行效率
    缓存机制:使用减少IO的方式来提高效率
    mybatis缓存包括:
    一级缓存:将查询到的数据存储到SqlSession当中
    二级缓存:将查询到的数据存储到SqlSessionFactory当中
    或者集成其他第三方的缓存

    补充
    SqlSessionFactory的生命周期:一个数据库一个SqlSessionFactory
    SqlSession的生命周期:当前的SQL会话

    缓存只针对于DQL语句,也就是说缓存机制只对应select语句

    二、一级缓存

    一级缓存默认是开启的,不需要做任何配置。
    原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存
    代码如下: 根据id查找对应的汽车信息
    接口:

    public interface CarMapper {
    
        /**
         * 根据id获取Car信息
         * @param id
         * @return
         */
        Car selectById(Long id);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CarMapper.xml

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

    测试

     @Test
        public void testCcarMapper() throws IOException {
            SqlSession sqlsession  = SqlSessionUtil.openSession();
            CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
            Car car = carMapper.selectById(1L);
            System.out.println(car);
            sqlsession.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    查询正常。
    在这里插入图片描述
    现在进行缓存测试,在测试类中再进行一次查找返回查找信息为car1

        @Test
        public void testCcarMapper() throws IOException {
            SqlSession sqlsession  = SqlSessionUtil.openSession();
            CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
            Car car = carMapper.selectById(1L);
            System.out.println(car);
            Car car1 = carMapper.selectById(1L);
            System.out.println(car1);
            sqlsession.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    根据日志信息提示只进行了一次SQL语句查询,是由于同一个mapper变量的原因?
    再进行测试:创建一个变量carMapper2 ,让carMapper2 去执行第二条SQL语句

        @Test
        public void testCcarMapper() throws IOException {
            SqlSession sqlsession  = SqlSessionUtil.openSession();
            CarMapper carMapper = sqlsession.getMapper(CarMapper.class);
            Car car = carMapper.selectById(1L);
            System.out.println(car);
    
            CarMapper carMapper2 = sqlsession.getMapper(CarMapper.class);
            Car car1 = carMapper2.selectById(1L);
            System.out.println(car1);
            sqlsession.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    两个carMapper 都是通过一个sqlsession对象获取的
    依然还是只进行了一次SQL语句查询
    在这里插入图片描述
    一级缓存存放在sqlSession中,那我们创建两个 sqlSession对象再去验证,
    答案是可以的。但是,在之前工具类中,使用了ThreadLocal
    SqlSessionUtil工具类:

    package com.powernode.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;
    
    /**
     * Mybatis工具类
     * @author bwy
     */
    public class SqlSessionUtil {
        static SqlSessionFactory factory = null;
        private SqlSessionUtil(){}
        static{
            try {
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //全局的服务器级别的 一个服务器当中定义一个即可,为了保证一个线程对应一个session
        //为什么把session对象放在ThreadLocal当中
        private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
        /**
         * 获取会话对象
         * @return
         * @throws IOException
         */
        public static SqlSession openSession() throws IOException {
             SqlSession session = local.get();
            if (session==null) {
                session = factory.openSession();
                //绑定 将session对象绑定到当前线程上
                local.set(session);
            }
            return session;
        }
        /**
         * 关闭session对象 (从当前线程中移除Session对象)
         * @param session
         */
        public static void close(SqlSession session){
            if (session!=null) {
                session.close();
                //注意移除session对象和当前线程的绑定关系
                //Tomcat服务器支持线程池, 也就是说用过的线程t1,可能下一次还会使用这个线程
                local.remove();
            }
        }
    }
    
    
    • 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

    因此,如果要获取两个sqlSession对象测试,就不能再使用工具类
    创建一个SqlSessionFactory可以创建多个SqlSession对象

       public void testCcarMapper2() throws IOException {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session1 = factory.openSession();
            CarMapper carMapper = session1.getMapper(CarMapper.class);
            Car car = carMapper.selectById(1L);
            System.out.println(car);
    
            SqlSession session2 = factory.openSession();
            CarMapper carMapper2 = session2.getMapper(CarMapper.class);
            Car car1 = carMapper2.selectById(1L);
            System.out.println(car1);
    
            session1.close();
            session2.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    执行了两次SQL语句,获取了两个SqlSesion就是两个缓存。

    思考
    1、什么时候不走缓存?
    SqlSession对象不是同一个,肯定不走缓存
    查询条件不一样肯定也不走缓存
    2、什么时候一级缓存失效?
    第一次DQL和第二次DQL之间做了以下两件事中的任意一件,都会让一级缓存清空:
    其一:执行了sqlSession的clearCache()方法,这是手动清空缓存。
    其二:执行了insert或delete或update语句,不管是操作哪张表的,都会清空一级缓存
    不做演示了。

    三、二级缓存

    二级缓存的范围是:SqlSessionFactory。
    使用二级缓存需要具备以下几个条件:

    1、< setting name=“cacheEnabled” value=“true”>全局性的开启或关闭所有映射器配置文件中已配置的任何缓存,默认就是true,无需设置
    2、在使用二级缓存的SqlMapper.xml文件中添加配置:< cache/>
    3、使用二级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口
    4、SqlSession对象关闭或提交后,一级缓存中的数据才会被写入到二级缓存当中,此时二级缓存才可用

    在这里插入图片描述
    在CarMapper.xml文件中添加标签

        
        <cache>cache>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    实体类实现java.io.Serializable接口

    public class Car implements Serializable{
    	……
    }
    
    • 1
    • 2
    • 3

    接口:

        /**
         * 根据id获取Car信息
         * 测试二级缓存
         * @param id
         * @return
         */
        Car selectById2(Long id);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    CarMapper.xml

        
        <cache>cache>
    <select id="selectById2" resultType="car">
        select * from t_car
        where id=#{id}
    select>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试:
    此时测试类中将数据放入两个一级缓存,当session1 session2 执行close之后数据才会被放入二级缓存当中。

        @Test
        public void testCcarMapper3() throws IOException {
            //这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session1 = factory.openSession();
            SqlSession session2 = factory.openSession();
            CarMapper carMapper = session1.getMapper(CarMapper.class);
            CarMapper carMapper2 = session2.getMapper(CarMapper.class);
    
            //这行结束之后,实际上数据是缓存到一级缓存当中了(session1)
            Car car = carMapper.selectById2(1L);
            System.out.println(car);
            //如果这里不关闭SqlSession对象的话,二级缓存中是没有数据的。
    
            //这行结束之后,实际上数据是缓存到一级缓存当中了(session2) 两个一级缓存不一样
            Car car1 = carMapper2.selectById2(1L);
            System.out.println(car1);
    
            //程序执行到这里会将session1这个一级缓存中的数据写入到二级缓存当中
            session1.close();
            //程序执行到这里会将session2这个一级缓存中的数据写入到二级缓存当中
            session2.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    应该会调用二次SQL语句:
    执行结果:
    在这里插入图片描述
    调整一下session1关闭的位置。

        @Test
        public void testCcarMapper3() throws IOException {
            //这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession session1 = factory.openSession();
            SqlSession session2 = factory.openSession();
            CarMapper carMapper = session1.getMapper(CarMapper.class);
            CarMapper carMapper2 = session2.getMapper(CarMapper.class);
    
            //这行结束之后,实际上数据是缓存到一级缓存当中了(session1)
            Car car = carMapper.selectById2(1L);
            System.out.println(car);
            //如果这里不关闭SqlSession对象的话,二级缓存中是没有数据的。
            //程序执行到这里会将session1这个一级缓存中的数据写入到二级缓存当中
            session1.close();
    
            //这行结束之后,实际上数据是缓存到一级缓存当中了(session2) 两个一级缓存不一样
            Car car1 = carMapper2.selectById2(1L);
            System.out.println(car1);
            
            //程序执行到这里会将session2这个一级缓存中的数据写入到二级缓存当中
            session2.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行结果:
    session1结束之后,执行了close将数据放在二级缓存当中。因此session2执行时直接去二级缓存中拿数据,不会再执行SQL语句。
    在这里插入图片描述
    只执行了一次SQL语句。

    二级缓存什么时候失效?
    只要两次查询之间出现了增删改操作,二级缓存就会失效【一级缓存也会失效】

    二级缓存的一些设置
    在这里插入图片描述

    四、Mybatis集成EhCache

    集成EhCache是为了代替mybatis自带的二级缓存,一级缓存是无法替代的。


  • 相关阅读:
    2021年9月电子学会图形化四级编程题解析含答案:小猫钓鱼
    公司里的VMware vSphere是用来干嘛的?—— vSphere服务器架构简单讲解
    反射_集合,,220816,
    【Java小白福利】Java面试、学习教程合集!
    记录paddlepaddle-gpu安装
    【设计模式】(二、)设计模式六大设计原则
    花费半年整理拼多多、饿了么、蚂蚁金服等大厂Java面试题大集合
    经验分析:数据可视化工具入门讲解,如何应用数据可视化
    企业架构LNMP学习笔记49
    @所有燃气企业,城燃企业数字化转型重点抓住的八个关键点
  • 原文地址:https://blog.csdn.net/qq_42338744/article/details/127695695