IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 你尝试过在mybatis某个mapper上同时配置<cache/>和<cache-ref/>吗? -> 正文阅读

[Java知识库]你尝试过在mybatis某个mapper上同时配置<cache/>和<cache-ref/>吗?

项目背景:mybatis3.4.1,mapper接口上没有任何注释,有两个对应的XXXMapper.xml和YYYMapper.xml文件,在xml文件中分别配置<cache/>和<cache-ref/>

首先看下官方文档:<cache/>对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,你可以使用 <cache-ref/> 元素来引用另一个缓存。

那如果在同一个xxxMapper.xml文件同时配置<cache/>和<cache-ref/>呢?这时当前命名空间是独享缓存还是与引用的缓存共享?

这里先说结论:独享缓存还是共享缓存要根据mybatis解析XXXMapper.xml和YYYMapper.xml文件的次序!

【1】项目背景

① 两个mapper接口

接口有一些crud方法,没有任何注解

public interface DepartmentMapper {
	//...
}

public interface EmployeeMapper {
//...
}

② 两个xml配置文件

DepartmentMapper.xml文件缓存配置如下:

<mapper namespace="com.mybatis.dao.DepartmentMapper">
	<!-- 引用缓存:namespace:指定和哪个名称空间下的缓存一样 -->
	<cache-ref namespace="com.mybatis.dao.EmployeeMapper"/>
	<cache/>
//...
</mapper>	

这里可以看到同同时配置了<cache/>和<cache-ref/>

EmployeeMapper.xml缓存配置如下:

<mapper namespace="com.mybatis.dao.EmployeeMapper">
	<cache /></cache>
	//...
</mapper>	

mybatis-config.xml中<mappers/>标签如何配置呢?下面分析。

【2】流程解析

① 二级缓存创建过程

下图是创建SqlSessionFactory过程,这里主要描述了二级缓存的创建过程。

在这里插入图片描述
如上图流程图示,在解析mapper.xml文件时会先尝试解析<cache-ref/>结点,然后解析<cache/>

MapperBuilderAssistant中解析<cache-ref/>源码

 public Cache useCacheRef(String namespace) {
    if (namespace == null) {
      throw new BuilderException("cache-ref element requires a namespace attribute.");
    }
    try {
      unresolvedCacheRef = true;
      Cache cache = configuration.getCache(namespace);
      if (cache == null) {
        throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
      }
      currentCache = cache;
      unresolvedCacheRef = false;
      return cache;
    } catch (IllegalArgumentException e) {
      throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
    }
  }

解释如下:

  • ① 根据namespace尝试从configuration获取对应的cache实例

  • ② 如果获取到,则设置当前MapperBuilderAssistant实例的currentCache为①中获取到的cache实例。否则走③

  • ③ 如果没有获取到,则抛出异常IncompleteElementException 。该异常会被XMLMapperBuilder拦截,将未正常处理的CacheRefResolver放入configuration的Collection<CacheRefResolver> incompleteCacheRefs

        private void cacheRefElement(XNode context) {
          if (context != null) {
            configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
            CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
            try {
              cacheRefResolver.resolveCacheRef();
            } catch (IncompleteElementException e) {
              configuration.addIncompleteCacheRef(cacheRefResolver);
            }
          }
        }
    

MapperBuilderAssistant中解析<cache/>源码

  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

解释如下

  • ① 根据命名空间、实现类、缓存过期策略…等创建Cache实例
  • ② 以namespace:cache实例这样的键值对放入configuration的成员变量中protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  • ③ 设置当前MapperBuilderAssistant实例的currentCache为①中获取到的cache实例

XMLMapperBuilder中parsePendingCacheRefs方法源码

 private void parsePendingCacheRefs() {
    Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
    synchronized (incompleteCacheRefs) {
      Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
      while (iter.hasNext()) {
        try {
          iter.next().resolveCacheRef();
          iter.remove();
        } catch (IncompleteElementException e) {
          // Cache ref is still missing a resource...
        }
      }
    }
  }

