若依框架多数据源实战:同时连接 MySQL、SQL Server、Firebird 三种数据库
一套系统连三种数据库,不是炫技,是制造业数字化的真实刚需。这篇文章复盘我在若依框架上踩过的多数据源深坑——从事务不回滚到动态切换翻车,每一个坑都花了至少一天。
一、为什么一个系统要连三种数据库?
先交代背景。我们的 MES 系统要跑在印刷包装厂的环境里,这些工厂的 IT 现状通常是这样的:
| 数据库 | 用途 | 为什么不能动 |
|---|---|---|
| MySQL 8 | 我们自己的 MES 主库 | 新建的,可控 |
| SQL Server | 客户的旧 ERP 系统 | 用了 8 年,不敢动 |
| Firebird | 老旧的印刷行业软件 | 用得更久,更不敢动 |
老板的要求很简单:新系统不能要求客户换掉旧系统。数据必须在三套库之间流转——工单从 ERP 同步过来、生产数据写进 MES、部分统计还要回写到 Firebird 给老软件读。
这就叫异构数据库数据同步,听起来高大上,做起来全是坑。
注:本文基于若依框架(RuoYi Spring Boot 3 版本)的多数据源方案进行实战讲解。方案思路可复用到任何 Spring Boot 项目。
二、若依框架的多数据源原理
2.1 框架做了什么
若依框架内置了基于 dynamic-datasource 的多数据源支持。核心思路是注解切换:
1 | // 走主库 |
框架帮你做的事情:
- 通过 AOP 拦截
@DS注解,自动切换数据源 - 支持多数据源的事务管理
- 配合 Druid 连接池
看起来很简单对吧?接下来就是实际场景中爆出来的坑。
2.2 框架没做的事
若依的多数据源方案有个前提假设:所有库的表结构是你可控的。但实际情况是:
- SQL Server 里的表是别人建的,字段命名毫无规律
- Firebird 的表连文档都没有,全靠猜
- 三套库的数据类型不完全对应
这些才是真正的难度所在。
三、第一步:配置多数据源
3.1 数据源配置
1 | # application-druid.yml |
3.2 引入驱动依赖
1 | <!-- pom.xml --> |
⚠️ 坑 #1:Firebird 驱动版本混乱
Jaybird 4.x 需要 Java 11+,但有些老版本 Firebird 数据库(2.x)只能用 Jaybird 3.x 连接。你的厂里的 Firebird 是哪个版本?先问清楚,否则连上去就是
GDS Exception。
四、真正的大坑:多数据源事务管理
4.1 第一个坑:切换数据源后事务不回滚
这是最常见也最致命的问题。看这段代码:
1 | // ❌ 错误写法:切换数据源后,事务管理会乱 |
原因:Spring 的 @Transactional 默认绑定的是 master 的 TransactionManager。当你在 erp 数据源上执行查询后,erp 的事务已经独立提交,和 master 事务不在同一个事务管理器下。
4.2 正确做法:显式指定事务管理器
1 | // ✅ 正确写法 |
核心原则:跨数据库的强一致性基本做不到(除非上分布式事务,性能极差)。工程上的做法是 逐条同步 + 异常补偿:
1 | // 同步失败记录写入补偿表 |
五、第二个大坑:异构数据库的数据类型映射
5.1 字段类型不对齐
三个数据库的数据类型各有各的脾气:
| 数据类型 | MySQL | SQL Server | Firebird | 处理方式 |
|---|---|---|---|---|
| 日期时间 | datetime |
datetime |
TIMESTAMP |
统一转 LocalDateTime |
| 布尔 | tinyint(1) |
bit |
SMALLINT |
用 Integer 接收,代码层判断 |
| 长文本 | longtext |
nvarchar(max) |
BLOB SUB_TYPE TEXT |
统一用 String 接收 |
| 金额 | decimal(18,2) |
money |
NUMERIC(18,2) |
BigDecimal 统一处理 |
5.2 一个生产事故
SQL Server 的 money 类型映射到 Java 时,默认会变成 Double,导致精度丢失。一个客户的工单金额 12345.67 同步后变成了 12345.669999…
1 | // ❌ 错误做法 |
六、第三个坑:Firebird 的编码问题
Firebird 是一个小众数据库,但印刷行业有大量基于它的老旧软件。连接 Firebird 最容易翻车的是字符编码。
1 | // ❌ 连接字符串不指定编码,中文全部乱码 |
但问题来了:你不知道对方的 Firebird 是什么字符集建的。如果有源码或者备份文件,用 Firebird 的 gstat 工具查:
1 | gstat -h YOUR_DB.GDB | grep "Character set" |
如果拿不到,就用 GB2312 和 UTF8 各试一次,看哪次中文不出现”锟斤拷”。
七、增量同步策略
全量同步不现实,ERP 里的工单表可能几十万行。增量同步的关键是找到可靠的变更标识。
7.1 理想情况:有更新时间戳
1 | -- SQL Server 侧:查询最近更新的工单 |
7.2 现实情况:没有时间戳
印刷厂的旧 ERP,大概率没有 UPDATE_TIME 字段。此时方案是:
- 有自增 ID:用
MAX(id)做增量标记,只同步 ID 大于上次最大 ID 的记录 - 日期字段:用
CREATE_DATE做分区查询,每天定时同步当天的数据 - 什么都没有:全量查询 + 本地比对(MD5 或行哈希),只插入/更新变更记录
1 | // 本地比对方案 |
个人建议:如果对方允许,帮他们在 ERP 表上加一个
UPDATE_TIME字段 + 触发器自动更新。这比任何取巧方案都靠谱。
八、踩坑汇总
| 坑 | 现象 | 原因 | 解决 |
|---|---|---|---|
| 多数据源事务不回滚 | 切换数据源后异常不生效 | 跨 TransactionManager,AOP 切面顺序问题 |
显式指定 transactionManager,读写分离 |
| 跨库强一致性 | 分布式事务不支持 | 不同数据库物理隔离 | 逐条同步 + 失败日志 + 定时补偿 |
| SQL Server money 类型 | 精度丢失 | JDBC 默认映射为 Double | 强制用 BigDecimal 接收 |
| Firebird 编码 | 中文乱码 | 默认字符集不匹配 | URL 指定 encoding 参数,先用 gstat 查 |
| 异构表字段映射 | 字段名、类型都不对应 | 三套库各自为政 | 建一个 DTO 层做字段映射转换 |
| 驱动版本不兼容 | 连接报 GDS Exception | Firebird 数据库版本和 JDBC 驱动不对 | 先确认 Firebird 版本,再选 Jaybird 版本 |
九、总结
多数据源这件事,配通只需要半小时,跑稳需要半个月。核心经验:
- 别幻想分布式事务:跨库场景下,逐条同步 + 补偿机制是工程上最务实的方案
- 提前确认对方数据库的版本和字符集:尤其是 Firebird 这种小众数据库,接上去再发现不对,debug 成本极高
- 建一层 DTO 映射:不要把异构库的字段直接暴露给业务代码,中间加一层转换,出问题只改一处
- 增量同步是必须的:全量同步撑不过第一个月,设计时就考虑好增量标记字段
下篇预告:《从单体到模块化:若依多模块 Maven 项目架构实战》




