news 2026/10/4 2:03:33

Spring Boot + MyBatis Plus 迁移达梦数据库(DM8)实战与踩坑记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot + MyBatis Plus 迁移达梦数据库(DM8)实战与踩坑记录

最近把一套跑在 MySQL 上的 Spring Boot 项目迁到了达梦数据库(DM8),底层 ORM 用的本来就是 MyBatis Plus,整个过程比预想中要折腾不少。Spring Boot + MyBatis Plus + 达梦数据库这套组合,很多文章只说“把 driver-class-name 换成 dm.jdbc.driver.DmDriver 就行”,但实际上分页、关键字、主键策略、驱动版本这些环节都会跳出来卡你一下。这篇就把我从环境准备、依赖配置到分页排查、批量操作、管理工具连库的完整过程写出来,希望可以帮正在做信创适配或数据库替换的人少走几个弯路。

1. 先搞清楚达梦数据库的定位:很多坑都出在“惯性思维”

1.1 达梦和 Oracle、MySQL 的真实关系

达梦不是 MySQL 的套壳,它更像 Oracle:有表空间、用户即模式(schema)、支持 PL/SQL 风格的过程和函数、有 ROWNUM 这类伪列。同时达梦也做了一套 MySQL 兼容模式,甚至允许你在 SQL 里使用反引号和 LIMIT。问题就出在这个“兼容”上,很多从 MySQL 迁过来的团队默认把它当 MySQL 用,结果在分页、批量 INSERT、字段类型、保留字上接连踩坑。

我的经验是:先确认当初初始化数据库实例时选的是哪种兼容模式(达梦安装时可以选 Oracle 模式还是 MySQL 模式),后面排查会轻松很多。如果建库时选的是 Oracle 模式,那 SQL 写法、工具行为都更贴近 Oracle;如果选了 MySQL 模式,LIMIT 这类语法会好用一些。不要把两种模式混在一起猜,不然你很难判断某个报错到底是语法问题还是驱动问题。

1.2 版本配套:Spring Boot 2.x 和 3.x 要分开选依赖

集成前最容易忽略的是版本对应关系。网上很多老教程里还是mybatis-plus-boot-starter,版本停在 3.4.x,如果你用的是 Spring Boot 3.2 甚至更高版本,是跑不起来的,会出现 AutoConfiguration 找不到、jar 包冲突或者连SqlSessionFactory都创建失败。结合搜索词里被频繁提到的“springboot版本太高”,这确实是这两年问得最多的问题。

我实际测试过的最低稳定组合是:

组件推荐版本说明
Spring Boot 2.7.x2.7.18用mybatis-plus-boot-starter,JDK 8 可跑,兼容性最稳
Spring Boot 3.2.x3.2.5 等必须用mybatis-plus-spring-boot3-starter,JDK 17 起步
MyBatis Plus3.5.5 以上内置DbType.DM达梦方言,分页插件直接支持
达梦 JDBC 驱动DmJdbcDriver18 8.1.2.x / 8.1.3.x尽量和达梦数据库小版本保持一致

如果你还在用旧的 MyBatis Plus 3.4.x,建议先升级,否则后面分页配置里可能找不到DbType.DM,只能被迫用DbType.MYSQL顶上,这种方案我不太推荐,因为达梦的 LIMIT 解析在特殊场景下和 MySQL 还是有差异。

1.3 达梦安装、实例初始化和管理工具的使用细节

达梦在 Windows 下安装比较简单,安装包解压后点安装程序,跟着向导走就行。真正需要注意的是这几个点:

  • 实例端口默认是 5236,不是 3306 也不是 1521。
  • 字符集建议选 UTF-8,除非你已经确定历史库是 GBK。
  • 大小写敏感这个选项会直接影响表名和数据;建议从第一天就统一用大写建表。
  • 初始密码、模式名这些信息要记好,后面连数据库、导数据都会用到。