解释如下:

  • ① 获取configuration中Collection<CacheRefResolver> incompleteCacheRefs
  • ② 循环遍历①中incompleteCacheRefs得到每一个CacheRefResolver,并调用resolveCacheRef方法
  • ③ 走前面MapperBuilderAssistant中解析<cache-ref/>源码的过程
  • ④ 如果③中没有正常处理,则从incompleteCacheRefs集合中移除当前元素;否则不移除。

OK,有了上面的分析,我们来对比不同<mappers>配置时缓存实例具体情况。

② EmployeeMapper在前

也就是说mybatis-config.xml中<mappers/>标签配置如下:

<mappers>
	<mapper resource="com/mybatis/dao/EmployeeMapper.xml"/>
	<mapper resource="com/mybatis/dao/DepartmentMapper.xml"/>
</mappers>

流程分析如下:

  • ① 解析EmployeeMapper.xml,只有<cache/>结点
    • ② 解析<cache-ref/>结点,不存在,则无影响
    • ③ 解析cache结点,并将创建的cache实例放入configuration
    • ④ 执行parsePendingCacheRefs方法,这时configuration中Collection<CacheRefResolver> incompleteCacheRefs为空,故而无影响。
  • ⑤ 解析DepartmentMapper,有<cache/>和<cache-ref/>结点
    • ⑥ 解析cache-ref结点,将DepartmentMapper的缓存实例引用指向③中创建的EmployeeMapper的缓存实例;
    • ⑦ 解析<cache/>结点,DepartmentMapper为自己创建一个缓存实例不再引用③中创建的EmployeeMapper的缓存实例;
    • ⑧ 执行parsePendingCacheRefs方法,这时configuration中Collection<CacheRefResolver> incompleteCacheRefs为空,故而无影响。

结论:DepartmentMapper与EmployeeMapper分别拥有自己的二级缓存Cache实例!

③ EmployeeMapper在后

也就是说mybatis-config.xml中<mappers/>标签配置如下:

<mappers>
	<mapper resource="com/mybatis/dao/DepartmentMapper.xml"/>
	<mapper resource="com/mybatis/dao/EmployeeMapper.xml"/>
</mappers>

流程分析如下:

  • ① 解析DepartmentMapper,有<cache/>和<cache-ref/>结点
    • ② 解析<cache-ref/>结点,此时不存在EmployeeMapper的缓存实例,故而抛出异常。将DepartmentMapper对应的CacheRefResolver放入configuration的incompleteCacheRefs集合中
    • ③ 解析cache结点,并将创建的cache实例放入configuration
    • ④ 执行parsePendingCacheRefs方法,这时configuration中Collection<CacheRefResolver> incompleteCacheRefs有一条数据,但是仍旧没有对应的EmployeeMapper的缓存实例。故而不做处理。
  • ⑤ 解析EmployeeMapper.xml,只有<cache/>结点
    • ⑥ 解析<cache-ref/>结点,不存在,则无影响
    • ⑦ 解析cache结点,并将创建的cache实例放入configuration
    • ⑧ 执行parsePendingCacheRefs方法,这时configuration中Collection<CacheRefResolver> incompleteCacheRefs有DepartmentMapper的CacheRefResolver,需要进行处理。会将DepartmentMapper的缓存引用指向EmployeeMapper的缓存实例。

结论:EmployeeMapper拥有自己的二级缓存Cache实例,DepartmentMapper的缓存引用指向EmployeeMapper的二级缓存Cache实例!

综上所述,永远不要在一个mapper.xml中同时配置<cache/>和<cache-ref/>标签(也永远不要在一个mapper接口类上面同时配置@CacheNamespace和@CacheNamespaceRef注解)

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 00:03:28  更:2021-07-24 00:03:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/18 18:12:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码