若依框架从MySQL迁移到达梦数据库的完整实战指南
简介若依框架与达梦数据库的集成源码包面向需要在国产化环境下搭建企业级应用的Java后端开发者解决将若依默认数据源切换到达梦后的适配与配置难题。压缩包共635个文件、4.66MB以253个Java核心代码、124个HTML页面、83个JS脚本、30个XML配置文件为主同时含SQL脚本、YML配置及Druid数据源调整相关文件覆盖后端逻辑、前端页面、数据库初始化与项目配置等关键环节。已有3032人学习下载。读者能从中掌握达梦JDBC驱动引入、数据源参数修改、SQL方言兼容、事务与异常处理、索引及数据库参数调优等实操方法可直接在信创项目中参考复用有效缩短国产数据库迁移与对接周期。 最近接到一个比较特殊的改造任务把若依框架RuoYi-Vue 前后端分离版整套从默认的 MySQL 迁到达梦数据库。刚开始我以为这就是改个连接串的事毕竟大家平时换库基本都是换 driver、改 url、然后祈祷一次过。真动起来才发现若依对 MySQL 的隐性绑定远不止数据源配置光 SQL 方言和代码生成器就花了我将近一周的休息时间。这篇文章就把我实际改造的完整源码思路、踩坑经过和可复用的适配方案整理出来给准备做类似集成的朋友一个参考。这个方案适合谁手里有若依源码、但被要求必须跑在达梦数据库上的开发者正在做国产化适配、需要把 Spring Boot MyBatis 项目迁到达梦的人以及那些想搞清楚“若依到底有多少地方绑死 MySQL”的好奇派。下文用的若依版本是 RuoYi-Vue 3.8.x 前后端分离版数据库是达梦 DM8JDK 1.8Spring Boot 2.5 系列如果你手里的版本和我不同核心思路照样通用。1. 迁移前的伏笔若依对MySQL的绑定到底有多深先说结论若依换成达梦难点不在 Spring Boot而在 SQL 方言、自增列语法、分页方言、代码生成器四块。这四块任何一个没处理干净项目都能跑起来但总会在某个业务页面给你颜色看。我习惯在动工前先盘点“绑定点”把风险摸清楚再动手。若依和 MySQL 的绑定关系大概有六处我列了个表绑定位置默认实现迁移影响数据源配置com.mysql.cj.jdbc.Driver必须替换为达梦驱动Druid 校验 SQL 也要改初始化脚本ry_2023.sql等ENGINEInnoDB、AUTO_INCREMENT等语法必须改写Mapper XML大量 MySQL 函数和分页写法IFNULL、DATE_FORMAT、LIMIT等需要兼容处理PageHelper 分页helper-dialect: mysql要改成 dm 方言否则分页 SQL 会拼错代码生成器查询information_schema达梦没有该视图必须换系统字典表Quartz 定时任务使用qrtz_系列 SQL达梦安装目录自带 Quartz 建表脚本可直接替换这个表的意义在于它告诉你不止是配置层的问题。很多人改到一半发现“SQL 报错怎么越来越多”就是因为只改了数据源后面几个点完全没排进计划。我的迁移策略是分四条线并行推进数据库实例准备、依赖与数据源改造、Mapper SQL 转换、代码生成器适配。跑通主流程后再回头处理 Quartz 和细节报错。下面按这个顺序一步步说。2. 建库建表与依赖置换先把地基铺对2.1 达梦实例与用户准备达梦安装完成后默认端口是5236安装路径下有DM管理工具类似 Navicat 的图形界面。第一次建议直接用管理工具操作命令行方式等熟练了再用。在管理工具里执行下面这段 SQL创建独立用户避免所有表都堆在 SYSDBA 下面CREATE USER RUOYI IDENTIFIED BY Ruoyi_123; GRANT DBA TO RUOYI;这里我直接给了 DBA 权限图省事方便调试。生产环境建议按最小权限分配但达梦对普通用户建表、建视图、建序列的限制比 MySQL 细碎很多权限不足会不断报 “无效的表或视图名”“权限不足”排查起来很耗时间。先 DBA 跑通流程再收敛权限是我一贯的做法。需要注意一个细节达梦默认会把不带引号的标识符转成大写存储所以用户名RUOYI实际存的是大写。后面 JDBC 连接串里如果用schemaRUOYI大小写必须对应否则会报无效的模式名。2.2 初始化 SQL 的达梦改写若依原始 SQL 是纯 MySQL 风格比如sys_user表开头是这样的create table sys_user ( user_id bigint(20) not null auto_increment comment 用户ID, dept_id bigint(20) default null comment 部门ID, user_name varchar(30) not null comment 用户账号, ... primary key (user_id) ) engineinnodb auto_increment100 comment 用户信息表;这段拿到达梦执行基本一行都过不去。达梦不认auto_increment、不认engineinnodb、不认行内comment。我按达梦语法重写成了CREATE TABLE sys_user ( user_id BIGINT IDENTITY(100, 1) NOT NULL, dept_id BIGINT, user_name VARCHAR(30) NOT NULL, nick_name VARCHAR(30) NOT NULL, user_type VARCHAR(2) DEFAULT 00, email VARCHAR(50) DEFAULT , phonenumber VARCHAR(11) DEFAULT , sex CHAR(1) DEFAULT 0, avatar VARCHAR(100) DEFAULT , password VARCHAR(100) DEFAULT , status CHAR(1) DEFAULT 0, del_flag CHAR(1) DEFAULT 0, login_ip VARCHAR(128) DEFAULT , login_date DATETIME, create_by VARCHAR(64) DEFAULT , create_time DATETIME, update_by VARCHAR(64) DEFAULT , update_time DATETIME, remark VARCHAR(500) DEFAULT , PRIMARY KEY (user_id) ); COMMENT ON TABLE sys_user IS 用户信息表; COMMENT ON COLUMN sys_user.user_id IS 用户ID; COMMENT ON COLUMN sys_user.dept_id IS 部门ID; -- 后续字段注释类似不再赘述自增列这里用了IDENTITY(100, 1)等价于 MySQL 的AUTO_INCREMENT100起始值从 100 开始步长 1。这是达梦最省事的自增方案。若依初始化脚本里几乎所有表都带自增值记得统一处理。如果嫌一个个改COMMENT ON麻烦也可以写个正则批量替换把行尾comment xxx摘出来生成COMMENT ON语句。我当时是手工加了前几张表后面几十张表用脚本生成的不推荐纯手工容易漏。2.3 pom 依赖与驱动来源若依默认的ruoyi-admin模块里引了mysql-connector-java要换掉。把 MySQL 驱动从 pom 中剔除替换成达梦驱动dependency groupIdcom.dameng/groupId artifactIdDmJdbcDriver18/artifactId version8.1.3.62/version scopesystem/scope systemPath${project.basedir}/lib/DmJdbcDriver18.jar/systemPath /dependency为什么用systemscope 而不是普通依赖因为达梦的 JDBC 驱动不会默认发布到 Maven 中央仓库很多内网环境根本拉不到这个包。把DmJdbcDriver18.jar直接从达梦安装目录一般在%DM_HOME%/drivers/jdbc/下拷贝到项目lib目录再用systemPath引入是我验证过最不容易出问题的方式。如果你不愿意在 pom 里写死路径更规范的做法是先用命令手动装进本地仓库mvn install:install-file -DfileDmJdbcDriver18.jar -DgroupIdcom.dameng -DartifactIdDmJdbcDriver18 -Dversion8.1.3.62 -Dpackagingjar装完后再用普通依赖坐标引用。两种方案都能跑但 systemPath 对团队协作更友好——同事拉代码后不用先执行 install 命令。还要提醒一点DmJdbcDriver18 对应 JDK 1.8若依默认就是 JDK 1.8没问题。如果你项目里是 JDK 7 的老环境得换 DmJdbcDriver17别搞混。依赖置换完成后处理 druid 连接池和 pagehelper 配置这个放到第 4 节专门讲。3. Mapper SQL方言转换从sys_user到quartz表的逐段排查3.1 字段类型和建表语法对照这一节是工作量的大头。若依所有核心表都在ry_2023.sql这类初始化脚本里我转完后发现真正需要改的字段类型其实不多因为达梦对 MySQL 常见类型的兼容度比预料中高。MySQL 类型达梦建议写法说明bigint(20)BIGINT长度标识可去掉达梦不强制int(11)INT同上varchar(30)VARCHAR(30)完全兼容datetimeDATETIME达梦支持也可用TIMESTAMPtinyint(1)TINYINT兼容但代码里可能转 Boolean需留意textTEXT或CLOB建议直接上CLOB兼容性最好longtextCLOB若依里gen_table的建表语句常用 longtext必须改真正麻烦的是 MySQL 的行内COMMENT语法以及带索引前缀长度的写法。MySQL 里允许KEY idx_user_name (user_name(20))这种前缀索引达梦不支持必须去掉括号里的长度直接建普通索引CREATE INDEX idx_user_name ON sys_user(user_name);3.2 Mapper XML 中常见函数替换若依的 Mapper XML 里藏着不少 MySQL 函数跑起来后报错会一个接一个。我整理了一份高频替换表MySQL 写法达梦兼容写法出现位置举例IFNULL(a, b)NVL(a, b)或COALESCE(a, b)各种列表查询的默认值DATE_FORMAT(t, %Y-%m-%d)TO_CHAR(t, YYYY-MM-DD)导出、统计类 SQLGROUP_CONCAT(x)LISTAGG(x, ,)代码生成器字典查询NOW()/SYSDATE()SYSDATE插入和更新时间LIMIT a, bOFFSET a ROWS FETCH NEXT b ROWS ONLY尽量不要手写交给 PageHelper这里我想展开说一下。很多朋友一看到LIMIT报错就冲进 Mapper 里改 SQL方向错了。若依的分页功能统一走 PageHelperstartPage()会自动拦截 SQL 并追加方言所以最优先做的是把pagehelper.helper-dialect配置成dm。只有那些 Mapper XML 里手动写了LIMIT 1的场景才需要人工处理。比如若依里某个查询后端逻辑需要取一条数据原始 MySQL 是select idselectConfigByKey resultMapSysConfigResult select * from sys_config where config_key #{configKey} limit 1 /select这种要看运气达梦部分版本兼容LIMIT 1但为了稳我会改成等价的达梦写法select idselectConfigByKey resultMapSysConfigResult select * from sys_config where config_key #{configKey} fetch first 1 rows only /selectFETCH FIRST 1 ROWS ONLY是 SQL 标准写法达梦完全支持也不会和 PageHelper 的分页逻辑冲突。3.3 Quartz 定时任务表若依自带 Quartz 定时任务启动时要求qrtz_开头的一堆表存在。MySQL 版本的建表脚本同样不能直接用。达梦安装目录下其实自带了 Quartz 建表脚本路径一般在%DM_HOME%/dmdbms/samples/quartz/quartz_DM.sql直接用这个脚本建表最省心省得手工改LONG VARCHAR、BLOB这些类型。若依的 Quartz 逻辑本身不涉及数据库方言表一建任务调度就能跑。如果找不到这个脚本也可以把 MySQL 脚本里的engineinnodb和comment全删掉varbinary改VARBINARY或BLOB手工处理也能过。4. 数据源与分页插件配置让Druid和PageHelper认识达梦4.1 application-druid.yml 改造若依的数据源配置文件是application-druid.yml默认指向 MySQL。我改造后的核心片段如下spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: dm.jdbc.driver.DmDriver druid: url: jdbc:dm://127.0.0.1:5236?schemaRUOYI username: ruoyi password: Ruoyi_123 # 初始连接数 initial-size: 5 # 最小空闲连接数 min-idle: 5 # 最大连接数 max-active: 20 # 获取连接等待超时时间 max-wait: 60000 # 校验 SQL达梦建议用 DUAL validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false # 连接池监控统计 filters: stat,wall,log4j2两个重点。第一个是 URL 里的schemaRUOYI。达梦的连接串可以不带 schema默认使用用户名对应的模式但如果你在用户名上踩了大小写坑显式指定 schema 是最直观的排查方式。第二个是validation-query。MySQL 默认是SELECT 1达梦其实也支持但我见过一些低版本驱动在连接初始化时对SELECT 1处理有兼容问题统一改成SELECT 1 FROM DUAL最稳妥不影响性能。filters里的wall要特别注意。Druid 的防火墙插件对达梦的语法支持不像对 MySQL 那么完善个别 SQL 会被误判为非法 SQL 拦截。如果你启动时看到类似sql injection violation, part alway true condition not allow这种异常优先怀疑 wall filter临时把filters改成stat验证一下确认是误杀后再决定要不要保留。4.2 PageHelper 方言配置若依的application-druid.yml里本来就有这一段pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: countcountSql直接把helper-dialect从mysql改成dm就行pagehelper: helper-dialect: dm reasonable: true support-methods-arguments: true params: countcountSql有人会问PageHelper 不是号称能自动识别数据库类型吗为什么还要手动指定我实测下来jdbc:dm这个 URL 在 PageHelper 的自动识别里经常失效它会把达梦误判成未知库然后默认走标准 JDBC 分页或直接拼错 SQL。手动指定dm一劳永逸。如果你不想依赖 Spring Boot Starter 的自动装配也可以在mybatis-config.xml里手动注册PageInterceptorplugins plugin interceptorcom.github.pagehelper.PageInterceptor property namehelperDialect valuedm/ property namereasonable valuetrue/ /plugin /plugins两种方式二选一不要同时配两遍否则分页会被执行两次列表接口返回的 total 和 pages 会异常。4.3 打包时别把驱动丢在外面用systemPath引入的 jar 在打包阶段容易被 spring-boot-maven-plugin 忽略最终打出的可执行 jar 里没有达梦驱动启动就报ClassNotFoundException。解决办法是在spring-boot-maven-plugin里加上includeSystemScope配置plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration includeSystemScopetrue/includeSystemScope /configuration /plugin不配置这个本地 IDE 跑得通java -jar直接翻车。这个坑我当时找了半小时差点以为驱动文件损坏。5. 代码生成器适配从information_schema切换到系统视图5.1 问题根源若依的代码生成器是它的一大卖点但也是迁到达梦后最容易崩的地方。它默认通过information_schema.tables和information_schema.columns来读取数据库里有哪些表、字段类型、注释等信息。达梦没有information_schema或者说不提供与 MySQL 完全一致的information_schema语义所以打开代码生成页面时表列表经常是空的或者直接报错。这个问题的典型报错长这样com.dameng.jdbc.DMException: 第 1 行, 第 100 列附近出现错误: 表或视图不存在: information_schema.tables5.2 改成达梦字典表达梦兼容 Oracle 风格的数据字典视图用user_tab_comments和user_tab_columns可以替代information_schema。我直接改了GenTableMapper.xml里查询表列表的 SQL。原 MySQL 写法大概是这样select table_name, table_comment, create_time, update_time from information_schema.tables where table_comment is not null and table_schema ruoyi改成达梦SELECT t.table_name, c.comments AS table_comment, NULL AS create_time, NULL AS update_time FROM user_tables t LEFT JOIN user_tab_comments c ON t.table_name c.table_name WHERE c.comments IS NOT NULL ORDER BY t.table_name注意user_tables和user_tab_comments查出来的都是大写表名。如果初始化建表时统一没加引号那么表名都是大写的查询没问题。如果哪张表当初是用双引号建的小写名这里就找不到需要额外处理。查询表字段的 SQL 也要改。默认的 Mapper 查information_schema.columns来获得列名、类型、长度、注释达梦版可以写成SELECT c.column_name, c.data_type, c.data_length, c.nullable, c.column_id, cm.comments AS column_comment FROM user_tab_columns c LEFT JOIN user_col_comments cm ON c.table_name cm.table_name AND c.column_name cm.column_name WHERE c.table_name #{tableName} ORDER BY c.column_id这套 SQL 和 Oracle 基本一样跑一次就能把若依代码生成器的表结构元数据读取拉回来。5.3 GenUtils 的类型映射也要补代码生成器拿到列类型后会走GenUtils.java里的方法把数据库类型映射成 Java 类型。varchar、bigint、datetime这些常见类型两边都有不需要动。但达梦里返回的类型名可能是CLOB、VARCHAR2、TIMESTAMP这类如果GenUtils里没有对应映射生成代码时会出现Unsupported data type或直接生成Object字段。我在GenUtils的columnTypeToJavaType方法里补过一段映射if (clob.equals(columnType) || text.equals(columnType) || longtext.equals(columnType) || varchar2.equals(columnType)) { return String; } else if (timestamp.equals(columnType) || datetime.equals(columnType)) { return Date; } else if (number.equals(columnType) || decimal.equals(columnType)) { return BigDecimal; }改完重新启动进入“系统工具-代码生成”选择表后点“预览”基本就能正常生成 controller、service、mapper 等全套代码了。6. 启动联调实录三个高频报错与定位过程最后分享几个我在实际跑通过程中真实撞上的问题。这三个问题非常有代表性如果你也是从 MySQL 迁达蒙大概率会碰见其中一个。6.1 启动时 Druid 初始化失败Failed to execute validationQuery先出现的是这个com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed validateConnection false意思是连接池校验失败了。一开始我怀疑是账号密码问题但用 DM 管理工具用同样的用户名密码连接是正常的。后来逐项排查发现问题出在validation-query。当时配置还留着 MySQL 时代的SELECT 1改成了SELECT 1 FROM DUAL启动立刻恢复。这个问题的误导性在于有些版本下SELECT 1也能通过导致不少人把排查方向带偏到用户名密码上。6.2 复杂 SQL 报 LIMIT 语法错误启动成功后打开“系统管理-用户管理”列表接口直接报dm.jdbc.driver.DMException: 第 1 行, 第 200 列, LIMIT 附近出现错误: 语法分析出错一看这个报错第一反应该是 PageHelper 方言没生效而不是急着去改 Mapper。我先检查application-druid.yml发现pagehelper.helper-dialect还是mysql。改成dm后重启这个报错就消失了。但紧接着又出现第二个关联问题若依的用户列表 SQL 本身没有手写 LIMIT而我在排查过程中把某个 Mapper 里的LIMIT 1手动改成了FETCH FIRST 1 ROWS ONLY所以后续没再触发同类问题。建议思路是先确保 PageHelper 方言正确再逐个排查 XML 里有没有裸写LIMIT不要反着来。6.3 代码生成页面不显示任何表登录系统后打开代码生成页面发现列表空空如也。这个方法我刚才已经写过根因就是information_schema不存在。但在实际排查时这个报错有迷惑性——因为前端接口返回的是空数组不仔细看后端日志根本不知道是 SQL 执行失败。我是在后台日志里看到表或视图不存在: information_schema.tables才锁定位置的。把GenTableMapper.xml里的查询换成user_tables和user_tab_comments后页面立刻正常。补了GenUtils的 CLOB 映射后代码生成预览也恢复正常。6.4 联调验证清单改造完成后我没有只测一个登录就收工而是按业务主链路过了一遍建议你也照着验证登录、验证码校验、权限菜单展示新增用户、分配角色、重置密码部门、岗位、字典的增删改查使用代码生成器生成一套包含单表查询的代码并跑起来手动触发一次定时任务确认qrtz_表正常写入删除一条带关联数据的记录确认事务回滚没有异常这里面的任何一步有问题多半还是 SQL 方言或类型映射的问题回到第 3 节和第 5 节的表格里对照排查即可。整个迁移做下来我最深的体会是换数据库这件事真正的成本不在“连接”而在“语义”。若依的代码里悄悄依赖了太多 MySQL 专属语法这些不会在编译期暴露只会在运行时一点点冒出来。把 Mapper SQL、分页方言、代码生成器三个核心点一次改到位后面反而顺风顺水。如果你也在迁达梦希望这份源码级的改造记录能帮你把一周的活压缩成一天。本文还有配套的精品资源点击获取