Springboot 同时支持不同的数据库,Oracle,Postgresql

关键字

Java,Springboot,Vscode,支持多种数据库

背景环境

我在实际项目开发工程中遇到这样一个问题,用户 A 使用 Oracle 数据库,用户 B 使用 Postgresql 数据库,但是用户 AB 都使用我们的项目。所以项目必须同时支持 Oracle 和 Postgresql。目前,我们项目的开发流程是这样的,Oracle 版本先行开发,然后再把代码手动合并到 Postgresql,测试修改 Postgresql 的 sql 部分。这个过程极大地增加了维护成本。

原因分析

那有没有更好的解决方案呢?首先,两个版本部分 sql 是可以共用的。然后能不能省去合并代码的过程?通过一些尝试,通过 Mybatis 指定**databaseId**可以实现我想要的效果。

解决过程

1. 添加依赖

确保 `pom.xml` 中已引入 MyBatis-Spring-Boot-Starter 依赖:

```xml

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>3.0.3</version> <!-- 使用最新版本 -->

</dependency>

```

2. 配置 VendorDatabaseIdProvider Bean

在 Spring Boot 的配置类中注册 `VendorDatabaseIdProvider`:

```java

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;

import org.apache.ibatis.mapping.DatabaseIdProvider;

import org.apache.ibatis.mapping.VendorDatabaseIdProvider;

@Configuration

public class MyBatisConfig {

@Bean

public DatabaseIdProvider databaseIdProvider() {

VendorDatabaseIdProvider provider = new VendorDatabaseIdProvider();

Properties properties = new Properties();

properties.setProperty("Oracle", "oracle");

properties.setProperty("PostgreSQL", "postgresql");

provider.setProperties(properties);

return provider;

}

// 选配

//@Bean

//public ConfigurationCustomizer configurationCustomizer() {

// return configuration -> configuration.setDatabaseId("default");

//}

}

```

3. 配置多数据源

`application.yml` 示例:

```yaml

spring:

datasource:

oracle 数据库

db1:

jdbc-url: jdbc:oracle:thin:@localhost:1521:ORCL

username: user

password: pass

driver-class-name: oracle.jdbc.OracleDriver

postgresql 数据库

db2:

jdbc-url: jdbc:postgresql://localhost:5432/mydb

username: user

password: pass

driver-class-name: org.postgresql.Driver

```

4. 在 Mapper XML 中使用 databaseId

示例分页查询:

```xml

<!-- Oracle 分页 -->

<select id="selectUsers" resultType="User" databaseId="oracle">

SELECT * FROM (

SELECT tmp.*, ROWNUM row_num FROM (

SELECT * FROM users ORDER BY id

) tmp WHERE ROWNUM <= #{end}

) WHERE row_num >= #{start}

</select>

<!-- PostgreSQL 分页 -->

<select id="selectUsers" resultType="User" databaseId="postgresql">

SELECT * FROM users ORDER BY id LIMIT #{limit} OFFSET #{offset}

</select>

```

5. 动态 SQL 适配

使用 `_databaseId` 变量:

```xml

<select id="selectDate" resultType="string">

SELECT

<choose>

<when test="_databaseId == 'oracle'">

TO_CHAR(sysdate, 'YYYY-MM-DD') AS current_date

</when>

<when test="_databaseId == 'postgresql'">

TO_CHAR(NOW(), 'YYYY-MM-DD') AS current_date

</when>

<otherwise>

CURRENT_DATE

</otherwise>

</choose>

FROM dual

</select>

```

6. 验证配置

方法 1:日志配置

```yaml

logging:

level:

org.mybatis: DEBUG

```

方法 2:代码获取

```java

@Autowired

private SqlSessionFactory sqlSessionFactory;

public void printDatabaseId() {

String databaseId = sqlSessionFactory.getConfiguration().getDatabaseId();

System.out.println("Current DatabaseId: " + databaseId);

}

```

关键点分析

显式指定 PostgreSQL 的 databaseId

