你尝试过在mybatis某个mapper上同时配置<cache/>和<cache-ref/>吗?

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 你尝试过在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注解)


目录
相关文章
|
2月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
53 10
|
3月前
|
SQL XML Java
mybatis复习01,简单配置让mybatis跑起来
文章介绍了MyBatis的基本概念、历史和特点,并详细指导了如何配置MyBatis环境,包括创建Maven项目、添加依赖、编写核心配置文件、创建数据表和实体类、编写Mapper接口和XML配置文件,以及如何编写工具类和测试用例。
mybatis复习01,简单配置让mybatis跑起来
|
4月前
|
SQL Java 数据库连接
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
文章讲述了在使用Mybatis时遇到的资源文件找不到的问题,并提供了通过修改Maven配置来解决资源文件编译到target目录下的方法。
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
|
4月前
|
安全 Java 数据库连接
后端框架的学习----mybatis框架(3、配置解析)
这篇文章详细介绍了MyBatis框架的核心配置文件解析,包括环境配置、属性配置、类型别名设置、映射器注册以及SqlSessionFactory和SqlSession的生命周期和作用域管理。
后端框架的学习----mybatis框架(3、配置解析)
|
3月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
67 1
|
4月前
|
缓存 Java 数据库连接
mybatis1.常见配置
本文介绍了MyBatis框架中的常见配置及其加载顺序。配置可通过`properties`元素、资源文件或方法参数传递,其中方法参数传递的属性具有最高优先级。文章列举了几个重要的配置项,如`cacheEnabled`用于全局开启或关闭缓存功能;`lazyLoadingEnabled`控制对象的延迟加载行为;`useGeneratedKeys`允许JDBC支持自动生成主键;`defaultExecutorType`设定默认执行器类型等。此外,还介绍了多环境配置方法,通过`environments`元素可定义不同环境下的数据库连接信息,并可根据需求动态选择加载特定环境
|
5月前
|
SQL Java 数据库连接
idea中配置mybatis 映射文件模版及 mybatis plus 自定义sql
idea中配置mybatis 映射文件模版及 mybatis plus 自定义sql
106 3
|
4月前
|
XML Java 数据库连接
Mybatis 模块拆份带来的 Mapper 扫描问题
Mybatis 模块拆份带来的 Mapper 扫描问题
49 0
|
5月前
|
SQL
自定义SQL,可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,如何自定义SQL呢?利用MyBatisPlus的Wrapper来构建Wh,在mapper方法参数中用Param注
自定义SQL,可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,如何自定义SQL呢?利用MyBatisPlus的Wrapper来构建Wh,在mapper方法参数中用Param注
若依修改,集成mybatisplus报错,若依集成mybatisplus,总是找不到映射是怎么回事只要是用mp的方法就找报,改成mybatisPlus配置一定要改
若依修改,集成mybatisplus报错,若依集成mybatisplus,总是找不到映射是怎么回事只要是用mp的方法就找报,改成mybatisPlus配置一定要改
下一篇
DataWorks