MyBatis学习——databaseIdProvider和databaseId

有什么作用

databaseIdProvider和databaseId的作用简单来说就是让一个项目支持不同的数据库。

让一个项目支持不同的数据库在企业开发中是一个比较常见的需求。由于不同的数据库支持的sql语法稍有差别,所以某些功能需要根据数据库的不同书写不同的sql语句。对于这种需求,首先能够想到的解决方案就是针对不同的数据库维护不同的mapper.xml文件,但是这种方案会严重增加开发和维护的成本。因为不同数据库支持的语法大部分都是相同的,不同的毕竟是少数,我们希望只重写不同的部分而重用相同的部分。

针对这种情况,MyBatis提供了解决方案,即databaseIdProvider和databaseId。通过MyBatis提供的这种功能,我们就只需要维护一套mapper.xml文件便可。

下面先讲解如何配置,然后在从源码层面对这个功能进行解读,最后探讨一下如何通过自定义来定制这个功能。

如何配置

配置databaseIdProvider(在mybatis的配置文件中配置,比如mybatis-config.xml)

代码段一:

<databaseIdProvider type="DB_VENDOR">
  <property name="DB2" value="db2" />
  <property name="Oracle" value="oracle" />
  <property name="Adaptive Server Enterprise" value="sybase" />
  <property name="MySQL" value="mysql" />
</databaseIdProvider>

上述配置用于决定当前databaseId的名称。在每一个property标签中,name代表数据库的productName(DatabaseMetaData#getDatabaseProductName()),value是用户自定义的databaseId名称。mybatis在初始化的时候会根据所使用的数据源得到当前databaseId的名称,得到的databaseId的名称供mybatis选择映射文件中相应的语句。比如我们使用的是Mysql数据库,则得到的databaseId名称为“mysql”。

配置databaseId(在映射文件中配置,比如:BlogMapper.xml)

代码段二:

<mapper namespace="info.songjie365.mybatis.sample.mapper.BlogMapper">
  <select id="selectBlog" resultType="info.songjie365.mybatis.sample.domain.Blog" databaseId="mysql">
    select * from blog where id = #{id}
  </select>
</mapper>

官方文档对databaseId的解释为:“如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略”。也就是说如果在mybatis的配置文件中没有配置 databaseIdProvider,则在映射文件中配置的databaseId不会生效

由于我们使用的是Mysql数据库,得到的databaseId为“mysql”,所以上述的映射片段会在生效。

源码解读

上面我们讲解了如何配置,下面我们从源码的角度来剖析一下mybatis是如何实现这个功能的。

databaseIdprovider的默认实现是VendorDatabaseIdProvider,其接口为DatabaseIdProvider。先看DatabaseIdProvider的定义:

代码片段三:

public interface DatabaseIdProvider {
 
  void setProperties(Properties p);
 
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

接口很简单,就两个方法。

void setProperties(Properties p);:该方法会把databaseIdprovider中配置的属性(见代码片段一)通过这个方法传递进来。

String getDatabaseId(DataSource dataSource):该方法会将返回一个databaseId。

再看一下默认实现VendorDatabaseIdProvider的源码:

代码片段四:

public class VendorDatabaseIdProvider implements DatabaseIdProvider {
  
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
 
  private Properties properties;
 
  @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<Object, Object> 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
        }
      }
    }
  }
  
}

默认实现就是根据数据库的productName去匹配在mybatis配置文件中配置的属性,然后选出databaseId。

至此,我们分析完了databaseIdprovider的源码,下面接着看一下databaseId是如何发挥作用的。

mybatis在解析映射文件的时候,首先会根据mybatis的配置文件解析出当前数据库对应的databaseId,然后根据mybatis的映射文件判断配置了databaseId属性的语句是否和接卸出的databaseId匹配,核心代码如下:

代码片段五:

 private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
    if (requiredDatabaseId != null) {
      if (!requiredDatabaseId.equals(databaseId)) {
        return false;
      }
    } else {
      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;
  }

自定义

这里的自定义只是针对databaseIdProvider的自定义,对映射文件中的databaseId不能自定义也没自定义的必要。

通过上述的分析,可以看出,如果要自定义databaseIdProvider只要实现接口DatabaseIdProvider便可。然后在mybatis的配置文件中将databaseIdProvider的type置为自定义的实现便可。

自定义的databaseIdProvider为:

代码片段六:

public class MyDatavbaseIdProvider implements DatabaseIdProvider {
	Properties props = null;
	@Override
	public void setProperties(Properties p) {
		//p代表mybatis中针对databaseIdProvider配置的属性
		props = p;
	}
 
	@Override
	public String getDatabaseId(DataSource dataSource) throws SQLException {
		//根据使用的数据源,返回不同的databaseId。这里没有给出具体实现
		return null;
	}
}

使用自定义的databaseIdProvider,则mybatis配置文件中的配置需要做相应的调整。

代码片段七:

<databaseIdProvider type="info.songjie365.mybatis.sample.custom.MyDatavbaseIdProvider">
	<property name="DB2" value="db2" />
	<property name="Oracle" value="oracle" />
	<property name="Adaptive Server Enterprise" value="sybase" />
	<property name="MySQL" value="mysql" />
</databaseIdProvider>

 

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值