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