有用户提到“达梦数据库 dm 管理工具没有对象导航栏”,我也遇到过。DM管理工具连接上数据库后,如果左侧对象导航栏整块不见了,先检查菜单栏里的“视图”相关选项,把对象导航窗口重新调出来;如果能看到导航栏但展开后没有表,通常是因为当前连接在模式下拉框里没有选中那个模式。达梦的用户和模式是绑定的,你用 SYSDBA 登录能看到所有模式,但要展开对应的用户模式才能看到表和视图。用其他业务账号登录时,要确认该账号对目标模式有访问权限,否则列表里是空的。

2. 项目里的依赖引入和数据源配置:细节都在配置里

2.1 Maven 依赖怎么加:达梦驱动大概率要手动装到本地仓库

先看 Spring Boot 2.7.x 项目里的依赖写法:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver18</artifactId> <version>8.1.2.192</version> </dependency>

如果用的是 Spring Boot 3.x,把 MyBatis Plus 依赖换成:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.5</version> </dependency>

重点说一下达梦驱动。DmJdbcDriver18这个 jar 很多时候不在公共 Maven 仓库里,你需要去达梦官网或者数据库安装目录的drivers/jdbc目录下找,找到后手动安装到本地仓库:

mvn install:install-file -Dfile=DmJdbcDriver18.jar -DgroupId=com.dameng \ -DartifactId=DmJdbcDriver18 -Dversion=8.1.2.192 -Dpackaging=jar

如果团队用私有 Nexus,直接上传到私服,避免每个人本地装一遍。另外驱动 jar 名字里的 18 不是数据库版本,而是 JDBC 规范版本,JDK 8 和 JDK 17 都能用 DmJdbcDriver18,JDK 1.6 那种老环境才需要去找 DmJdbcDriver16。

2.2 application.yml 里的数据源配置:driverClassName 是第一个坑

达梦的驱动类名是dm.jdbc.driver.DmDriver,不是com.mysql.cj.jdbc.Driver。配置如下:

spring: datasource: driver-class-name: dm.jdbc.driver.DmDriver url: jdbc:dm://127.0.0.1:5236 username: SYSDBA password: SYSDBA001 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000

这里有两个容易踩的点。第一,URL 默认写jdbc:dm://127.0.0.1:5236就可以,不需要像 MySQL 那样带库名;如果你的实例配置了服务名,可以在端口后加/服务名,但大多数情况下不加更稳。第二,达梦驱动对 URL 上的一些参数是能认的,但对 MySQL 的那套useSSL、serverTimezone完全不认,加上去反而可能让连接失败。

有人喜欢在这里加?useUnicode=true&characterEncoding=UTF-8,实际测试中达梦驱动对字符编码的处理主要看数据库实例的字符集,URL 参数不是必须的,反而可能因为参数不识别导致问题。真遇到乱码,先查数据库字符集和客户端的编码环境变量。

2.3 MyBatis Plus 方言配置和分页拦截器:必须显式写 DbType.DM

MyBatis Plus 3.5.5 已经内置了达梦方言,分页插件里可以直接指定DbType.DM。这个配置要单独建一个配置类,并且确保被 Spring Boot 扫描到:

