MyBatis之配置文件解析_databaseIdProvider及多数据库支持

Mybatis多数据库支持:Configuration中有一个字符串属性databaseId,mybatis试图通过这个属性解决多数据库配置问题。
DatabaseIdProvider接口及实现类,它能根据DataSource获得数据库的标识id,mybatis提供了一个默认的实现VendorDatabaseIdProvider,下面配置例子中的DB_VENDOR别名即是在它的别名,并在Configuration的无参构造方法中注册了这个别名

// 接口
public interface DatabaseIdProvider {

  default void setProperties(Properties p) {
    // NOP
  }
  // 可以根据DataSource对象获得一个数据库id	
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

// 
public Configuration() {
   // Configuration初始化时注册了别名DB_VENDOR 
   // VendorDatabaseIdProvider实现了DatabaseIdProvider接口 
   typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
}

配置:在mybatis的配置文件中,通过databaseIdProvider标签配置,其中type是一个类型别名,类要实现DatabaseIdProvider接口。

<!-- 旧版本中写成type="VENDOR",使用默认的实现类VendorDatabaseIdProvider -->
<databaseIdProvider type="DB_VENDOR">
  <!-- 若通过数据源获得的产品名与这里键一样,则使用值作为databaseId -->  
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>        
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

解析:XMLConfigBuilder的方法databaseIdProviderElement解析了这个节点databaseIdProvider,调用DatabaseIdProvider的getDatabaseId的方法获得Database的id,并作为Configuration的属性databaseId的值

     
 // XMLConfigBuilder中解析了databaseIdProvider标签    
 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";
      }
      Properties properties = context.getChildrenAsProperties();
      // 此即VendorDatabaseIdProvider  
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      // 获得databaseId  
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      //configuration对象上设置好databaseId,备用  
      configuration.setDatabaseId(databaseId);
    }
  }
     
  

VendorDatabaseIdProvider获得databaseId的实现逻辑:先通过DataBase的元数据获得数据库的产品名,若产品名中含有配置属性name所定义的名字,则使用对应的value作为属性名,若都不包含 ,则产品名为null,若没有定义property子标签,则就使用产品名。其意图是根据实际运行的数据库去适配对应的数据库

// VendorDatabaseIdProvider是DatabaseIdProvider接口的实现
// 下面的方法返回dataBaseID  
private String getDatabaseName(DataSource dataSource) throws SQLException {
  // 从dataSource获得原数据DatabaseMetaData,通过它获得数据库名
  String productName = getDatabaseProductName(dataSource);
  //   
  if (this.properties != null) {
    for (Map.Entry<Object, Object> property : properties.entrySet()) {
      // 若这个名是属性中的key,则返回其值作为作为databaseID  
      if (productName.contains((String) property.getKey())) {
        return (String) property.getValue();
      }
    }
    // no match, return null
    return null;
  }
  // 若没有,则使用数据库产品名,作为dataBaseId  
  return productName;
}

映射文件中的使用 映射文件的标签sql、insert、update、delete中都有一个属性_databaseId用来指定当前标签与给定数据库匹配的SQL语句,如下所示:

<!-- 匹配当前使用的databaseId才会处理对应的SQL语句,不匹配则不处理 -->
<sql id="mysql" databaseId="MySQL8">
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <if test="_databaseId == 'oracle'">
          account
        </if>
        <if test="_databaseId == 'db2'">
          dep_code
        </if>
        from SYS_USER
        where ID = #{id,jdbcType=CHAR}
</select>

SQL语句标签中有databaseId的处理逻辑 :先处理语名中有databaseId属性的语句标签,若语句中定义的databaseId与Configuration的databaseId不匹配,则不作处理,匹配再作处理,接着再处理语句中没有属性databaseId的语句标签。这是XMLStatementBuilder中方法parseStatementNode开始的步骤,过滤掉不需要的SQL语句

posted @ 2022-05-07 10:13  beckwu  阅读(3319)  评论(0)    收藏  举报