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语句

浙公网安备 33010602011771号