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

个人博客:http://qinyangx.top/

相关推荐
小乌龟不会飞8 分钟前
Ubuntu 安装 etcd 与 etcd-cpp-apiv3
数据库·etcd
程序员张31 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端
计算机毕设定制辅导-无忧学长8 小时前
西门子 PLC 与 Modbus 集成:S7-1500 RTU/TCP 配置指南(一)
服务器·数据库·tcp/ip
麦兜*8 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
KK溜了溜了8 小时前
JAVA-springboot 整合Redis
java·spring boot·redis
大只鹅8 小时前
解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题
spring boot·后端·elasticsearch
天河归来8 小时前
使用idea创建springboot单体项目
java·spring boot·intellij-idea
程序员柳8 小时前
基于微信小程序的校园二手交易平台、微信小程序校园二手商城源代码+数据库+使用说明,layui+微信小程序+Spring Boot
数据库·微信小程序·layui
梦在深巷、9 小时前
MySQL/MariaDB数据库主从复制之基于二进制日志的方式
linux·数据库·mysql·mariadb
IT乌鸦坐飞机9 小时前
ansible部署数据库服务随机启动并创建用户和设置用户有完全权限
数据库·ansible·centos7