MyBatis的DatabaseIdProvider解析

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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章