MySQL 迁移达梦数据库(DM)实践总结
# MySQL 迁移达梦数据库(DM)实践总结
# 背景
达梦数据库(DM)是国产关系型数据库,在政府、金融、能源等行业的信创项目中被广泛采用。在将原有基于 MySQL 的系统迁移至达梦时,SQL 层面存在较多语法差异,以下是项目实践中踩过的坑和解决方案汇总。
# 一、模式名与表名规范
# 1.1 模式名(Schema)强制前缀
达梦的表必须带上模式名前缀,格式为 "模式名"."表名"。
-- MySQL
SELECT * FROM RI_REG_TEXT_CLAUSE
-- 达梦(必须加模式名)
SELECT * FROM "lc_ri"."RI_REG_TEXT_CLAUSE"
2
3
4
5
模式名相当于 MySQL 的数据库名,连接时默认模式不一定与表所在模式一致,不加前缀会报"表或视图不存在"。
# 1.2 表名与字段名必须加双引号
达梦对标识符大小写敏感,建表时字段名如果未加引号会被自动转为大写;查询时如果不加引号,传入的小写字段名会匹配不到。
-- MySQL(不加引号正常)
SELECT regulation_id, publish_date FROM ri_reg_text
-- 达梦(字段和表名都需要加双引号)
SELECT "REGULATION_ID", "PUBLISH_DATE" FROM "lc_ri"."RI_REG_TEXT"
2
3
4
5
MyBatis XML 中的处理方式:
<!-- MySQL 写法 -->
<select id="queryList" resultType="RegText">
SELECT regulation_id, publish_date
FROM ri_reg_text
</select>
<!-- 达梦写法 -->
<select id="queryList" resultType="RegText">
SELECT "REGULATION_ID", "PUBLISH_DATE"
FROM "lc_ri"."RI_REG_TEXT"
</select>
2
3
4
5
6
7
8
9
10
11
批量替换时建议用脚本处理,手工改容易遗漏。
# 二、数据类型差异
# 2.1 布尔类型
MySQL 中常见用 char(1) 存 '0' / '1' 表示布尔,Java 代码中用 false / true 判断时 MySQL 会自动做隐式转换,但达梦不支持。
-- MySQL(可以识别)
WHERE GENERAL_ANNOUNCEMENT = false
-- 达梦(必须用字符串值)
WHERE "GENERAL_ANNOUNCEMENT" = '0'
2
3
4
5
解决方案:
- 数据库字段如果是
char(1),在 MyBatis 的<if>条件里用'0'/'1'做判断 - 如果字段本身是
BOOLEAN类型,则用0/1整数 - 建议在 Java 实体类中明确字段类型,避免框架自动转换失效
# 2.2 日期时间类型
达梦的 DATETIME / TIMESTAMP 与 MySQL 基本对应,但部分格式函数有差异:
-- MySQL 获取当前时间
NOW()
CURRENT_TIMESTAMP
-- 达梦(两者均支持,但建表时 DEFAULT CURRENT_TIMESTAMP 需注意)
CURRENT_TIMESTAMP -- ✅ 支持
SYSDATE -- ✅ 达梦特有,等同 Oracle 用法
2
3
4
5
6
7
建表语句中的坑:
-- MySQL(自动更新时间)
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
-- 达梦不支持 ON UPDATE CURRENT_TIMESTAMP,需要在应用层或触发器处理
"UPDATE_TIME" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2
3
4
5
迁移时要检查所有带
ON UPDATE CURRENT_TIMESTAMP的字段,改为在代码中手动赋值或写 DM 触发器。
# 2.3 数字类型:SIGNED / UNSIGNED
MySQL 的 SIGNED / UNSIGNED 修饰符达梦不支持:
-- MySQL(将字符串转为有符号整数)
CAST(b.TYPE_KEY AS SIGNED)
-- 达梦(改用 NUMERIC)
CAST("TYPE_KEY" AS NUMERIC)
2
3
4
5
# 2.4 字符串转换
-- MySQL
CAST(a.REGULATION_ID AS CHAR)
-- 达梦(改用 VARCHAR)
CAST("REGULATION_ID" AS VARCHAR)
2
3
4
5
# 三、函数差异
# 3.1 字符串拼接
-- MySQL(CONCAT 函数)
rsl.PATH_ID LIKE CONCAT('%', #{item}, '%')
-- 达梦(推荐用 || 拼接,CONCAT 也支持但可能有兼容性问题)
"PATH_ID" LIKE '%' || #{item} || '%'
2
3
4
5
# 3.2 常用函数对照
| 功能 | MySQL | 达梦 |
|---|---|---|
| 当前时间 | NOW() | SYSDATE 或 NOW() |
| 字符串长度 | LENGTH() | LENGTH() ✅ |
| 字符串截取 | SUBSTRING() | SUBSTR() 或 SUBSTRING() |
| 字符串拼接 | CONCAT() | \|\| 或 CONCAT() |
| 日期格式化 | DATE_FORMAT() | TO_CHAR(date, 'YYYY-MM-DD') |
| 字符串转日期 | STR_TO_DATE() | TO_DATE('2024-01-01', 'YYYY-MM-DD') |
| 空值替换 | IFNULL() | NVL() 或 COALESCE() |
| 条件表达式 | IF(cond, a, b) | CASE WHEN cond THEN a ELSE b END |
| 分页 | LIMIT offset, size | LIMIT size OFFSET offset |
| 自增主键 | AUTO_INCREMENT | IDENTITY(1, 1) 或序列 |
达梦在语法上更接近 Oracle,遇到 MySQL 特有函数时优先查是否有 Oracle 同名写法。
# 四、转义字符
达梦中单引号的转义是两个连续单引号 '',而不是反斜杠 \':
-- MySQL
INSERT INTO table VALUES ('it\'s a test')
-- 达梦
INSERT INTO "lc_ri"."TABLE" VALUES ('it''s a test')
2
3
4
5
MyBatis XML 中如果 SQL 有单引号,确保使用 '' 而非 \'。
# 五、MyBatis 兼容性注意事项
# 5.1 <if> 条件判断
达梦对布尔值判断不做隐式转换,<if test="flag == false"> 这类判断在达梦里可能失效,建议改为字符串或整数判断:
<!-- 不推荐(达梦下可能异常)-->
<if test="generalAnnouncement == false">
AND "GENERAL_ANNOUNCEMENT" = false
</if>
<!-- 推荐 -->
<if test="generalAnnouncement != null and generalAnnouncement == '0'">
AND "GENERAL_ANNOUNCEMENT" = '0'
</if>
2
3
4
5
6
7
8
9
# 5.2 <foreach> 批量操作
批量 INSERT 时 MySQL 支持 VALUES (..),(..),(..) 的多行语法,达梦同样支持,无需修改。
但批量 UPDATE 时 MySQL 的 CASE WHEN 写法达梦也支持,注意加上模式名和双引号。
# 5.3 resultMap 字段映射
达梦默认将未加引号建的列名存为大写,而 MyBatis 的 resultMap 中如果 column 写的是小写,可能匹配不上:
<!-- 可能匹配不上 -->
<result column="regulation_id" property="regulationId"/>
<!-- 明确大写或开启 mapUnderscoreToCamelCase -->
<result column="REGULATION_ID" property="regulationId"/>
2
3
4
5
# 六、迁移检查清单
迁移前逐项过一遍,可以省去大量排查时间:
- [ ] 所有 SQL 中的表名加上模式名前缀
"schema"."TABLE" - [ ] 所有表名、字段名加双引号
- [ ] 检查
ON UPDATE CURRENT_TIMESTAMP,改为应用层处理 - [ ] 替换
CAST(x AS SIGNED)→CAST(x AS NUMERIC) - [ ] 替换
CAST(x AS CHAR)→CAST(x AS VARCHAR) - [ ] 替换
CONCAT('%', x, '%')→'%' || x || '%' - [ ] 替换
DATE_FORMAT()→TO_CHAR() - [ ] 替换
IFNULL()→NVL() - [ ] 替换
IF(cond, a, b)→CASE WHEN - [ ] 检查布尔字段判断,统一用
'0'/'1' - [ ] 检查单引号转义
\'→'' - [ ] 检查 MyBatis resultMap 的 column 大小写
- [ ] 分页语法确认:
LIMIT size OFFSET offset - [ ] 自增主键:
AUTO_INCREMENT→IDENTITY或序列
# 七、连接配置(Spring Boot)
spring:
datasource:
driver-class-name: dm.jdbc.driver.DmDriver
url: jdbc:dm://192.168.18.39:5236?schema=lc_ri&compatibleMode=mysql
username: lc_ri
password: xxxxxx
2
3
4
5
6
compatibleMode=mysql可以开启达梦的 MySQL 兼容模式,部分函数(如IFNULL、DATE_FORMAT)在此模式下可以直接使用,减少改造工作量,但不能完全替代手工适配。
# 八、常见报错速查
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
| 表或视图不存在 | 未加模式名或表名大小写不对 | 加 "schema"."TABLE" |
| 列 xxx 不存在 | 字段名大小写不匹配 | 字段加双引号或全大写 |
| 不支持的数据类型 | SIGNED / CHAR 类型 | 改 NUMERIC / VARCHAR |
| 语法错误 near ON UPDATE | DM 不支持该语法 | 删除,改应用层处理 |
| 布尔类型不匹配 | false/true 隐式转换失败 | 改用 '0'/'1' |
| 日期函数不识别 | DATE_FORMAT 等 MySQL 特有函数 | 改 TO_CHAR |