Configuration--mappers--XMLMapperBuilder.parse(三-8-2)

上一篇我们讲了ConfigurationaddMappers方法,可以看出,这个方法的最终目的,是将我们定义的mapper接口最终转化为MappedStatement,然后加入Configuration中,并且我们从上一篇文章中可以看出,即使我们是使用了class去配置mapper,也会去默认路径下加载xml文件.

本篇我们来看看当mapper标签加载的是一个xml文件时使用的XMLMapperBuilder.parse:

  public void parse() {
    // 看是否已经加载过该配置文件
    if (!configuration.isResourceLoaded(resource)) {
      // 解析mapper标签
      configurationElement(parser.evalNode("/mapper"));
      // 标识该xml文件已经被加载过
      configuration.addLoadedResource(resource);
      // 去加载对应的namespace对应的class
      bindMapperForNamespace();
    }

    // 这三个方法是重新加载一遍之前加载出错的标签
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

我们先来看bindMapperForNamespace方法:

  // 在这个方法里,我们看到了熟悉的configuration.addMapper()方法,这说明在加载xml文件时,也会去解析一下namespace对应的class(如果该class存在的话)
  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }

接下来,我们再来看解析标签的configurationElement方法:

  // 从该方法中,我们可以看出,会逐步解析mapper标签下的子标签,具体解析过程,这里就不慢慢分析了,因为涉及到的细节过多,但是最终肯定是会生成一个MappedStatement的
  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
          throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

至此,我们Configuration就分析完了,也成功的构建出了DefaultSqlSessionFactory,因为最终DefaultSqlSessionFactory中就只有一个Configuration:

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

由于最后的mapper解析过程实在是复杂,所以本系列文章就简化了,之后如果有机会,博主会单开章节来写.

### 修改 Activiti 7 中底层 Job 的 SQL 查询语句 在 Activiti 7 中,要自定义或重写底层 Job 的 SQL 查询语句,可以通过扩展 `MybatisCustomSqlSession` 或者通过配置 MyBatis 自定义 XML 映射文件来实现。具体方法如下: #### 方法一:使用 MyBatis 自定义 XML 映射文件 1. **创建自定义的 Mapper 文件** 需要在项目的 resources 目录下创建一个新的 MyBatis mapper 文件,例如 `custom-job-mapper.xml`。 ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.activiti.engine.impl.persistence.entity.JobEntity"> <!-- 自定义查询 --> <select id="selectNextJobsToExecute" parameterType="map" resultType="org.activiti.engine.impl.persistence.entity.JobEntity"> SELECT * FROM ACT_RU_JOB J WHERE J.DUEDATE_ IS NULL OR J.DUEDATE_ <= #{currentTime} AND (J.RETRIES_ > 0) AND J.LOCK_EXP_TIME_ IS NULL ORDER BY J.PRIORITY DESC, J.ID_ LIMIT #{maxResults} </select> </mapper> ``` 2. **注册自定义 Mapper 文件** 在 Spring Boot 应用程序中,可以在 `application.properties` 或 `application.yml` 中指定额外的 MyBatis 映射文件位置。 对于 `application.properties`: ```properties mybatis.mapper-locations=classpath*:mybatis/mappers/*.xml ``` 这样可以确保新的映射文件被加载并覆盖默认的行为[^2]。 #### 方法二:继承和扩展 MyBatis SqlSessionFactoryBean 如果希望更深入地控制 SQL Session,则可以选择继承 `MybatisCustomSqlSession` 并提供自己的实现方式。 ```java import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; public class CustomSqlSessionFactoryBean extends SqlSessionFactoryBean { @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); // 获取已有的 Configuration 实例 SqlSessionFactory factory = getObject(); if (factory != null && !isInitialized()) { org.apache.ibatis.session.Configuration configuration = factory.getConfiguration(); // 添加自定义的 Mapped Statements addCustomMappedStatements(configuration); setInitializationCompleted(true); } } private void addCustomMappedStatements(org.apache.ibatis.session.Configuration config) { // 注册自定义的 SQL 映射语句 String resourcePath = "path/to/your/custom-job-mapper.xml"; try { Reader reader = Resources.getResourceAsReader(resourcePath); XmlMapperBuilder xmlParser = new XmlMapperBuilder(reader, config, resourcePath, config.getSqlFragments()); xmlParser.parse(); } catch (IOException e) { throw new RuntimeException("Failed to load custom job mapper", e); } } private boolean isInitialized() { return Boolean.TRUE.equals(getApplicationContext().getBeansOfType(CustomSqlSessionFactoryBean.class).containsKey(this)); } } ``` 以上两种方法都可以有效地修改 Activiti 7 中底层 Job 的 SQL 查询语句。选择哪种取决于具体的项目需求和技术栈偏好[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值