zoukankan      html  css  js  c++  java
  • Mybatis:缓存和装饰器模式

    简介

    mybatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大地提升查询效率。

    Mybatis系统中默认定义了两级缓存。一级缓存和二级缓存。

    • 默认情况下,只有一级缓存(SqlSession级别地缓存,也成为本地缓存)开启
    • 二级缓存需要手动开启和配置,它是基于namespace级别地缓存
    • 为了提高扩展性,mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存。

    一级缓存

    又称本地缓存,与数据库同一次会话期间查询道德数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

    测试:

        @Test
        public void test01() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper= session.getMapper(EmployeeMapper.class);
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
                Employee emp2 = employeeMapper.getEmpById(1);
                System.out.println(emp2);
                System.out.println(emp==emp2);
            } finally {
                session.close();
            }
        }
    
        private SqlSessionFactory getSqlSessionFactory() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            return new SqlSessionFactoryBuilder().build(inputStream);
        }
    

    image-20210113205849101

    发现只查询了一次数据库。并且第二次查询和第一次查询得到地对象是同一个。

    一级缓存失效情况

    sqlSession不同

        @Test
        public void test01() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            SqlSession session2 = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper= session.getMapper(EmployeeMapper.class);
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
                EmployeeMapper employeeMapper2 = session2.getMapper(EmployeeMapper.class);
                Employee emp2 = employeeMapper2.getEmpById(1);
                System.out.println(emp2);
                System.out.println(emp==emp2);
            } finally {
                session.close();
            }
        }
    

    image-20210113210201507

    查询条件不同

    原因:一级缓存中不存在该缓存

        @Test
        public void test01() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper= session.getMapper(EmployeeMapper.class);
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
                Employee emp2 = employeeMapper.getEmpById(2);
                System.out.println(emp2);
                System.out.println(emp==emp2);
            } finally {
                session.close();
            }
        }
    

    image-20210113210439772

    增删改

    sqlSession相同,但是两次查询之间执行了增删改操作(尽管没有影响到原来缓存的数据)

        @Test
        public void test01() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper= session.getMapper(EmployeeMapper.class);
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
    
                employeeMapper.delEmpById(5);
    
                Employee emp2 = employeeMapper.getEmpById(2);
                System.out.println(emp2);
                System.out.println(emp==emp2);
            } finally {
                session.close();
            }
        }
    

    image-20210113210750107

    手动清空缓存

        @Test
        public void test01() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper= session.getMapper(EmployeeMapper.class);
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
    
                session.clearCache();
    
                Employee emp2 = employeeMapper.getEmpById(2);
                System.out.println(emp2);
                System.out.println(emp==emp2);
            } finally {
                session.close();
            }
        }
    

    image-20210113210950259

    二级缓存

    基于namespace级别的缓存,一个namespace对应一个二级缓存。

    工作机制:

    • 一个会话,查询一条数据,这个数据就会被放到当前会话的一级缓存中

    • 只有会话关闭,一级缓存中的数据才会被转移到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容

    • sqlSession=>EmployeeMapper=>Employee

      不同namespace查出的数据会放在自己对应的缓存中(map)

    使用:

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

    注意:该配置控制的是二级缓存,对一级缓存没有影响

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

    2.在sql映射文件配置使用二级缓存

    <mapper namespace="com.wj.dao.EmployeeMapper">
    
        <cache eviction="FIFO"
               flushInterval="6000"
               readOnly="false"
               size="1024"/>
        ....
    </mapper>
    

    常用配置项说明:

    eviction:缓存的回收策略

    • LRU:最近最少使用(默认使用)
    • FIFO:先进先出
    • SOFT:软引用:基于垃圾回收器状态和软引用规则的对象
    • WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象

    flushInterval:缓存刷新间隔,默认不清空,设置毫秒值

    readOnly:缓存是否只读

    • true:只读,mybatis认为所以从缓存中获取数据的操作都是只读操作,不会修改数据,mybatis为了较快获取速度,直接就会将数据缓存中的引用交给用户,不安全但速度快。
    • false:非只读,mybatis觉得获取的数据可能会被修改,mybatis会利用序列化&反序列化技术克隆一份新的数据给用户,安全(默认)

    size:缓存存放多少元素

    type:指定自定义缓存的全类型,需要实现Cache接口即可。

    3.我们的实体类需要实现序列化接口

    @Data
    @ToString
    public class Employee implements Serializable {
        private Integer id;
        private String lastName;
        private String email;
        private String gender;
    }
    
    

    测试:

        @Test
        public void testSecondLevel() throws IOException {
            //1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            SqlSession session2 = sqlSessionFactory.openSession();
            try {
                EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
                EmployeeMapper employeeMapper2 = session2.getMapper(EmployeeMapper.class);
    
                Employee emp = employeeMapper.getEmpById(1);
                System.out.println(emp);
                session.close();
                System.out.println("===============关闭session================");
                Employee emp2 = employeeMapper2.getEmpById(1);
                System.out.println(emp2);
                session2.close();
            }finally {
    
            }
        }
    

    image-20210113213851469

    注意:

    只有之前的sqlSession关闭了,才有二级缓存,否则不会进入二级缓存。

    缓存相关属性/设置

    1.cacheEnabled=false 二级缓存关闭,一级缓存仍有用

    2.select标签的 useCache=false 二级缓存关闭,一级缓存仍有用

    3.每个增删改标签的 flushCache=true 增删改完成之后,二级缓存和一级缓存同时清空

    查询标签 flushCache=true 每次查询之后都会清空缓存

    4.sqlSession.clearCache() 只是清楚当前session的一级缓存

    5.localCacheScope:本地缓存作用域

    • SESSION:当前会话的所有数据保存在会话缓存中
    • STATEMENT:可以禁用一级缓存

    查询顺序

    二级缓存=》一级缓存=》数据库

    缓存创建部分源码

    跟缓存有关的核心接口:org.apache.ibatis.cache.Cache.java

    image-20210113222022424

    光从包名decorators,我们就只可以得知,这Cache的设计就是装饰器模式。

    查看PerpetualCache代码,平平无奇!!

    public class PerpetualCache implements Cache {
    
      private String id;
    
      private Map<Object, Object> cache = new HashMap<Object, Object>();
    
      public PerpetualCache(String id) {
        this.id = id;
      }
      .......
    

    我们点进decorators包下面任意一个Cache实现:例如BlockingCache

    image-20210113222503091

    典型的装饰器模式的结构。该BlockingCache组合了另一个Cache,我们猜测它是装饰者,而BlockingCache是具体组件。

    在XMLMapperBuilder中:会根据sql映射文件中cache标签创建一个Cache,参数是标签的配置属性。

    image-20210113223302591

    点击进入MapperBuilderAssistant的useNewCache方法:

    image-20210113223718498

    点击进入CacheBuilder的build()方法

    image-20210113224228474

    点击进入newCacheDecoratorInstance方法:base是Cache的具体实现,默认是PerpetualCache

    image-20210113224436079

    既然是通过反射进行装饰的,那我们可以任意点击一个Cache的装饰类,查看它的构造器,还以BlockingCache为例

    image-20210113224835872

    而mybatis的缓存模块,也是装饰器模式的典型代表,学习楷模!!!

  • 相关阅读:
    xposed的基本使用
    scrapy发送邮件
    Image Pipeline
    javax.validation.ConstraintViolationException---Hibernate后台实体校验
    oracle中lead和lag函数 (转载)
    oracle中根据当前记录查询前一条和后一条记录
    物化视图知识了解(转载)
    10个线程同步处理1000行消息
    SQL语句执行效率及分析(note)
    js对文本框特殊字符串过滤
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14274899.html
Copyright © 2011-2022 走看看