Mybatis如何通过databaseId属性支持不同数据库的不同语法
目录
一、前言
二、如何配置
三、源码解读
四、自定义
一、前言
在一次项目功能测试中,发现有个sql在其他嵌入式数据库中执行正常,但是在mysql中执行失败,发现是因为有个字段在mysql中是关键字,需要使用反引号(``)包起来才行,但是,反引号在嵌入式数据库(如hsql)中是不支持的,这就导致,同一条sql具有不同的写法。
让一个项目支持不同的数据库在企业开发中是一个比较常见的需求。由于不同的数据库支持的sql语法稍有差别,所以某些功能需要根据数据库的不同书写不同的sql语句。对于这种需求,首先能够想到的解决方案就是针对不同的数据库维护不同的mapper.xml文件,但是这种方案会严重增加开发和维护的成本。因为不同数据库支持的语法大部分都是相同的,不同的毕竟是少数,我们希望只重写不同的部分而重用相同的部分。
针对这种情况,MyBatis提供了解决方案,即databaseIdProvider和databaseId。通过MyBatis提供的这种功能,我们就只需要维护一套mapper.xml文件便可。
下面先讲解如何配置,然后在从源码层面对这个功能进行解读,最后探讨一下如何通过自定义来定制这个功能。
二、如何配置
配置databaseIdProvider(在mybatis的配置文件中配置,比如mybatis-config.xml)
<databaseIdProvider type="DB_VENDOR"><property name="MySQL" value="mysql" /><property name="HSQL Database Engine" value="hsql" /><property name="DM DBMS" value="dm" /><property name="Derby" value="derby" />
</databaseIdProvider>
上述配置用于决定当前databaseId的名称。在每一个property标签中,name代表数据库的productName(DatabaseMetaData#getDatabaseProductName()),value是用户自定义的databaseId名称。mybatis在初始化的时候会根据所使用的数据源得到当前databaseId的名称,得到的databaseId的名称供mybatis选择映射文件中相应的语句。比如我们使用的是Mysql数据库,则得到的databaseId名称为“mysql”。
配置databaseId
<!-- mysql数据库生效 -->
<select id="select" resultMap="XXCResultMap" databaseId="mysql">
select name, status, passwords, `keys` from test
</select><!-- 非mysql数据库则生效 -->
<select id="select" resultMap="XXCResultMap" >
select name, status, passwords, keys from test
</select>
官方文档对databaseId的解释为:
“如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略”。
也就是说如果在mybatis的配置文件中没有配置 databaseIdProvider,则在映射文件中配置的databaseId不会生效。
由于我们使用的是Mysql数据库,得到的databaseId为“mysql”,所以上述的映射片段会在生效。
三、源码解读
上面我们讲解了如何配置,下面我们从源码的角度来剖析一下mybatis是如何实现这个功能的。
databaseIdprovider的默认实现是VendorDatabaseIdProvider,接口为DatabaseIdProvider。
先看DatabaseIdProvider的定义:
public interface DatabaseIdProvider {default void setProperties(Properties p) {}String getDatabaseId(DataSource var1) throws SQLException;
}
接口很简单,就两个方法。
void setProperties(Properties p) 该方法会把databaseIdprovider标签中配置的属性传递进来。
String getDatabaseId(DataSource dataSource) 该方法会将返回一个databaseId。
再看一下默认实现类VendorDatabaseIdProvider的源码:
package org.apache.ibatis.mapping;import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Properties;
import java.util.Map.Entry;
import javax.sql.DataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;public class VendorDatabaseIdProvider implements DatabaseIdProvider {private Properties properties;public VendorDatabaseIdProvider() {}public String getDatabaseId(DataSource dataSource) {if (dataSource == null) {throw new NullPointerException("dataSource cannot be null");} else {try {return this.getDatabaseName(dataSource);} catch (Exception var3) {VendorDatabaseIdProvider.LogHolder.log.error("Could not get a databaseId from dataSource", var3);return null;}}}public void setProperties(Properties p) {this.properties = p;}private String getDatabaseName(DataSource dataSource) throws SQLException {String productName = this.getDatabaseProductName(dataSource);if (this.properties != null) {Iterator var3 = this.properties.entrySet().iterator();Entry property;do {if (!var3.hasNext()) {return null;}property = (Entry)var3.next();} while(!productName.contains((String)property.getKey()));return (String)property.getValue();} else {return productName;}}private String getDatabaseProductName(DataSource dataSource) throws SQLException {Connection con = dataSource.getConnection();Throwable var3 = null;String var5;try {DatabaseMetaData metaData = con.getMetaData();var5 = metaData.getDatabaseProductName();} catch (Throwable var14) {var3 = var14;throw var14;} finally {if (con != null) {if (var3 != null) {try {con.close();} catch (Throwable var13) {var3.addSuppressed(var13);}} else {con.close();}}}return var5;}private static class LogHolder {private static final Log log = LogFactory.getLog(VendorDatabaseIdProvider.class);private LogHolder() {}}
}
看下不同数据库的驱动getDatabaseProductName()返回什么:
默认实现就是根据数据库的getDatabaseProductName去匹配在mybatis配置文件中配置的属性,然后选出databaseId。
至此,我们分析完了databaseIdprovider的源码,下面接着看一下databaseId是如何发挥作用的。mybatis在解析映射文件的时候,首先会根据mybatis的配置文件解析出当前数据库对应的databaseId,然后根据mybatis的映射文件判断配置了databaseId属性的语句是否和接卸出的databaseId匹配,核心代码如下:
// XMLStatementBuilder类中
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {if (requiredDatabaseId != null) {return requiredDatabaseId.equals(databaseId);} else if (databaseId != null) {return false;} else {id = this.builderAssistant.applyCurrentNamespace(id, false);if (!this.configuration.hasStatement(id, false)) {return true;} else {MappedStatement previous = this.configuration.getMappedStatement(id, false);return previous.getDatabaseId() == null;}}}
四、自定义
这里的自定义只是针对databaseIdProvider的自定义,对映射文件中的databaseId不能自定义也没自定义的必要。
通过上述的分析,可以看出,如果要自定义databaseIdProvider只要实现接口DatabaseIdProvider便可。然后在mybatis的配置文件中将databaseIdProvider的type置为自定义的实现便可。
自定义的databaseIdProvider为:
public class MyDatavbaseIdProvider implements DatabaseIdProvider {Properties props = null;@Overridepublic void setProperties(Properties p) {//p代表mybatis中针对databaseIdProvider配置的属性props = p;}@Overridepublic String getDatabaseId(DataSource dataSource) throws SQLException {//根据使用的数据源,返回不同的databaseId。这里没有给出具体实现return null;}
}
使用自定义的databaseIdProvider,则mybatis配置文件中的配置需要做相应的调整。
<databaseIdProvider type="xxx.MyDatavbaseIdProvider"><property name="DB2" value="db2" /><property name="Oracle" value="oracle" /><property name="Adaptive Server Enterprise" value="sybase" /><property name="MySQL" value="mysql" />
</databaseIdProvider>
或者直接用通过xml给VendorDatabaseIdProvider注入:
相关文章:

Mybatis如何通过databaseId属性支持不同数据库的不同语法
目录 一、前言 二、如何配置 三、源码解读 四、自定义 一、前言 在一次项目功能测试中,发现有个sql在其他嵌入式数据库中执行正常,但是在mysql中执行失败,发现是因为有个字段在mysql中是关键字,需要使用反引号(&…...
android edittext 防止输入多个小数点或负号
有些英文系统的输入法,或者定制输入法。使用xml限制不了输入多个小数点和多个负号。所以代码来控制。 一、通过XML设置限制 <EditTextandroid:id="@+id/editTextNumber"android:layout_width="wrap_content"android:layout_height="wrap_conten…...

windows部署spleeter 版本2.4.0:分离音频的人声和背景音乐
windows部署spleeter 版本2.4.0:分离音频的人声和背景音乐 一、Spleeter 是什么? Spleeter 是由法国音乐流媒体公司 Deezer 开发并开源的一款基于深度学习的音频分离工具。它能够将音乐中的不同音轨(如人声、鼓、贝斯、钢琴等)分…...

深度学习、宽度学习、持续学习与终身学习:全面解析与其在大模型方面的应用
目录 引言: 1. 深度学习(Deep Learning) 1.1 深度学习的基本概念 1.2 深度学习的数学原理 1.3 深度学习的特点 1.4 深度学习在大模型中的应用 2. 宽度学习(Wide Learning) 2.1 宽度学习的基本概念 2.2宽度学习…...
【量化科普】Arbitrage,套利
【量化科普】Arbitrage,套利 🚀量化软件开通 🚀量化实战教程 什么是套利? 套利(Arbitrage)是金融市场中的一种交易策略,指的是在不同市场或不同形式中同时买入和卖出相同或相似的金融产品&a…...
删除已加入 .gitignore却仍被git追踪的文件
.gitignore 文件只会影响未被跟踪的文件,而已经被 Git 跟踪的文件不会因为被添加到 .gitignore 而停止被跟踪。 eg:例如在创建.gitignore文件前,已经将sync.sh文件推送到远程分支,因此该文件已被git追踪。 去掉sync.sh文件追踪的步…...

pytest框架 核心知识的系统复习
1. pytest 介绍 是什么:Python 最流行的单元测试框架之一,支持复杂的功能测试和插件扩展。 优点: 语法简洁(用 assert 替代 self.assertEqual)。 自动发现测试用例。 丰富的插件生态(如失败重试、并发执…...

Spring Cloud Alibaba学习 5- Seata入门使用
Spring Cloud Alibaba学习 5- Seata入门使用 Seata是Spring Cloud Alibaba中用于分布式事务管理的解决方案 一. Seata的基本概念 1. Seata的三大角色 1> TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态,驱动全局事务提交或回滚。TC作…...
WebAssembly技术及应用了解
WebAssembly(Wasm)是一种为Web设计的高效、低级的二进制指令格式,旨在提升Web应用的性能并支持多种编程语言。以下是对其核心概念、优势、应用场景及开发流程的系统介绍: 1. 核心概念 二进制格式:Wasm采用紧凑的二进制…...

Deepseek中的MoE架构的改造:动态可变参数激活的MoE混合专家架构(DVPA-MoE)的考虑
大家好,我是微学AI,今天给大家介绍一下动态可变参数激活MoE架构(Dynamic Variable Parameter-Activated MoE, DVPA-MoE)的架构与实际应用,本架构支持从7B到32B的等多档参数动态激活。该架构通过细粒度难度评估和分层专家路由,实现“小问题用小参数,大问题用大参数”的精…...

NodeJS学习笔记
NodeJS软件安装 node环境安装: https://nodejs.org 安装好后的node通常在C:\Program Files\nodejs验证安装是否成功 node -v npm -v 进入REPL模式命令行模式 nodeNodeJS在REPL模式和编辑器使用 windos在dos下常用命令 windos命令: 1、cmd dos系统2、…...
【交通网络拓扑图实现原理深度解析】
交通网络拓扑图实现原理深度解析 简易demo地址 背景故事:交通网络调度可视化的演进 1. 项目背景 在现代城市轨道交通系统中,交通网络线路的可视化展示一直是一个重要而复杂的问题。传统的交通网络线路图往往采用静态图片方式展示,这种方式…...

【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
https://time.geekbang.org/column/article/118826 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的? 2.5介绍了渲染流水线中的 DOM 生成、样式计算和布局三个阶段,2.6讲解渲染流水线后面的阶段…...

NO2.C++语言基础|C++和Java|常量|重载重写重定义|构造函数|强制转换|指针和引用|野指针和悬空指针|const修饰指针|函数指针(C++)
6. C 和 Java 区别(语⾔特性,垃圾回收,应⽤场景等) 指针: Java 语⾔让程序员没法找到指针来直接访问内存,没有指针的概念,并有内存的⾃动管理功能,从⽽有效的防⽌了 C 语⾔中的指针…...

【CSS】---- 纯 CSS 实现无限滚动轮播
1. 前言 仅使用 CSS 创建一个具有无限滚动轮播的动画,无需 JavaScript。首先是无限滚动轮播动画效果在我们常见的开发中都是借用 JavaScript 实现,如果纯粹使用 CSS,我觉得还是一个比较有趣的。 2. 效果预览 3. 效果分析 一屏展示了三个图片元素;动画依次向左移动;三个图…...

软考架构师笔记-计算机网络
1.9 计算机网络 OSI/RM 七层模型 物理层 二进制传输(中继器、集线器) (typedef) 数据链路层 传送以帧为单位的信息(网桥、交换机、网卡) 网络层 分组传输和路由选择(三层交换机、路由器)ARP/RARP/IGMP/ICMP/IP 传输层 端到端的连接(TCP/UDP)在前向纠错系统中,当接…...
Spring MVC 页面重定向返回后通过nginx代理 丢失端口号问题处理
Spring MVC页面重定向通过Nginx代理后出现端口丢失问题,通常由以下原因及解决方案构成: ## 一、Nginx配置问题(核心原因) 1. Host头传递不完整 Nginx默认未将原始请求的端口信息传递给后端,导致应用生成重定向…...
道可云人工智能每日资讯|亚马逊云业务部门成立智能体人工智能团队
道可云元宇宙每日简报(2025年3月6日)讯,今日元宇宙新鲜事有: 《杭州市富阳区未来产业培育行动计划(2025-2026年)》发布 3月3日,杭州市富阳区经信局正式发布了《杭州市富阳区未来产业培育行动计划(2025-2026年)》&…...
算力100问☞第72问:算力与算法、数据的关系是什么?
目录 1、数据是基础 2、算法是核心 3、算力是保障 4、三者的关系 5、实际应用中的体现 算力、算法和数据是人工智能和计算机科学领域的三个核心要素,它们之间相互依赖、相互促进,共同构成了现代计算系统的基础。以下是它们之间的关系: 1、数据是基础 定义:数据是信息…...
AI-Ollama本地大语言模型运行框架与Ollama javascript接入
1.Ollama Ollama 是一个开源的大型语言模型(LLM)平台,旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型,支持文本生成、翻译、代码编写、问答等多种…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...