Mybatis源码研究之DatabaseIdProvider
2021/6/2 1:20:50
本文主要是介绍Mybatis源码研究之DatabaseIdProvider,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
借助Mybatis提供的 databaseId特性,我们可以实现让应用同时支持多种类型的数据库。
0. 目录这里写目录标题
- 1. 测试用例
- 2. 原理解析
- 3. databaseId的其它应用
- 4. Links
1. 测试用例
相关的配置和测试用例如下 (这里我们以源生的mybatis为例,与SpringBoot的集成留待读者自行解读):
mybatis-config.xml
配置文件
<environments default="development"> <environment id="development"> .... </environment> </environments> <!-- 启用databaseId支持 --> <databaseIdProvider type="DB_VENDOR"> <property name="Oracle" value="oracle"/> </databaseIdProvider> <mappers> ...... </mappers>
- Mapper类
// Mapper public interface MultiDbTestMapper { int getMtHisTableExists(@Param("dbName") String dbName, @Param("tableName") String tableName); }
- Mapper文件
// mapper xml <mapper namespace="xxx.mapper.MultiDbTestMapper"> <!-- 查看某表是否存在 --> <select id="getMtHisTableExists" resultType="java.lang.Integer"> select count(*) ICOUNT from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=#{dbName} and TABLE_NAME=upper(#{tableName}) </select> <!-- oracle方言查看某表是否存在 --> <select id="getMtHisTableExists" resultType="java.lang.Integer" databaseId="oracle"> SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=upper(#{tableName}) </select> <!-- sqlserver方言查看某表是否存在 --> <select id="getMtHisTableExists" resultType="java.lang.Integer" databaseId="sqlserver"> select count(*) from dbo.sysobjects where id = object_id(upper(#{tableName})) and OBJECTPROPERTY(id, N'IsUserTable') = 1 </select> </mapper>
- 测试用例。
@Autowired private MultiDbTestMapper mapper; @Test public void testName() throws Exception { Console.log(mapper.getMtHisTableExists("xxx_boot", "xxx_code")); }
- 用例执行结果:
- mybatis将根据实际配置的数据库地址,从相同id的映射语句(
<select>/<insert>/<update>/<delete>
)中抓取相对应的进行执行。 - 以上操作由mybatis自行完成,使用者只需要正确配置数据库链接即可。无需其他额外操作。
- mybatis将根据实际配置的数据库地址,从相同id的映射语句(
2. 原理解析
先说结论:
- mybatis在启动阶段,根据数据源的配置,将xml映射文件中对应数据库类型的 xml节点语句加载到内存中,以供之后的调用。
- 以上述"getMtHisTableExists"为例,只会有其中一个被加载到内存中。
源码解读:
-
与当前环境下所使用的DataBase相适应的databaseId的获取。
//================= XMLConfigBuilder.databaseIdProviderElement() , 对应于上述测试用例中mybatis-config.xml的配置 // 1. 以下方法将为 Configuration 中的 databaseId 字段赋值。 // 2. 赋值逻辑大致是通过用户注册的自定义DatabaseIdProvider实现类来完成 // 3. DatabaseIdProvider默认实现为 VendorDatabaseIdProvider . 其中databaseId的获取逻辑为:根据实际的DataSource类型来获取配置的对应databaseId private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility if ("VENDOR".equals(type)) { // 在 Configuration 的构造函数中注册 type = "DB_VENDOR"; } Properties properties = context.getChildrenAsProperties(); databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance(); databaseIdProvider.setProperties(properties); } Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { // 从实际使用的数据源中获取当前适用的databaseId String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } }
-
databaseId生效。
//================= XMLStatementBuilder.parseStatementNode() public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); // 如果当前 <select>/<update>/<insert>/<delete>节点上的 databaseId 属性值 与 全局配置上的 databaseId 不一致, 则直接跳过对其的解析, 不会将其加载到内存中 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } String nodeName = context.getNode().getNodeName(); ...... } private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { if (requiredDatabaseId != null) { return requiredDatabaseId.equals(databaseId); } if (databaseId != null) { return false; } id = builderAssistant.applyCurrentNamespace(id, false); if (!this.configuration.hasStatement(id, false)) { return true; } // skip this statement if there is a previous one with a not null databaseId MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2 return previous.getDatabaseId() == null; }
3. databaseId的其它应用
-
databaseId的另外一种使用方式:
@Select
注解。// 注解的方式实现SQL, 效果等同于上述测试用例中的映射文件 // 底层支撑类: MapperAnnotationBuilder.AnnotationWrapper @Select(value = "select count(*) ICOUNT from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=#{dbName} and TABLE_NAME=upper(#{tableName})", databaseId = "mysql") @Select(value = "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=upper(#{tableName})", databaseId = "oracle") int getMtHisTableExistsByAnnotation(@Param("dbName") String dbName, @Param("tableName") String tableName);
-
通过占位符,在映射语句中获取当前的databaseId。(支撑类为
DynamicContext
)<select id="getMtHisTableExists" resultType="java.lang.Integer" databaseId="oracle"> SELECT '${_databaseId}' as databaseId FROM USER_TABLES WHERE TABLE_NAME=upper(#{tableName}) </select>
4. Links
- spring boot项目中mybatis plus多数据库支持
- MyBatis之databaseIdProvider多数据库支持
这篇关于Mybatis源码研究之DatabaseIdProvider的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器
- 2024-11-26Java云原生资料:新手入门教程与实战指南
- 2024-11-26JAVA云原生资料入门教程
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程