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

Mybatis-Plus入门系列(20) -兼容多种数据库

有道无术,术尚可求,有术无道,止于术。

文章目录

    • 前言
    • 方案分析
      • 1. 分页
      • 2. XML自定义SQL
    • 案例演示
      • 1. 配置
      • 2. 简单分页查询
      • 3. 带方言的分页查询
    • 参考

前言

在我们实际开发软件产品过程中,数据库的类型可能不是确定的,也有客户会有要求必须用什么数据库,比如很多政府机构要求必须使用国产数据库,所以我们在开发时,需要适配多种数据库。

MySQLOraclePostgreSQL、达梦等数据库在进行增删改查时,都是基于美国国家标准局制定的SQL标准,比如SQL-92SQL-99

但是每个数据库厂商实际的SQL会有较小差异,也就是数据库方言,大家最熟知的就是MySQL分页使用limit,Oracle分页使用rownum`。

MyBatis-Plus支持各种标准 SQL 的数据库,接下来我们实际演示如何使用MyBatis-Plus适配各种数据库。
在这里插入图片描述

方案分析

1. 分页

很多数据库分页SQL使用方式都不大相同,MyBatis-Plus内置分页插件PaginationInnerInterceptor已支持多种数据库,官网说明:
在这里插入图片描述

在使用内置分页插件时,可以设置数据库的类型:

@Configuration
@MapperScan("com.pearl.pay.mapper") //持久层扫描
@EnableTransactionManagement //启用事务管理
public class MybatisPlusConfig {@Bean@ConditionalOnMissingBean(MybatisPlusInterceptor.class)public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setDbType(DbType.MYSQL);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}

内置分页插件在执行SQL时,会根据当前数据库类型获取分页方言
在这里插入图片描述
分页方言工厂类DialectFactory中,可以看到具体获取方言逻辑,mysqlmariadbclickhouseoceanbase等数据库都是使用mysql方言。
在这里插入图片描述
oracle达梦数据库用的是oracle方言:
在这里插入图片描述
MYSQL 数据库分页语句使用LIMIT组装:
在这里插入图片描述
ORACLE 数据库分页语句使用ROWNUM、ROW_ID组装:
在这里插入图片描述
综上: 在分页时,适配多种数据库只需要在分页插件中设置数据库类型即可。

2. XML自定义SQL

调用MPAPI进行增删改查时,比如调用xxMpper.selectList()时,因为MP在构建SQL时,都是使用的基础标准,所以一般不存在兼容问题。但是我们自己在XML文件中编写SQL,就需要注意各种数据库匹配兼容问题了。

Mybatis本身已经做了多数据库支持,只需要告诉框架用的是什么数据库,可以根据不同的数据库厂商执行不同的语句。

Mybatis中的DatabaseIdProvider (数据库厂商标识提供者)接口声明了获取厂商标识的方法,标识可用于以后为每种数据库类型构建不同的查询,该机制支持多个厂商或版本。

public interface DatabaseIdProvider {default void setProperties(Properties p) {// NOP}// 根据数据源获取数据库厂商标识String getDatabaseId(DataSource dataSource) throws SQLException;
}

Mybatis也提供了VendorDatabaseIdProvider实现类:

public class VendorDatabaseIdProvider implements DatabaseIdProvider {// 支持的数据库厂商(需要自己定义),比如: Oracle=》oracleprivate Properties properties;// 获取数据库厂商标识ID(databaseId),eg:oracle@Overridepublic String getDatabaseId(DataSource dataSource) {if (dataSource == null) {throw new NullPointerException("dataSource cannot be null");}try {return getDatabaseName(dataSource);} catch (Exception e) {LogHolder.log.error("Could not get a databaseId from dataSource", e);}return null;}@Overridepublic void setProperties(Properties p) {this.properties = p;}// 根据产品名称,获取对应的databaseId。private String getDatabaseName(DataSource dataSource) throws SQLException {String productName = getDatabaseProductName(dataSource);if (this.properties != null) {for (Map.Entry<Object, Object> property : properties.entrySet()) {if (productName.contains((String) property.getKey())) {return (String) property.getValue();}}// no match, return nullreturn null;}return productName;}// 从数据源中获取数据库产品名称,比如: Oracleprivate String getDatabaseProductName(DataSource dataSource) throws SQLException {try (Connection con = dataSource.getConnection()) {DatabaseMetaData metaData = con.getMetaData();return metaData.getDatabaseProductName();}}
}

MybatisXML中编写SQL时,有个databaseId属性,可以指定当前语句块属于哪个数据库类型,比如:

<mapper namespace="org.pearl.mybatis.demo.dao.UserMapper"><select id="selectOneById" resultType="org.pearl.mybatis.demo.pojo.entity.User" databaseId="mysql">select * from user where user_id = #{id}</select>
</mapper>

综上:我们只需要配置DatabaseIdProvider 中支持哪些数据库,然后在XML中针对每种数据库方言编写查询语句,并添加databaseId属性,Mybatis会在启动时获取数据源使用的哪个类型数据库,然后执行配置了当前数据库对应的语句

案例演示

1. 配置

搭建工程,集成MPOracleMysql很简单,这里就不赘述了。

在配置文件中,添加对应的OracleMysql连接地址:

spring:datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: oracle.jdbc.OracleDriverurl: jdbc:oracle:thin:@127.0.0.1:1521:ORCLusername: rootpassword: root#driver-class-name: com.mysql.cj.jdbc.Driver#url: jdbc:mysql://127.0.0.1:3306/d_account?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai#username: root#password: root

MP中添加配置类:

@Configuration
@MapperScan("com.pearl.pay.mapper") //持久层扫描
@EnableTransactionManagement //启用事务管理
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(DataSource dataSource,DatabaseIdProvider databaseIdProvider) throws SQLException {// MP插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 获取当前数据源对应的数据库类型,添加分页插件String databaseId = databaseIdProvider.getDatabaseId(dataSource);DbType dbType = DbType.getDbType(databaseId);paginationInnerInterceptor.setDbType(dbType);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}@Beanpublic DatabaseIdProvider databaseIdProvider() {// 数据库厂商提供者DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();Properties p = new Properties();p.setProperty("Oracle", "oracle");p.setProperty("Mysql", "mysql");databaseIdProvider.setProperties(p);return databaseIdProvider;}
}

2. 简单分页查询

因为MyBatis-Plus内置分页插件已经做了适配,简单的(没有数据库方言)分页查询不用自己写代码适配。

首先添加一个分页查询:

    IPage<User> test(Page<User> page);    
    <select id="test" resultType="com.pearl.entity.User">select * from user</select>

数据源配置的是Oracle,没有配置databaseId,测试SQL语句打印如下:

SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( select * from user) TMP WHERE ROWNUM <=10) WHERE ROW_ID > 0

数据源配置为Mysql,测试SQL语句打印如下:

select * from user LIMIT 10

3. 带方言的分页查询

需求查询时间节点小于当前时间的数据。

Oracle当前时间使用的是sysdate函数,Mysql使用的是now()函数,这个时候就需要手动去兼容了。

编写两个重名的查询语句,针对不同的数据库厂商编写SQL,并配置对应的databaseId

    <select id="test" resultType="com.pearl.entity.User" databaseId="mysql">select * from user t <![CDATA[ where t.create_time <= now()]]></select><select id="test" resultType="com.pearl.entity.User" databaseId="oracle">select * from user t  <![CDATA[ where t.create_time  <=  sysdate ]]></select>

也可以使用if语句,判断当前数据库类型,添加不同语句(推荐)。

    <select id="test" resultType="com.pearl.entity.User">select * from user t<where><if test="_databaseId == 'mysql'"><![CDATA[ AND t.create_time <= now()]]></if><if test="_databaseId == 'oracle'"><![CDATA[ AND t.create_time  <=  sysdate ]]></if></where></select>

切换数据库,执行如下:

select * from user t where t.create_time <= now() LIMIT 10
SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( select * from user t where t.create_time <= sysdate ) TMP WHERE ROWNUM <=10) WHERE ROW_ID > 0

参考

接下来我们简单了解下OracleMysql的一些区别,便于开发。

数据类型对照:

数据库对比项类型
MySQL数据类型INTEGER、SMALLINT、TINYINT、MEDIUMINT、BIGINT
Oracle数据类型number
MySQL日期和时间date、timestamp、timestamp
Oracle日期和时间date、timestamp
MySQL字符类型char、varchar
Oracle字符类型char、varchar、varchar2、nvarchar、nvarchar2
MySQL大字段LONGTEXT
Oracle大字段clob

常用函数对照:

数据库对比项函数
MySQL获取字符串长度char_length(str)
Oracle获取字符串长度length(str)
MySQL生成随机序列UUID()
Oracle生成随机序列sys_guid()
MySQL时间转换为字符串date_format(NOW(),‘%Y-%m-%d’)
Oracle时间转换为字符串to_char(sysdate, ‘YYYY-MM-DD’)
MySQL字符串型时间转换为时间str_to_date(‘2019-01-01’,‘%Y-%m-%d’)
Oracle字符串型时间转换为时间to_date(‘2019-01-01’, ‘YYYY-MM-DD’)
MySQL包含时分秒的函数转换date_format(NOW(),‘%Y-%m-%d %H:%i:%s’)
Oracle包含时分秒的函数转换str_to_date(‘2019-01-01’,‘%Y-%m-%d %H:%i:%s’)
MySQL当前时间now()
Oracle当前时间sysdate

其他:

数据库对比项支持
MySQL引号双引号和单引号
Oracle引号只能识别单引号
MySQL字符串连接符concat()函数
Oracle字符串连接符可用双竖线连接字符串
MySQL分页limit
Oracle分页rownum

相关文章:

Mybatis-Plus入门系列(20) -兼容多种数据库

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录前言方案分析1. 分页2. XML自定义SQL案例演示1. 配置2. 简单分页查询3. 带方言的分页查询参考前言 在我们实际开发软件产品过程中&#xff0c;数据库的类型可能不是确定的&#xff0c;也有客户…...

JetPack板块—Android X解析

Android Jetpack简述 AndroidX 是Android团队用于在Jetpack中开发&#xff0c;测试&#xff0c;打包&#xff0c;发布和版本管理的开源项目。相比于原来的Android Support库,AndroidX 可以称得上是一次重大的升级改进。 和Support库一样&#xff0c;AndroidX与Android 操作系…...

C++学习笔记-数字

当我们使用数字时&#xff0c;通常我们使用原始数据类型&#xff0c;例如 int&#xff0c;short&#xff0c;long&#xff0c;float 和 double 等。数字数据类型&#xff0c;它们的可能值和取值范围在讨论 C 数据类型时已经解释了。 C 定义数字 我们已经在之前笔记的各种实例…...

Nginx——Nginx的基础原理

摘要 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,是一个高性能的HTTP和反向代理服务器&#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服务器。Nginx 是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的&#xff0c;它已经在该站点运行超过两年半了。…...

服务端开发Java之备战秋招面试篇1

在这个面试造火箭工作拧螺丝的时代背景下&#xff0c;感觉不是很好&#xff0c;不过还好也是拿到了还行的offer&#xff0c;准备去实习了&#xff0c;接下来就是边实习边准备秋招了&#xff0c;这半年把&#xff08;技术栈八股文面经算法题项目&#xff09;吃透&#xff0c;希望…...

【C++的OpenCV】第三课-OpenCV图像加载和显示

我们开始学习OpenCV一、OpenCV加载图片和显示图片1.1 imread()函数的介绍1.2 cv::namedWindow()函数的介绍1.4 imshow()函数介绍1.5 Mat容器介绍二、 代码实例&#xff08;带注释&#xff09;2.1 代码2.2 执行结果一、OpenCV加载图片和显示图片 本章节中&#xff0c;将会学习到…...

【面试1v1实景模拟】Spring事务 一文到底

老面👴:小伙子,了解Spring的事务吗? 解读🔔:这个必须了解,不了解直接挂~😂😂😂,但面试官肯定不是想听你了解两个字,他是想让你简单的介绍下。 笑小枫🍁:了解,事务在逻辑上是一组操作,要么执行,要不都不执行。主要是针对数据库而言的,比如说 MySQL。为…...

Neuron Selectivity Transfer 原理与代码解析

paper&#xff1a;Like What You Like: Knowledge Distill via Neuron Selectivity Transfercode&#xff1a;https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/NST.py本文的创新点本文探索了一种新型的知识 - 神经元的选择性知识&#xff0c…...

vue项目关闭子页面,并更新父页面的数据

今天下午是一个非常痛苦的&#xff0c;想要实现一个功能&#xff1a; 父页面打开了一个新的页面&#xff08;浏览器打开一个新的窗口&#xff09;&#xff0c;并在子页面提交数据之后&#xff0c;父页面的数据要同步更新。 难点&#xff1a;父页面是一个表格列表&#xff0c;…...

第五次作业:修改redis的配置文件使得windows的图形界面客户端可以连接redis服务器

1. 安装 Redis 依赖 Redis 是基于 C语言编写的&#xff0c;因此首先需要安装 Redis 所需要的 gcc 依赖&#xff1a; yum install -y gcc tcl 2、上传安装文件 将下载好的 redis-6.2.7.tar.gz 安装包上传到虚拟机的任意目录&#xff08;一般推荐上传到 /usr/local/src目录&am…...

【11】FreeRTOS的延时函数

目录1.延时函数-介绍2.相对延时函数-解析2.1函数prvAddCurrentTaskToDelayedList-解析2.3滴答定时器中断服务函数xPortSysTickHandler()-解析2.4函数taskSWITCH_DELAYED_LISTS() -解析3.延时函数-实验4.总结1.延时函数-介绍 函数描述vTaskDelay()相对延时xTaskDelayUntil()绝对…...

Vue页面组成及常用属性

一、Vue页面组成 目前的项目中&#xff0c;Vue页面都是采用组件套娃的形式&#xff0c;由一个一个的组件拼接而成整个页面。一个组件就是一个.vue文件。组件通常由template和script两部分组成&#xff1a; template部分&#xff1a;页面展示的具体元素内容&#xff0c;比如文字…...

j6-IO流泛型集合多线程注解反射Socket

IO流 1 JDK API的使用 2 io简介 输入流用来读取in 输出流用来写出Out 在Java中&#xff0c;根据处理的数据单位不同&#xff0c;分为字节流和字符流 继承结构 java.io包&#xff1a; File 字节流&#xff1a;针对二进制文件 InputStream --FileInputStream --BufferedInputStre…...

创业能否成功?这几个因素很重要!

创业能否成功&#xff1f;这几个因素很重要&#xff01; 2023-02-22 19:06:53 大家好&#xff0c;我是你们熟悉而又陌生的好朋友梦龙&#xff0c;一个创业期的年轻人 上周末跟朋友一起钓鱼&#xff0c;他跟吐槽现在生意越来越难做。他是我身边可以说是创业很成功的例子&#…...

Bmp图片格式介绍

Bmp图片格式介绍 介绍 BMP是英文Bitmap&#xff08;位图&#xff09;的简写&#xff0c;它是Windows操作系统中的标准图像文件格式&#xff0c;能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的开发&#xff0c;BMP位图格式理所当然地被…...

Day4 leetcode

Day4 啊啊啊啊&#xff0c;什么玩意&#xff0c;第一次因为测评没过&#xff0c;约好的面试取消了&#xff0c;好尴尬呀&#xff0c;还有一家厦门的C/C电话面&#xff0c;是一家我还挺喜欢的公司&#xff0c;面的稀烂&#xff0c;只能安慰自己我现在手上至少有一个offer 有效括…...

Java设计模式-原型模式

1、定义 原型模式是一种创建型模式&#xff0c;用于创建重复的对象&#xff0c;并且保证性能。原型模式创建的对象是由原型对象自身创建的&#xff0c;是原型对象的一个克隆&#xff0c;和原型对象具有相同的结构和相同的值。 2、适用场景 创建对象时我们不仅仅需要创建一个新…...

2023年度最新且最详细Ubuntu的安装教程

目录 准备ISO镜像 1.去官网下载镜像&#xff0c;或者找有镜像源的网站下载 阿里云镜像站 2. 如果服务器是打算直接把底层系统安装为Ubuntu的话还需制作系统U盘 安装 1.新建虚拟机调整基础配置 2.打开电源&#xff0c;进入安装界面&#xff08;到这一步就跟u盘安装步骤一致…...

unix高级编程-fork之后父子进程共享文件

~/.bash_profile:每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件. 这里我看到的是centos的操作&#xff0c;但我用的是debian系的ubuntu&#xff0c;百度了一下发现debian的在这里…...

vue+echarts:柱状图横向展示和竖向展示

第021个点击查看专栏目录本示例是显示柱状图&#xff0c;分别是横向展示和纵向展示。关键是X轴和Y轴的参数互换。 文章目录横向示例效果横向示例源代码&#xff08;共81行&#xff09;纵向示例效果纵向示例源代码&#xff08;共81行&#xff09;相关资料参考专栏介绍横向示例效…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...