本篇文章我们来分析一下databaseIdProvider标签.
mybatis可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId属性.mybatis会加载不带databaseId属性和带有匹配当前数据库databaseId属性的所有语句.如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃.
用法为:
<databaseIdProvider type="DB_VENDOR" />
这里的DB_VENDOR会通过DatabaseMetaData的getDatabaseProductName()方法返回的字符串进行设置databaseId. 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短,如下:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
我们来看一下解析该标签的代码,databaseIdProviderElement(root.evalNode(“databaseIdProvider”))方法:
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
String type = context.getStringAttribute("type");
// 从这里可以看出,指定为VENDOR,也会被当作DB_VENDOR
if ("VENDOR".equals(type)) type = "DB_VENDOR";
Properties properties = context.getChildrenAsProperties();
// 实例化别名为type的DatabaseIdProvider(内置的为DB_VENDOR)
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
// 通过DataSource,获取databaseId
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
// 将databaseId设置进Configuration中
configuration.setDatabaseId(databaseId);
}
}
从上述代码我们可以看出,获取databaseId主要还是要看DatabaseIdProvider的getDatabaseId方法,当然我们可以自定义DatabaseIdProvider,但是本篇文章,我们先来了解mybatis内置的DatabaseIdProvider.
首先,我们在Configuration的构造函数中,找到DB_VENDOR别名对应的VendorDatabaseIdProvider:
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
接着我们来看VendorDatabaseIdProvider的getDatabaseId方法:
public String getDatabaseId(DataSource dataSource) {
if (dataSource == null) throw new NullPointerException("dataSource cannot be null");
try {
// 调用getDatabaseName方法
return getDatabaseName(dataSource);
} catch (Exception e) {
log.error("Could not get a databaseId from dataSource", e);
}
return null;
}
private String getDatabaseName(DataSource dataSource) throws SQLException {
// 获取数据库名称
String productName = getDatabaseProductName(dataSource);
if (this.properties != null) {
// 通过之前在databaseIdProvider子标签property中设置的key,value值,返回一个我们定义的比较简单的名称作为databaseId,这样我们在设置映射语句的databaseId属性时,也能使用这个简短的名称,而不是使用复杂并且可能还带有版本号的数据库名称
for (Map.Entry<Object, Object> property : properties.entrySet()) {
// 如果productName为"Oracle (DataDirect)",根据之前的设置,将会返回"oracle"
if (productName.contains((String) property.getKey())) {
return (String) property.getValue();
}
}
return null;
}
return productName;
}
// 该方法通过DatabaseMetaData获取数据库的名称
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
}
}
}
}
综上,databaseIdProvider标签我们就讲完啦.