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

```

相关推荐
ling-45几秒前
MySQL-运维篇
数据库·mysql
小汤猿人类8 分钟前
ES关系映射(数据库中的表结构)
大数据·数据库·elasticsearch
小杨40410 分钟前
springboot框架项目实践应用二十(扩展mybatis插件及原理解析)
spring boot·后端·mybatis
MacroZheng16 分钟前
狂揽77k star!GitHub官方支持的画图神器,用起来够优雅!
java·spring boot·后端
爱的叹息27 分钟前
关于 Spring Boot 监控方式的详细对比说明及总结表格
java·spring boot·后端
努力的搬砖人.43 分钟前
Spring Boot整合Kafka的详细步骤
spring boot·后端·kafka
Fanche40444 分钟前
SQL 语句基础(增删改查)
运维·数据库·sql·mysql·web安全·oracle
King.6241 小时前
SQL2API 核心理念:如何重构数据服务交付范式
大数据·开发语言·数据库·人工智能·sql·lua
YiSLWLL1 小时前
Tauri 2.3.1+Leptos 0.7.8开发桌面应用--Sqlite数据库的写入、展示和选择删除
数据库·vscode·rust·sqlite·sqlx
谦行1 小时前
前端视角 Java Web 入门手册 5.6:真实世界 Web 开发——Redis
java·spring boot·后端