MyBatis 源碼閱讀之 databaseId

MyBatis 源碼閱讀之 databaseId

MyBatis 的配置文件全部配置會被 org.apache.ibatis.builder.xml.XMLConfigBuilder 類讀取,咱們能夠經過此類來了解各個配置是如何運做的。而 MyBatis 的映射文件配置會被 org.apache.ibatis.builder.xml.XMLMapperBuilder 類讀取。咱們能夠經過此類來了解映射文件的配置時如何被解析的。java

databaseId

databaseId 是用於項目中存在多種數據庫 SQL 時區分同一條 SQL 對應的數據庫。能夠這樣認爲,在 Mybatis 中 SQL 的 id 和 databaseId 組合纔是一條 SQL 的惟一標識。實際上 MyBatis 只會選擇性加載指定 databaseId 的 SQL ,還有一些沒有指定 databaseId 的 SQL。這裏說的有點不是很準確,咱們來慢慢分析即可以知曉。sql

databaseId 的配置

MyBatis 配置文件中 databaseId 的配置以下:數據庫

<!-- mybatis-config.xml -->
<databaseIdProvider type="DB_VENDOR">
    <property name="SQL Server" value="sqlserver"/>
    <property name="DB2" value="db2"/>
    <property name="Oracle" value="oracle" />
</databaseIdProvider>

讀取的代碼以下:apache

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();
        // 找到 type 配置對應的類
        databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
        databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
        // 經過數據源肯定使用的 databaseId ,以後 SQL 也只會加載這種 databaseId 的 SQL ,其餘類型都會被忽略
        String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
        configuration.setDatabaseId(databaseId);
    }
}

這裏的代碼邏輯比較簡單:session

  1. 讀取 databaseIdProvider 節點的 type 值與子節點屬性值
  2. 根據 type 值找到與之匹配的 DatabaseIdProvider 子類,建立相應的實例,將子節點屬性設置到實例中
  3. 調用 DatabaseIdProvider 實例的 getDatabaseId() 方法獲取值設置到 Configuration 實例中

注:mybatis

  1. type 爲 DB_VENDOR 表示使用 org.apache.ibatis.mapping.VendorDatabaseIdProvider 做爲 DatabaseIdProvider 的實現類。這一點能夠在 org.apache.ibatis.session.Configuration 的構造方法中找到證據。
  2. 若是發現本身的 databaseId 沒被正確識別,能夠查看 getDatabaseId() 方法是否和預期一致。

databaseId 的使用

databaseId 在映射文件裏要和上一節的配置的屬性 value 值對應,以下:oracle

<!-- mybatis-mapper.xml -->
<!-- 指定 sql 和 select 節點的內容只適用於 oracle 數據庫,那麼使用 Oracle 的數據庫時便會加載這些節點 -->
<sql id="column" databaseId="oracle">
    <!-- ... -->
</sql>
<select id="selectOne" databaseId="oracle">
    <!-- ... -->
</select>

讀取的代碼在這,這只是 <sql> 節點加載的代碼:app

private void sqlElement(List<XNode> list) throws Exception {
    if (configuration.getDatabaseId() != null) {
        // 加載 DataSource 對應的 databaseId 的 SQL 節點
        sqlElement(list, configuration.getDatabaseId());
    }
    // 記載 databaseId 爲空的 SQL 節點
    sqlElement(list, null);
}

private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) {
        String databaseId = context.getStringAttribute("databaseId");
        String id = context.getStringAttribute("id");
        id = builderAssistant.applyCurrentNamespace(id, false);
        if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
            sqlFragments.put(id, context);
        }
    }
}

private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
    if (requiredDatabaseId != null) {
        if (!requiredDatabaseId.equals(databaseId)) {
            // 兩個 databaseId 一致纔會返回 true,此處不一致
            return false;
        }
    } else {
        // 一個爲空,一個不爲空,也不一致
        if (databaseId != null) {
            return false;
        }

        // 若是先前已經加載過節點,則再也不加載
        // 是否視爲同一個節點是由 id 決定
        // 但 id 相同,databaseId 不一樣 mybatis也能夠加載,因此有些地方說,id+databaseId 肯定惟一一條 SQL
        if (this.sqlFragments.containsKey(id)) {
            XNode context = this.sqlFragments.get(id);
            if (context.getStringAttribute("databaseId") != null) {
                return false;
            }
        }
    }
    return true;
}

代碼上已經有了詳細的註釋,這裏就簡單說一下。sqlElement() 方法會被調用兩次,第一次用於處理 databaseId 與全局 Configuration 實例的 databaseId 一致的節點;另外一次用於處理節點的 databaseId 爲 null 的狀況,針對同一個 id ,優先選擇存在 databaseId 而且與數據源的一致。ide

一樣的,<select> 之類的節點解析代碼也是相似,不過它們的解析代碼在 org.apache.ibatis.builder.xml.XMLStatementBuilder 中。sqlserver

相關文章
相關標籤/搜索