MyBatis 的配置文件全部配置會被 org.apache.ibatis.builder.xml.XMLConfigBuilder
類讀取,咱們能夠經過此類來了解各個配置是如何運做的。而 MyBatis 的映射文件配置會被 org.apache.ibatis.builder.xml.XMLMapperBuilder
類讀取。咱們能夠經過此類來了解映射文件的配置時如何被解析的。java
databaseId 是用於項目中存在多種數據庫 SQL 時區分同一條 SQL 對應的數據庫。能夠這樣認爲,在 Mybatis 中 SQL 的 id 和 databaseId 組合纔是一條 SQL 的惟一標識。實際上 MyBatis 只會選擇性加載指定 databaseId 的 SQL ,還有一些沒有指定 databaseId 的 SQL。這裏說的有點不是很準確,咱們來慢慢分析即可以知曉。sql
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
DatabaseIdProvider
子類,建立相應的實例,將子節點屬性設置到實例中DatabaseIdProvider
實例的 getDatabaseId()
方法獲取值設置到 Configuration
實例中注:mybatis
DB_VENDOR
表示使用 org.apache.ibatis.mapping.VendorDatabaseIdProvider
做爲 DatabaseIdProvider
的實現類。這一點能夠在 org.apache.ibatis.session.Configuration
的構造方法中找到證據。getDatabaseId()
方法是否和預期一致。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