@Configuration @MapperScan("com.example.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.DM); // 分页数量限制,按实际业务设置,不需要限制就保持默认 paginationInnerInterceptor.setMaxLimit(500L); // 超出总数时自动回退到最后一页 paginationInnerInterceptor.setOverflow(true); interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; } }

很多“分页失效”问题的根因就是这个拦截器没配置。如果拦截器缺失,MyBatis Plus 会走内存分页甚至直接报错。拦截器配了但分页方言用错,也会导致 SQL 生成不符合达梦语法。所以这里建议:无论你数据库实例用的是哪种兼容模式,代码里都统一用DbType.DM,让 MyBatis Plus 自己处理达梦方言,不要去猜它是不是兼容 MySQL。

3. 实体映射和建表规范:把 MySQL 思维切到达梦习惯

3.1 表名、字段名大小写与保留字:SQL 异常的常见来源

从 MySQL 迁到达梦,保留字问题几乎必然遇到。MySQL 里可以利用反引号把order、group、user这样的字段包起来,但达梦对反引号的支持要看兼容模式,不是所有模式都认。达梦的保留字体系和 Oracle 更接近,COMMENT、OPTION、CONDITION、LEVEL、ROWNUM、SIZE、VALUE、USER这些词在 SQL 里出现时都有被解析成关键字的风险。

保险做法是两点:

  1. 建表字段尽量避开保留字,从源头解决。
  2. 如果老表已经建成这样,实体里用@TableField显式转义:
@TableName("T_USER") public class User { @TableId(value = "ID", type = IdType.AUTO) private Long id; @TableField("USER_NAME") private String userName; // OPTION 是保留字,必须加双引号 @TableField("\"OPTION\"") private String option; }

注意这里用的是双引号,不是 MySQL 习惯的反引号。达梦在解析带双引号的标识符时,对大小写是敏感的:如果你建表时写的是"OPTION",那表里存储的列名就是大写;如果建表时没加引号,达梦默认也会转成大写存储。所以代码里统一按大写写,配合@TableField的双引号转义,基本不会出错。

3.2 主键策略:IDENTITY、ASSIGN_ID 和达梦自增列的关系

MyBatis Plus 默认主键策略是ASSIGN_ID,也就是应用侧生成雪花 ID,这个策略在达梦上最省事,因为它不依赖数据库自增机制。从 MySQL 迁过来的项目,如果原来主键是bigint auto_increment,在达梦里建表要用类似这样的语法:

CREATE TABLE T_USER ( ID BIGINT IDENTITY(1,1), USER_NAME VARCHAR(64), OPTION VARCHAR(128), PRIMARY KEY (ID) );

这时实体里的@TableId(type = IdType.AUTO)才能配合数据库自增。如果你的迁移工具把自增列映射成了普通BIGINT,但代码里又写成IdType.AUTO,插入的时候就会报错或者主键为 NULL。迁完后别急着跑业务,先对着表结构检查几张大表的主键是否真的是自增列。

生产环境里老数据主键往往不能乱变,如果主键由业务系统生成,建议直接在实体上标注IdType.INPUT,不要让它走默认的雪花策略,否则会出现插入的数据主键和外部系统对不上。

3.3 类型映射检查清单:这些字段迁移后最容易出问题

达梦的数据类型体系比 MySQL 更复杂,迁移工具虽然能帮你做映射,但映射完不等于能用。我整理过一份需要人工复查的类型清单:

MySQL 类型达梦常用类型注意点
tinyint / int / bigintINT / BIGINT / NUMBER长度和自增属性要核对
datetimeTIMESTAMP达梦 DATE 也带时间,但要看你建表时的具体类型
textTEXT / CLOBCLOB 在 XML 查询里的一些比较操作有限制
varchar(n)VARCHAR(n)达梦 VARCHAR 长度默认按字节还是字符要确认,会影响中文长度
decimalNUMERIC / NUMBER精度位数要核对,避免金额字段被截断
bit / booleanBIT / INT达梦没有完全等价的 boolean 语义,通常用 INT 0/1

迁移完数据后,我的建议是写一段“类型冒烟测试”:把每张表的字段都查一遍,用SELECT COUNT(1) WHERE 字段 IS NOT NULL之类的方式扫一遍,至少能发现隐式转换和长度截断的问题。真遇到异常,优先改表字段类型,不要在 XML 里写一堆CAST,那个维护成本太高。

4. 分页问题的完整排查链路:从“列表翻页异常”到定位根因

4.1 现象:总数不对、翻页超过某条数就报错、单页 500 条之后行为反常

迁移过程中最有意思的是分页问题。最初项目里几个列表接口的表现很怪:前几页正常,后面越翻越不对劲;有的接口返回总数和 count 总数不一致;还有一页查 500 条以内没问题,超过 500 条就报 SQL 错误。搜索“mybatisplus单页500条限制”“mybatisplus分页失效”的相关信息,基本都是在达梦或类似国产数据库上遇到的。

在动手改代码之前,我先确认了一件事:分页插件到底有没有生效?如果没生效,MyBatis Plus 的Page对象虽然能接住数据,但底层执行的是不带分页的原始 SQL,然后IPage在内存里做截断。这种模式下业务看结果是“有数据的”,但总数不准、页数越界,且数据量大了以后非常慢。

4.2 排查第一步:让 MyBatis Plus 把真实 SQL 打到控制台

像这种诡异问题,不要猜,直接看 SQL。我建议在测试环境把 MyBatis Plus 的日志打开:

mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这时候控制台会把 MyBatis Plus 生成并执行的 SQL 完整打出来,包括分页插件拼接后的语句和参数列表。拿到完整 SQL 之后,复制到 DBeaver 或 DM 管理工具里手工跑一遍,看看到底是达梦解析不了,还是返回结果本身有问题。

4.3 排查第二步:检查分页拦截器的配置和版本

日志确认 SQL 只是普通查询、完全没带分页关键字时,问题基本就是PaginationInnerInterceptor没注册。这种情况通常有两种原因:一是配置类被 Spring Boot 的组件扫描漏掉了;二是项目里改了自定义拦截器,分页拦截器没加进去。

另一种情况是 SQL 里带LIMIT但达梦报语法错误。这就要回溯到数据库初始化时的兼容模式了。如果你的达梦实例跑在 Oracle 兼容模式下,MySQL 风格的LIMIT offset, size不一定被支持;而旧版 MyBatis Plus 里如果你强行用DbType.MYSQL生成分页 SQL,很可能生成的就是这种语法。

我的配置方案是统一使用 MyBatis Plus 3.5.5 以上的版本,并且在拦截器里显式写DbType.DM。这个方言类会根据达梦支持的语法生成分页语句,比手动指定 MySQL 稳妥得多。

4.4 修复后的标准配置和验证建议

修复完配置之后,不要只点两下页面就觉得完事了,我建议验证这几项:

  1. 分别用第 1 页、中间页、最后一页调用接口,检查total、pages、current、size是否正确。
  2. 在调用接口时同时打印 count 查询 SQL 和数据查询 SQL,确认 count 语句没有分页关键字,数据语句有达梦风格的分页语法。
  3. 如果设置了setMaxLimit(500L),那么单页 size 大于 500 时会被拦截或报错,你自己要清楚这是业务限制而不是数据库 bug;如果你没设置过却出现 500 条上限,大概率是项目里有人加过配置,直接全局搜maxLimit就能找到。

我之前在排查中看到的“单页500条限制”现象,最后发现不是达梦数据库本身强制的,而是项目早期接入时有人在分页拦截器里配了setMaxLimit(500L)。这个配置是会真实生效的,超过 500 的查询可能被直接修正或报异常,所以遇到类似情况时,先在代码仓库里搜一下maxLimit,别一上来就怀疑数据库。

5. 批量操作、事务边界和 SQL 日志的落地姿势

5.1 saveBatch 到达梦之后的性能变化:别期望和 MySQL 一样

MyBatis Plus 的IService.saveBatch()在 MySQL 上表现不错,但在达梦上不一定。原因是 MySQL 驱动支持rewriteBatchedStatements=true时会把批量插入重写成多 VALUES 语句,达梦驱动的批处理行为不完全相同,而且我的测试里,同样一批数据在达梦上逐条执行的比例更高,插入耗时明显增加。

如果是偶发的小批量操作,直接用saveBatch问题不大;如果是定时任务、数据导入这种批量插入量大的场景,建议自己写 XML 里的批量插入:

<insert id="batchInsert" parameterType="list"> INSERT INTO T_USER(ID, USER_NAME, OPTION) VALUES <foreach collection="list" item="item" separator=","> (#{item.id}, #{item.userName}, #{item.option}) </foreach> </insert>

注意控制批次大小,我一般控制在 200 到 500 条一批,避免 SQL 过长超出达梦的参数上限或者触发事务日志暴涨。全量导入数据时,还可以关闭自动提交,分批次手动 commit。

5.2 事务边界与默认隔离级别:@Transactional 的默认行为要小心

达梦默认的隔离级别是读提交,和 MySQL 默认的可重复读不一样,但我们的业务里多数场景读提交反而够用。集成时更需要注意的是 Spring 事务的语义:

@Transactional(rollbackFor = Exception.class) public void doSomething() { // 业务逻辑 }

一定不要只写@Transactional不指定rollbackFor。Spring 默认只对RuntimeException和Error回滚,如果你在事务里抛了一个自定义的受检异常,事务不会回滚,数据库里会留下半截数据。这在 MySQL 时代也是这个规则,但达梦侧排查起来会更隐蔽,因为日志里看不到明显报错。

还有一个经验:不要把长报表查询、大数据量导出放在@Transactional里去跑。达梦的事务开销和 Oracle 类似,长事务会拖住 undo 空间和锁,批量操作和查询之间要严格分开。如果只是查询,直接不要加事务注解,让连接自动提交就好。

5.3 打开 SQL 日志,用达梦工具复现执行

前面提到的 SQL 日志配置,除了排错,还有一个重要用途:让你能随时把 MyBatis Plus 生成的 SQL 复制到数据库工具里验证。这一点在达梦上尤其重要,因为达梦没有 MySQL 那么丰富的在线调试生态,很多问题只能靠“拿真实 SQL 去管理工具里重新执行”来确认。

但生产环境一定不要开StdOutImpl,它会把 SQL 里的参数值全部打到日志文件里,可能包含用户手机号、身份证号之类的敏感数据。生产需要慢 SQL 分析时,可以通过数据库层面的 SQL 日志或者监控平台来做,应用侧只保留异常 SQL 的输出。

6. DBeaver、Navicat、达梦管理工具的连接和配合方式

6.1 DBeaver 手动注册达梦驱动

新版 DBeaver 部分版本内置了达梦驱动,但如果没有,手动添加也不复杂:

  1. 下载DmJdbcDriver18.jar。
  2. 在 DBeaver 菜单里选择“数据库” -> “驱动管理器” -> “新建”。
  3. 驱动名称填DM,驱动类填dm.jdbc.driver.DmDriver。
  4. URL 模板填jdbc:dm://{host}:{port}。
  5. 在“库”区域添加下载好的 jar 包。
  6. 新建连接,主机填数据库 IP,端口填 5236,用户名密码填达梦账号。

DBeaver 连接达梦最常见的坑是端口写错,或者驱动 jar 和数据库版本不对应。如果测试连接报“未找到驱动类”,先确认 jar 是否真的被 DBeaver 加载进去了。

6.2 Navicat 和达梦管理工具的配合

Navicat 连接达梦需要用支持达梦的版本,新建连接时选择达梦数据库类型,连接参数和 DBeaver 基本一致:IP、端口 5236、用户名、密码。如果你的 Navicat 版本较旧,新建连接列表里没有达梦,那就换 DBeaver 或者达梦自带的 DM 管理工具,没必要纠结。

达梦自带管理工具连接上之后,最容易被新手卡住的是“看不到表”。默认用 SYSDBA 登录后,左侧对象导航栏展开的是当前用户模式,如果你要看的表在业务模式,比如APPUSER下,那需要在导航栏上方的模式下拉框里选到APPUSER模式,表才会出现。如果整个对象导航栏都不显示,检查菜单栏里的视图开关,不要误以为数据库里没表。

6.3 迁移验证阶段的数据准备和备份恢复

为了不影响生产,迁移验证阶段我会先在一台独立环境里把达梦库搭好,然后用达梦的数据迁移工具 DTS 把 MySQL 的表结构和数据导过来。备份和还原用达梦的逻辑导入导出命令就可以:

dexp USERID=SYSDBA/SYSDBA@localhost:5236 FILE=backup.dmp FULL=Y
dimp USERID=SYSDBA/SYSDBA@localhost:5236 FILE=backup.dmp FULL=Y

当然生产环境建议用达梦的物理备份工具,这里只是说测试验证阶段用逻辑备份最快。还原 dmp 文件时,如果目标库里的模式不存在,可以先手工建好同名用户/模式,再用dimp导入,避免还原一半报用户名不存在。

另一件容易被忽略的事:迁移完数据后,先跑一轮 MyBatis Plus 的 Mapper 方法自测,把每个实体对应的selectPage、selectList、insert、updateById都执行一遍,重点观察有没有保留字、类型转换、主键冲突这几类问题。这种“全量扫 Mapper”看起来笨,但确实能提前暴露大部分兼容性问题,比业务侧手工点流程要高效得多。

最后再分享一个实际体会:在把 Spring Boot 项目切到达梦数据库这类国产数据库上时,最大的成本不是写代码,而是把团队里“一直默认是 MySQL”的隐性假设全部翻出来。分页方言、主键自增、字段保留字、批量插入、事务语义,每一项都要单独验证。把前面这些检查点过完,安装、编码、部署这套流程基本就能稳定重复了,之后再有新人加入,照着这份事项清单走,不会一头扎进“为什么数据库查不了第二页”的坑里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 2:03:30

Vite 为什么比 Webpack 快?前端构建工具选型与实战对比

我第一次认真体会 Vite 的优势&#xff0c;是在 2021 年切换 Vue 3 项目的时候。当时从 npm create 到浏览器打开页面&#xff0c;基本就是几秒钟的事&#xff0c;对比之前 Webpack 项目冷启动动辄三四十秒的“煎熬”&#xff0c;那种差距是直接体感层面的。后来我在多个团队里…

作者头像 李华
网站建设 2026/10/4 2:02:50

USB(三): 磁盘、卷、分区解释(适用于U盘,SD等移动存储)

磁盘、卷、分区解释 最近快被磁盘、卷、分区搞懵了&#xff0c;于是整理一些资料理清这三个概念 一、简介 磁盘&#xff1a;磁盘很好理解&#xff0c;就是平时我们用的存储设备&#xff0c;包括像U盘、电脑硬盘以及软盘。 分区&#xff1a;将磁盘划分为多个独立的存储区域&…

作者头像 李华
网站建设 2026/10/4 2:02:37

ai-guide 程序员成长指南:工作后快速突破职业瓶颈的六大方法

文档教程知识库人工智能 【免费下载链接】ai-guide 程序员鱼皮的 AI 资源大全 Vibe Coding 零基础教程&#xff0c;分享 OpenClaw 保姆级教程、大模型玩法&#xff08;DeepSeek / GPT / Gemini / Claude / GLM&#xff09;、最新 AI 资讯、Prompt 提示词大全、AI 知识百科&…

作者头像 李华
网站建设 2026/10/4 2:01:28

大气污染数值模式零基础入门:从WRF到WRF-Chem/CAMx全流程指南

如果你刚开始接触大气污染数值模式&#xff0c;最容易被一堆缩写砸晕&#xff1a;WRF、WRF-Chem、SMOKE、CAMx、CMAQ、WPS、排放清单、化学机理……每个词单独看都认识&#xff0c;凑在一起就像天书。我当年入坑的时候&#xff0c;光搞清楚这些模式之间的关系就花了两周&#x…

作者头像 李华