```java

@Configuration

public class MyBatisConfig {

@Bean

public DatabaseIdProvider databaseIdProvider() {

VendorDatabaseIdProvider provider = new VendorDatabaseIdProvider();

Properties properties = new Properties();

properties.setProperty("PostgreSQL", "postgresql");

properties.setProperty("Oracle", "oracle");

provider.setProperties(properties);

return provider;

}

}

```

强制设置默认 databaseId 为 PostgreSQL

```java

@Configuration

public class MyBatisConfig {

// 强制指定默认 databaseId 为 postgresql

@Bean

ConfigurationCustomizer configurationCustomizer() {

return configuration -> configuration.setDatabaseId("postgresql");

}

}

```

**效果说明**:

  • 无论实际连接的数据库类型如何,MyBatis 会始终使用 `databaseId="postgresql"`

  • XML 中无 databaseId 的 SQL 会被忽略

  • 需要确保所有 SQL 都标记了 `databaseId="postgresql"`

实际使用

若希望保留多数据库支持,同时让 PostgreSQL 作为默认数据库,最佳实践是**为所有 PostgreSQL 专用的 SQL 显式标记 databaseId="postgresql",并保留无 databaseId 的 SQL 作为"通用后备"**(适用于简单兼容场景)。

```xml

<!-- 通用 SQL(兼容 PostgreSQL 和 Oracle) -->

<select id="getAllUsers" resultType="User">

SELECT * FROM users

</select>

<!-- PostgreSQL 专用 -->

<select id="getUser" resultType="User" databaseId="postgresql">

SELECT * FROM users WHERE id = #{id}

</select>

<!-- Oracle 专用 -->

<select id="getUser" resultType="User" databaseId="oracle">

SELECT * FROM users WHERE ROWNUM = 1

</select>

```

**效果说明**:

  • 当连接 PostgreSQL 时:

  • 执行 getUser 时使用 databaseId="postgresql" 的 SQL。

  • 执行 getAllUsers 时使用无 databaseId 的 SQL。

  • 当连接 Oracle 时:

  • 执行 getUser 时使用 databaseId="oracle" 的 SQL。

  • 执行 getAllUsers 时使用无 databaseId 的 SQL。

总结

这个方法我使用两周了,总的来说实现效果和我预想中一样,我在同一个分支做兼容,省去了大量合并代码,测试的工作量,别的不说,这个项目至少节省了20%~30%的工作量。

还有一种情况会经常用到:

```xml

SELECT

<if test="_databaseId == 'oracle'">ROWNUM,</if>

<if test="_databaseId == 'postgresql'">ROW_NUMBER() OVER() as rownum</if>

FROM table1

```

相关推荐
l1t31 分钟前
在DuckDB中使用http(s)代理
数据库·网络协议·http·xlsx·1024程序员节·duckdb
十碗饭吃不饱43 分钟前
RuoYi/ExcelUtil修改(导入excel表时,表中字段没有映射上数据库表字段)
数据库·windows·excel
李小白661 小时前
Redis常见指令
数据库·redis·缓存
Slow菜鸟1 小时前
SpringBoot集成Elasticsearch | Elasticsearch 7.x专属HLRC(High Level Rest Client)
spring boot·elasticsearch·jenkins
Slow菜鸟2 小时前
SpringBoot教程(安装篇):Elasticsearch及可视化工具安装(Windows环境)
spring boot·elasticsearch
忧郁的橙子.2 小时前
IntelliJ IDEA 2023中为 Spring Boot 项目添加注释模板
java·spring boot·intellij-idea
聆风吟º2 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
雨奔2 小时前
Flask 学习路线图
数据库·学习·flask
TDengine (老段)2 小时前
从“事后抢险”到“事前防控”:江西水投用 TDengine 时序数据库重塑防汛模式
大数据·数据库·物联网·时序数据库·tdengine·涛思数据·1024程序员节
友友马3 小时前
『 QT 』QT控件属性全解析 (二)
开发语言·数据库·qt