当前位置: 首页 > news >正文

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属性支持不同数据库的不同语法

目录 一、前言 二、如何配置 三、源码解读 四、自定义 一、前言 在一次项目功能测试中&#xff0c;发现有个sql在其他嵌入式数据库中执行正常&#xff0c;但是在mysql中执行失败&#xff0c;发现是因为有个字段在mysql中是关键字&#xff0c;需要使用反引号&#xff08;&…...

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&#xff1a;分离音频的人声和背景音乐 一、Spleeter 是什么&#xff1f; Spleeter 是由法国音乐流媒体公司 Deezer 开发并开源的一款基于深度学习的音频分离工具。它能够将音乐中的不同音轨&#xff08;如人声、鼓、贝斯、钢琴等&#xff09;分…...

深度学习、宽度学习、持续学习与终身学习:全面解析与其在大模型方面的应用

目录 引言&#xff1a; 1. 深度学习&#xff08;Deep Learning&#xff09; 1.1 深度学习的基本概念 1.2 深度学习的数学原理 1.3 深度学习的特点 1.4 深度学习在大模型中的应用 2. 宽度学习&#xff08;Wide Learning&#xff09; 2.1 宽度学习的基本概念 2.2宽度学习…...

【量化科普】Arbitrage,套利

【量化科普】Arbitrage&#xff0c;套利 &#x1f680;量化软件开通 &#x1f680;量化实战教程 什么是套利&#xff1f; 套利&#xff08;Arbitrage&#xff09;是金融市场中的一种交易策略&#xff0c;指的是在不同市场或不同形式中同时买入和卖出相同或相似的金融产品&a…...

删除已加入 .gitignore却仍被git追踪的文件

.gitignore 文件只会影响未被跟踪的文件&#xff0c;而已经被 Git 跟踪的文件不会因为被添加到 .gitignore 而停止被跟踪。 eg&#xff1a;例如在创建.gitignore文件前&#xff0c;已经将sync.sh文件推送到远程分支&#xff0c;因此该文件已被git追踪。 去掉sync.sh文件追踪的步…...

pytest框架 核心知识的系统复习

1. pytest 介绍 是什么&#xff1a;Python 最流行的单元测试框架之一&#xff0c;支持复杂的功能测试和插件扩展。 优点&#xff1a; 语法简洁&#xff08;用 assert 替代 self.assertEqual&#xff09;。 自动发现测试用例。 丰富的插件生态&#xff08;如失败重试、并发执…...

Spring Cloud Alibaba学习 5- Seata入门使用

Spring Cloud Alibaba学习 5- Seata入门使用 Seata是Spring Cloud Alibaba中用于分布式事务管理的解决方案 一. Seata的基本概念 1. Seata的三大角色 1> TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。TC作…...

WebAssembly技术及应用了解

WebAssembly&#xff08;Wasm&#xff09;是一种为Web设计的高效、低级的二进制指令格式&#xff0c;旨在提升Web应用的性能并支持多种编程语言。以下是对其核心概念、优势、应用场景及开发流程的系统介绍&#xff1a; 1. 核心概念 二进制格式&#xff1a;Wasm采用紧凑的二进制…...

Deepseek中的MoE架构的改造:动态可变参数激活的MoE混合专家架构(DVPA-MoE)的考虑

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

NodeJS学习笔记

NodeJS软件安装 node环境安装&#xff1a; https://nodejs.org 安装好后的node通常在C:\Program Files\nodejs验证安装是否成功 node -v npm -v 进入REPL模式命令行模式 nodeNodeJS在REPL模式和编辑器使用 windos在dos下常用命令 windos命令&#xff1a; 1、cmd dos系统2、…...

【交通网络拓扑图实现原理深度解析】

交通网络拓扑图实现原理深度解析 简易demo地址 背景故事&#xff1a;交通网络调度可视化的演进 1. 项目背景 在现代城市轨道交通系统中&#xff0c;交通网络线路的可视化展示一直是一个重要而复杂的问题。传统的交通网络线路图往往采用静态图片方式展示&#xff0c;这种方式…...

【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?

https://time.geekbang.org/column/article/118826 2.6 渲染流程&#xff08;下&#xff09;&#xff1a;HTML、CSS和JavaScript&#xff0c;是如何变成页面的&#xff1f; 2.5介绍了渲染流水线中的 DOM 生成、样式计算和布局三个阶段&#xff0c;2.6讲解渲染流水线后面的阶段…...

NO2.C++语言基础|C++和Java|常量|重载重写重定义|构造函数|强制转换|指针和引用|野指针和悬空指针|const修饰指针|函数指针(C++)

6. C 和 Java 区别&#xff08;语⾔特性&#xff0c;垃圾回收&#xff0c;应⽤场景等&#xff09; 指针&#xff1a; Java 语⾔让程序员没法找到指针来直接访问内存&#xff0c;没有指针的概念&#xff0c;并有内存的⾃动管理功能&#xff0c;从⽽有效的防⽌了 C 语⾔中的指针…...

【CSS】---- 纯 CSS 实现无限滚动轮播

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

软考架构师笔记-计算机网络

1.9 计算机网络 OSI/RM 七层模型 物理层 二进制传输(中继器、集线器) (typedef) 数据链路层 传送以帧为单位的信息(网桥、交换机、网卡) 网络层 分组传输和路由选择(三层交换机、路由器)ARP/RARP/IGMP/ICMP/IP 传输层 端到端的连接(TCP/UDP)在前向纠错系统中&#xff0c;当接…...

Spring MVC 页面重定向返回后通过nginx代理 丢失端口号问题处理

Spring MVC页面重定向通过Nginx代理后出现端口丢失问题&#xff0c;通常由以下原因及解决方案构成&#xff1a; #‌# 一、Nginx配置问题&#xff08;核心原因&#xff09;‌ ‌1. Host头传递不完整‌ Nginx默认未将原始请求的端口信息传递给后端&#xff0c;导致应用生成重定向…...

道可云人工智能每日资讯|亚马逊云业务部门成立智能体人工智能团队

道可云元宇宙每日简报&#xff08;2025年3月6日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 《杭州市富阳区未来产业培育行动计划(2025-2026年)》发布 3月3日&#xff0c;杭州市富阳区经信局正式发布了《杭州市富阳区未来产业培育行动计划(2025-2026年)》&…...

算力100问☞第72问:算力与算法、数据的关系是什么?

目录 1、数据是基础 2、算法是核心 3、算力是保障 4、三者的关系 5、实际应用中的体现 算力、算法和数据是人工智能和计算机科学领域的三个核心要素,它们之间相互依赖、相互促进,共同构成了现代计算系统的基础。以下是它们之间的关系: 1、数据是基础 定义:数据是信息…...

AI-Ollama本地大语言模型运行框架与Ollama javascript接入

1.Ollama Ollama 是一个开源的大型语言模型&#xff08;LLM&#xff09;平台&#xff0c;旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型&#xff0c;支持文本生成、翻译、代码编写、问答等多种…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

Easy Excel

Easy Excel 一、依赖引入二、基本使用1. 定义实体类&#xff08;导入/导出共用&#xff09;2. 写 Excel3. 读 Excel 三、常用注解说明&#xff08;完整列表&#xff09;四、进阶&#xff1a;自定义转换器&#xff08;Converter&#xff09; 其它自定义转换器没生效 Easy Excel在…...