手敲Mybatis(17章)-二级缓存功能,感受装饰器的魅力

1.目的

本节主要是讲Mybatis的二级缓存,一级缓存是会话SqlSession级别的,二级缓存是Mapper级别的这个大家都知道,一级缓存主要是同一个SqlSession实例才可以缓存,那么出现commit等其他情况可能清除缓存,我想要再发起的会话还是相同的查询操作,最好也是可以把数据从缓存中获取出来。这个时候该如何实现呢?

这时候引出来二级缓存,以一个 Mapper 为生命周期,在这个 Mapper 内的同一个操作,无论发起几次会话都可以使用缓存来处理数据。叫二级缓存,是因为要在一级缓存基础上额外添加缓存操作,(也就是要引出来设计模式-装饰器模式),当会话发生 close、commit 操作时则把数据刷新到二级缓存中进行保存,直到执行器发生 update 操作时清空缓存。

本节最重点主要就是装饰器模式,所以先提装饰器模式的思路,就是不改变原有的类和方法,在其上包装了一成自己的功能,是现有类的包装。

二级缓存需要在设置中开启全局缓存,如图,开启了缓存,关闭了一级缓存,然后需要在XMLConfigBuilder中解析,解析完毕将全局缓存设置到Configuration中的cacheEnabled中,true和false代表是否开启。

由于下图在Mapper中设置了cache标签的一些属性,所以需要在XMLMapperBuilder类中需要解析一下,解析一级缓存类,二级缓存类,缓存间隔时间,队列大小以及属性等等,最后将属性构建到缓存构建器中,接着将缓存放入到当前类MapperBuilderAssistant的Cache变量赋值,然后将MapperBuilderAssistant存入的缓存变量存入到MappedStatement的cache里。到此构建结束。

执行时则是构建Executor时判断是否全局缓存,如果全局缓存则在DefaultSqlSession中给的Executor就是CachingExecutor,如果不是则是SimpleExecutor,这里相当于CachingExecutor做了一层SimpleExecutor包装,因为CachingExecutor里有一个Executor变量,这个变量传的就是SimpleExecutor,那么真正执行操作时,我们就可以执行SimpleExecutor的业务,而CachingExecutor主要执行缓存业务,这就是扩展其功能,却又不改变其功能,这里就是装饰器的设计精髓和思想。

CachingExecutor类里实现Executor类,所以还是实现query、update等等的方法,执行query方法前先查看是否有缓存,此时进入事务缓存管理器TransactionalCacheManager,主要是事务缓存TransactionalCache

的装饰器,调用事务缓存里的获取缓存以及存入缓存操作,事务缓存是二级缓存FifoCache(本节二级缓存实现方式是先进先出的队列缓存)的装饰器,也就是说这里最终会调用二级缓存的存储获取缓存操作,事务缓存生命周期是在提交回滚将事务缓存中的变量数据缓存清空,放入到FifoCache缓存中。

2.uml类图

看类图UML就可以知道整个过程,

1.构建时由XMLConfigBuilder开始解析然后放入到Configration中,然后进入到XMLMapperBuilder解析图二的cache标签,然后进入MapperBuilderAssistant去存储相关内容,处理完后放入Configuration中和MappedStatement中。

2.执行时操作,由SqlSession开始进入到Executor中,首先进入第一个装饰器,CachingExecutor,然后进入事务缓存管理器TransactionalCacheManager,依赖事务缓存TransactionalCache,事务缓存又是二级缓存FifoCache的包装器,二级缓存又是一级缓存的PerpetualCache包装器,最终调度到最底层返回。大致就是UML的这个类图的过程

3.代码

3.1 全局缓存解析

3.1.1 XMLConfigBuilder

XMLConfigBuilder中添加解析全局缓存操作,解析setting标签的name和value,解析完毕放入configuration中的cacheEnabled变量里。

 /**
     * 解析配置在 XML 文件中的缓存机制。并把解析出来的内容存放到 Configuration 配置项中。
     * <settings>
     * <!--全局缓存:true/false -->
     * <setting name="cacheEnabled" value="false"/>
     * <!--缓存级别:SESSION/STATEMENT-->
     * <setting name="localCacheScope" value="SESSION"/>
     * </settings>
     */
    private void settingsElement(Element context) {
        if (context == null) return;
        List<Element> elements = context.elements();
        Properties props = new Properties();
        for (Element element : elements) {
            props.setProperty(element.attributeValue("name"), element.attributeValue("value"));
        }
        // 设置全局缓存 step-18加
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
        // 设置缓存级别
        configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope")));
    }

Configuration中的更改就比较简单了,主要是将是否是全局缓存以及将当前的缓存放入到Map中的操作处理,Configuration构造方法中则往类型处理器中注册一级缓存(PerpetualCache)和二级缓存(FiFoCache,本节二级缓存暂时实现先进先出)

public class Configuration {
   // 省略其他

    // 缓存,存在Map里
    protected final Map<String, Cache> caches = new HashMap<>();
   // 默认启用缓存,cacheEnabled = true/false
    protected boolean cacheEnabled = true;

    public Configuration() {

        // 省略其他
      
        // step-18-添加
        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
       
    }

    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
    }

    public void addCache(Cache cache) {
        caches.put(cache.getId(), cache);
    }

    public Cache getCache(String id) {
        return caches.get(id);
    }
}

3.1.2  XMLMapperBuilder

XMLMapperBuilder类:

因为二级缓存是Mapper级别的,所以会有一些配置操作放入到mapper里需要去解析,如

<cache eviction="FIFO" flushInterval="600000" size="512" readOnly="true"/>标签,所以在cacheElement方法主要是处理解析这个标签里属性信息操作的,eviction=fifo,代表这个二级缓存是使用先入先出操作,

public class XMLMapperBuilder extends BaseBuilder {
  // 省略其他。。。

  private void configurationElement(Element element) {
    // 省略其他。。。
    // 2. 配置cache
    cacheElement(element.element("cache"));
  }

   // 新添加的方法
   /**
     * <cache eviction="FIFO" flushInterval="600000" size="512" readOnly="true"/>
     */
    private void cacheElement(Element context) {
        if (context == null) return;
        // 基础配置信息
        String type = context.attributeValue("type", "PERPETUAL");
        Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
        // 缓存队列FIFO
        String eviction = context.attributeValue("eviction", "FIFO");
        Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
        Long flushInterval = Long.valueOf(context.attributeValue("flushInterval"));
        Integer size = Integer.valueOf(context.attributeValue("size"));
        boolean readWrite = !Boolean.parseBoolean(context.attributeValue("readOnly", "false"));
        boolean blocking = !Boolean.parseBoolean(context.attributeValue("blocking", "false"));

        // 解析额外属性信息;<property name="cacheFile" value="/tmp/xxx-cache.tmp"/>
        List<Element> elements = context.elements();
        Properties props = new Properties();
        for (Element element : elements) {
            props.setProperty(element.attributeValue("name"), element.attributeValue("value"));
        }
        // 构建缓存
        builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWri
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值