番外篇-1:databaseIdProvider的使用

其实刚开始的时候我也不知道是啥意思,一般也不会去用这个功能,不过今天在看源码的时候理解了这个问题,不过这个东西不会讲全部,主要是讲下怎么使用,官网的文档为

mybatis – MyBatis 3 | 配置

刚开始看的时候其实不知道啥意思,直到看到了源码才明白了

首先先解析下这个功能是在干啥,就是MyBatis 可以根据不同的数据库厂商执行不同的语句,这是官网原话,看起来可能有点懵,举个例子你就知道了

以下是xml里面关于数据库厂商的配置


     
     

其中name是数据库的名字,而值是一个标识来的,而数据库名字是每个厂商自己返回的,获取代码在

org.apache.ibatis.mapping.VendorDatabaseIdProvider#getDatabaseProductName

看到代码你就明白了

private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    Connection con = null;
    try {
      con = dataSource.getConnection();
      DatabaseMetaData metaData = con.getMetaData();
      return metaData.getDatabaseProductName();
    } finally {
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          // ignored
        }
      }
    }
  }

其实就是jdbc的一些规范进行获取的

而value是用来干啥的呢,就是用来配置在你mapper.xml里面的标识

看个例子

   

上面这个databaseId就代表这个sql语句只有当你当前的数据库返回的是MySQL的时候才能使用到,否则你是拿不到这个语句的,这么说应该还好理解吧,这个功能就等于mybatis进行了数据隔离,但是我们一般不用这个功能,所以也就比较少使用了

 

对于这种没有指定databaseId的语句就是每个数据源都是可以获取到的

这篇就是随便讲讲databaseIdProvider的使用,估计业务中也不这么去用,不过mybatis确实提供了这个支持

源码

源码的话就稍微看下,不过这里只是一部分,要看全部的还要去其他地方看,这里不细说了

org.apache.ibatis.builder.xml.XMLConfigBuilder#databaseIdProviderElement


  private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    //配置了数据库厂商
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
          type = "DB_VENDOR";
      }
      //获取设置的熟悉以及设置给DatabaseIdProvider
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    //环境不为空和数据库厂商不为空时会进入这个判断
    if (environment != null && databaseIdProvider != null) {
      //通过数据源获取对应的数据标识,就是上面说的意思
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      //标记当前应用使用的数据库厂商标识
      configuration.setDatabaseId(databaseId);
    }
  }



再稍微看下是如何获取数据库厂商id的,估计你点开就明白了


org.apache.ibatis.mapping.VendorDatabaseIdProvider#getDatabaseId


@Override
  public String getDatabaseId(DataSource dataSource) {
    if (dataSource == null) {
      throw new NullPointerException("dataSource cannot be null");
    }
    try {
      return getDatabaseName(dataSource);
    } catch (Exception e) {
      log.error("Could not get a databaseId from dataSource", e);
    }
    return null;
  }

  @Override
  public void setProperties(Properties p) {
    this.properties = p;
  }

  private String getDatabaseName(DataSource dataSource) throws SQLException {
    String productName = getDatabaseProductName(dataSource);
    if (this.properties != null) {
      for (Map.Entry property : properties.entrySet()) {
        if (productName.contains((String) property.getKey())) {
          return (String) property.getValue();
        }
      }
      // no match, return null
      return null;
    }
    return productName;
  }

  private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    Connection con = null;
    try {
      con = dataSource.getConnection();
      DatabaseMetaData metaData = con.getMetaData();
      return metaData.getDatabaseProductName();
    } finally {
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          // ignored
        }
      }
    }
  }

至于他是怎么把数据标识id存起来的对应关系,这里就不说了,大概就是解析的时候把它做了一个标识吧,有兴趣的可以参考一下

第六篇:Configuration之mappers_zxc_user的博客-CSDN博客

怎么设置的

这里还是稍微说下Mybatis是怎么设置这个关系的,但是也只是简单说下,其实也并不难

还是复习下原来的方法

org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //解析二级缓存相关的
      cacheRefElement(context.evalNode("cache-ref"));
      //解析缓存
      cacheElement(context.evalNode("cache"));
      //解析parameterMap,现在已经基本不用了
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql片段,应该是要放到某个map中,其他地方引用的时候再从map拿出来
      sqlElement(context.evalNodes("/mapper/sql"));
      //核心的sql语句解析逻辑,增删改查
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }


最核心的方法为



org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List)


  private void buildStatementFromContext(List list) {
    //如果数据库标识不为空,则设置一下,这个标识是在前面设置的
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }



那么最终就是看 statementParser.parseStatementNode()方法是怎么处理的




org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode




  public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    //判断是否与厂商一致
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
}

后面的去掉了,因为之前已经说过了,最重要是看看下面这个方法




  private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
    //数据库厂商设置了
    if (requiredDatabaseId != null) {
      //核心在这里,但是mapper里面配置的跟当前的数据库厂商不一致,直接返回,也就是不把当前
      //的mapper加到配置中,那么你在获取的时候自然就找不到了
      if (!requiredDatabaseId.equals(databaseId)) {
        return false;
      }
    } else {
      //如果本地环境没有设置数据库厂商,但是mapper里面设置了,那也是不添加到配置文件中的,因为根本就不知道属于哪个数据库厂商
      if (databaseId != null) {
        return false;
      }
      // skip this statement if there is a previous one with a not null databaseId
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (this.configuration.hasStatement(id, false)) {
        MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
        if (previous.getDatabaseId() != null) {
          return false;
        }
      }
    }
    return true;
  }



这样应该就比较清晰了

你可能感兴趣的:(mybatis源码相关,mybatis,java,mysql)