MyBatis 的 DatabaseIdProvider 解析
數據庫廠商標識
MyBatis 可以根據不同的數據庫廠商執行不同的語句,這種多廠商的支持是基於映射語句中的 databaseId
屬性。 MyBatis 會加載不帶 databaseId
屬性和帶有匹配當前數據庫 databaseId
屬性的所有語句。 如果同時找到帶有 databaseId
和不帶 databaseId
的相同語句,則後者會被捨棄
配置方法
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="developmentOracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
</environment>
</environments>
<databaseIdProvider type="com.yczuoxin.databaseidorovider.MyDatabaseIdProvider">
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
</databaseIdProvider>
DatabaseIdProvider 接口
public interface DatabaseIdProvider {
default void setProperties(Properties p) {
// NOP
}
// 返回數據源的產品名稱
String getDatabaseId(DataSource dataSource) throws SQLException;
}
其繼承關係如下
org.apache.ibatis.mapping.DatabaseIdProvider
org.apache.ibatis.mapping.VendorDatabaseIdProvider
VendorDatabaseIdProvider 實現類
public class VendorDatabaseIdProvider implements DatabaseIdProvider {
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) {
LogHolder.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
}
}
}
}
private static class LogHolder {
private static final Log log = LogFactory.getLog(VendorDatabaseIdProvider.class);
}
}
查詢數據源產品名稱底層調用的是 java.sql.DatabaseMetaData#getDatabaseProductName
方法,在 mapper.xml 中選擇對應的 databaseId
就會將信息保存在org.apache.ibatis.mapping.MappedStatement
對象中並保存在 Configuration 的 Map<String, MappedStatement> mappedStatements
中保存
配置讀取
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
String type = context.getStringAttribute("type");
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
Properties properties = context.getChildrenAsProperties();
// DB_VENDOR 類型創建 VendorDatabaseIdProvider 對象
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
自定義
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private static final Log log = LogFactory.getLog(MyDatabaseIdProvider.class);
private Properties properties;
@Override
public void setProperties(Properties p) {
this.properties = p;
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
Connection connection = dataSource.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
log.debug("Current DataBase Product Name is: " + databaseProductName);
for (Object key : properties.keySet()) {
if (key.equals(databaseProductName)) {
log.debug("Find a matched property value: " + properties.get(key));
return (String) properties.get(key);
}
}
return null;
}
}
在配置中引入
<databaseIdProvider type="com.yczuoxin.databaseprovider.MyDatabaseIdProvider">
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>