资讯动态

Java实现的MySQL到Oracle SQL脚本自动转换工具

发布时间:2026/10/9 12:32:44 来源:尧图企业网站定制
简介这是一款面向Java开发者与数据库运维人员的MySQL→Oracle脚本自动转换工具专为跨数据库迁移、系统升级及多DB兼容适配场景设计有效解决因语法差异、数据类型不一致、函数/存储过程写法不同导致的手动改写耗时易错问题。资源包共34个文件含11个核心Java源码涵盖SQL解析、DDL/DML语句映射、Oracle语法生成等模块、11个编译后class文件、6个关键依赖jar包如ojdbc14、mysql-connector-java、commons-dbutils等以及配置文件properties、项目元数据project、classpath和说明文档README.md整体3.01MB结构完整开箱即用。目前已有183人学习下载读者可直接运行或二次开发该工具获得可执行转换能力、清晰的Java数据库脚本处理范式、典型RDBMS差异对照实践案例以及从源码到依赖管理的完整工程化参考。1. 为什么一个 Java 写的 MySQL→Oracle 脚本转换工具能省掉 DBA 3 天手动改写时间你刚接手一个老系统迁移项目原库是 MySQL 5.7目标库是 Oracle 12c要迁移 87 张表、42 个存储过程、19 个视图和一堆触发器。DBA 拿着 SQL 脚本一条条改——AUTO_INCREMENT换成SEQUENCE TRIGGERVARCHAR(255)得加BYTE或CHAR语义LIMIT 10,20分页硬生生拆成三层嵌套ROWNUMINSERT INTO t SELECT ...里ON DUPLICATE KEY UPDATE直接没对应语法……改完一测建表失败、字段截断、日期函数报 ORA-01861返工三次后团队开始怀疑人生。这个.zip包里的 Java 工具就是专治这种「语法失语症」的止痛针它不碰数据、不连数据库、不依赖 Oracle 客户端只做一件事——把 MySQL DDL/DML 脚本文本按 Oracle 语法规范批量重写。不是简单字符串替换比如把int全替成number就翻车而是用 Java 构建轻量级 SQL 解析器识别CREATE TABLE结构、INSERT字段映射、ALTER变更意图再按 Oracle 的类型体系、约束规则、函数兼容表生成新脚本。适合开发自测阶段快速出迁移初稿也适合 DBA 拿来当改写草稿机——它不保证 100% 可执行但能把人工改写工作量从 3 天压到 2 小时剩下那 2 小时是留给人工校验和补漏的。提示这不是数据库同步工具不传数据也不是 ORM 映射框架不生成 Java 实体类更不是在线迁移服务不连 Oracle 实例。它就是一个命令行驱动的、基于 Java 的 SQL 文本翻译器输入是.sql文件输出是.sql文件中间全是纯文本解析与重构。2. 从解压到跑通5 分钟本地验证最小可行流程这个.zip包结构很干净解压后是标准 Maven 项目布局src/main/java下核心逻辑resources/放配置和映射规则scripts/存测试样例。没有 Web 层、不依赖 Spring、不启动任何服务——它就是一个main()方法驱动的 CLI 工具。我们跳过 IDE 导入直接用命令行验证是否真能跑起来。2.1 环境准备JDK 8 和最简依赖检查工具本身只依赖antlr4-runtime用于 SQL 语法解析和commons-lang3字符串处理无 Spring、无 MyBatis、无 JDBC 驱动。确认 JDK 版本java -version # 输出需含 1.8.0_ 或 11 或 17 —— 不支持 JDK 18因 ANTLR4 默认未适配注意若用 JDK 17需确认pom.xml中antlr4-runtime版本 ≥ 4.10旧版在 JDK 17 下会抛UnsupportedClassVersionError。本包实测使用4.9.3故推荐 JDK 8 或 11。2.2 编译打包跳过测试快速构建可执行 jar进入解压目录执行mvn clean package -Dmaven.test.skiptrue # 输出 target/db-converter-1.0.0.jar版本号以 pom.xml 为准该 jar 是 fat-jar含全部依赖无需额外 classpath。验证是否可执行java -jar target/db-converter-1.0.0.jar --help # 应输出 usage: java -jar db-converter.jar [options] # -i,--input arg Input SQL file path (required) # -o,--output arg Output SQL file path (required) # -d,--db-type arg Target database type: oracle (required)2.3 用一个 CREATE TABLE 测试看它怎么把 MySQL 的 auto_increment 翻译成 Oracle 的 sequencetrigger准备测试文件test-mysql.sqlCREATE TABLE user_info ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL DEFAULT , created_time DATETIME DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );执行转换java -jar target/db-converter-1.0.0.jar \ -i test-mysql.sql \ -o test-oracle.sql \ -d oracle生成的test-oracle.sql内容应类似-- Sequence for user_info.id CREATE SEQUENCE seq_user_info_id START WITH 1 INCREMENT BY 1; -- Table user_info CREATE TABLE user_info ( id NUMBER(19,0) NOT NULL PRIMARY KEY, name VARCHAR2(64 CHAR) NOT NULL, created_time DATE DEFAULT SYSDATE, updated_time DATE DEFAULT SYSDATE ); -- Trigger for auto-increment emulation CREATE OR REPLACE TRIGGER trg_user_info_id BEFORE INSERT ON user_info FOR EACH ROW BEGIN IF :NEW.id IS NULL THEN SELECT seq_user_info_id.NEXTVAL INTO :NEW.id FROM DUAL; END IF; END; /✅ 关键点验证BIGINT→NUMBER(19,0)Oracle 无原生 bigint用 NUMBER 模拟VARCHAR(64)→VARCHAR2(64 CHAR)显式声明字符语义避免字节截断DATETIME/TIMESTAMP→DATEOracle DATE 类型同时含日期时间精度足够AUTO_INCREMENT→SEQUENCE TRIGGER标准 Oracle 替代方案逻辑说明工具内部用 ANTLR4 解析 MySQL DDL 为 AST抽象语法树识别ColumnDefinition节点中的autoIncrement标志再根据targetDbTypeoracle触发OracleDDLGenerator类调用generateSequence()和generateTrigger()方法拼装 SQL。整个过程不访问数据库元信息纯文本驱动。3. 核心转换逻辑拆解类型映射、函数重写、约束处理三板斧工具不是靠正则暴力替换而是分层处理 SQL 元素。理解这三层才能知道它能做什么、不能做什么、哪里要人工干预。3.1 类型映射表为什么 VARCHAR(255) 有时变 VARCHAR2(255 BYTE)有时变 VARCHAR2(255 CHAR)MySQL 的VARCHAR(n)默认按字符计数但 Oracle 的VARCHAR2(n)默认按字节计数——中文字符在 UTF8 下占 3 字节若不声明CHAR255 字节最多存 85 个汉字极易截断。工具通过TypeMappingRule类控制策略MySQL 类型Oracle 类型默认触发条件可配置项VARCHAR(n)VARCHAR2(n CHAR)-- charsetutf8mb4注释存在-Doracle.charsetcharVARCHAR(n)VARCHAR2(n BYTE)无 charset 注释或-- byte-Doracle.charsetbyteTEXTCLOB所有 TEXT 类型不可配置强制 CLOBTINYINT(1)NUMBER(1)且含COMMENT boolean-Dmysql.booleantinyint实际使用中建议在 MySQL 脚本头部加注释控制行为-- charsetutf8mb4 -- engineInnoDB CREATE TABLE product ( id INT PRIMARY KEY, title VARCHAR(255) COMMENT 商品标题 );这样工具会自动选用VARCHAR2(255 CHAR)。若项目历史脚本无注释可在命令行强制指定java -Doracle.charsetchar -jar db-converter.jar -i in.sql -o out.sql -d oracle3.2 函数重写引擎CURRENT_TIMESTAMP、NOW()、IFNULL 怎么变成 Oracle 等价物MySQL 函数在 Oracle 中无直译工具内置FunctionRewriter按上下文重写MySQL 函数Oracle 等价写法场景说明NOW()/CURRENT_TIME()SYSDATE仅用于DEFAULT子句时CURRENT_TIMESTAMP()SYSTIMESTAMP需毫秒精度时如日志时间戳IFNULL(expr1, expr2)NVL(expr1, expr2)所有 DML 中的空值判断CONCAT(str1,str2)str1UNIX_TIMESTAMP()(CAST(SYS_EXTRACT_UTC(SYSTIMESTAMP) AS DATE) - DATE 1970-01-01) * 86400精确到秒需注意时区⚠️ 注意UNIX_TIMESTAMP(NOW())这种组合会被识别为时间戳生成而非单纯函数调用重写更复杂。工具对嵌套函数支持有限遇到DATE_FORMAT(NOW(), %Y-%m-%d)这类会直接报Unsupported function: DATE_FORMAT并跳过该行——这是设计选择不是 bug。3.3 约束与索引迁移PRIMARY KEY、UNIQUE、INDEX 如何保留语义MySQL 的KEY idx_name (col1,col2)和INDEX idx_name (col1,col2)在 Oracle 中统一为CREATE INDEX但工具会保留命名习惯PRIMARY KEY→PRIMARY KEYOracle 原生支持UNIQUE KEY uk_name (col)→CONSTRAINT uk_name UNIQUE (col)显式命名约束KEY idx_name (col1,col2)→CREATE INDEX idx_name ON table_name(col1,col2)独立建索引语句特别处理FULLTEXT索引Oracle 无原生 FULLTEXT工具将其转为注释并提示-- WARNING: FULLTEXT index ft_title not supported in Oracle. Consider Oracle Text. -- KEY ft_title (title)这样 DBA 一眼看到哪些索引需后续人工重建如用CTXSYS.CONTEXT索引。4. 避坑指南那些让转换结果无法执行的 4 个典型翻车现场这工具不是黑匣子魔法它有明确边界。以下 4 个问题我亲手踩过、被测试环境打脸过、被 DBA 拉着改了三轮才确认是工具局限——列在这里帮你省下至少半天排查时间。4.1 现象生成的 Oracle 脚本执行时报ORA-00922: missing or invalid option原因MySQL 脚本含ENGINEInnoDB DEFAULT CHARSETutf8mb4表选项工具虽能忽略但若CREATE TABLE末尾有多余逗号如updated_time TIMESTAMP, )ANTLR 解析失败导致整句丢弃后续;位置错乱Oracle 解析器报语法错。解决用sed -i /^)/d *.sql清理 MySQL 脚本末尾孤立括号或启用工具内置校验开关-Dvalidator.stricttrue会提前报错并退出不生成残缺脚本。4.2 现象INSERT INTO t VALUES (1,a,NOW())转成INSERT INTO t VALUES (1,a,SYSDATE)后Oracle 报ORA-01722: invalid number原因MySQL 允许NOW()作为VALUES字面量Oracle 要求SYSDATE必须在SELECT子句或DEFAULT中直接写VALUES (..., SYSDATE)语法非法。解决工具默认将NOW()重写为SYSDATE但仅适用于DEFAULT上下文。对INSERT ... VALUES中的时间字面量必须改用INSERT ... SELECT ... FROM DUAL形式。工具提供-Dinsert.modedual参数强制启用此模式java -Dinsert.modedual -jar db-converter.jar -i in.sql -o out.sql -d oracle # 输出INSERT INTO t SELECT 1,a,SYSDATE FROM DUAL;4.3 现象MySQL 的ENUM(on,off)被转成VARCHAR2(10)但业务代码用WHERE statuson查询Oracle 中大小写敏感导致查不到原因Oracle 默认区分大小写而 MySQLENUM实际存储为字符串且比较时不区分大小写取决于 collation。工具无法推断业务语义保守转为VARCHAR2。解决人工在 Oracle 表上加虚拟列或函数索引或修改应用层 SQL 加UPPER(status)ON。工具提供-Denum.as-check参数将ENUM转为VARCHAR2CHECK约束status VARCHAR2(10) CHECK (status IN (on,off))4.4 现象MySQL 存储过程CREATE PROCEDURE p1(IN p_id INT)转成 Oracle 后IN p_id INT变成p_id IN NUMBER但调用时CALL p1(123)报ORA-06550: line 1, column 7: PLS-00306: wrong number or types of arguments原因MySQL 的CALL语法在 Oracle 中不存在Oracle 用EXEC p1(123)或BEGIN p1(123); END;。工具只转换CREATE PROCEDURE体不改调用语句。解决工具不处理调用侧需人工批量替换CALL xxx为BEGIN xxx; END;。建议用sed预处理sed -i s/CALL \([^ ]*\)(/BEGIN \1(/g; s/);$/); END;/g *.sql提示所有避坑方案都已在README.md的Troubleshooting章节列出但文档不会告诉你“DBA 第一次执行就卡在这儿”只有亲手在测试库跑一遍才会记住ORA-01722是INSERT VALUES里SYSDATE惹的祸。5. 进阶用法定制化映射规则、批量处理多文件、集成到 CI/CD 流水线工具默认规则覆盖 80% 场景但真实迁移总有例外——比如你们 Oracle 数据库禁用SEQUENCE要求全用SELECT MAX(id)1 FROM t模拟自增或者某张表的created_time必须用SYSTIMESTAMP而非SYSDATE。这时就得改规则而不是改生成的 SQL。5.1 自定义类型映射如何让DECIMAL(10,2)固定转成NUMBER(12,2)预留 2 位防溢出工具的类型映射由src/main/resources/mapping/oracle-type-mapping.json控制。原始内容片段{ decimal: NUMBER(10,2), varchar: VARCHAR2(${length} CHAR) }你想让所有DECIMAL统一加 2 位精度余量编辑该文件{ decimal: NUMBER(${precision2},${scale}), varchar: VARCHAR2(${length} CHAR) }${precision2}是工具内置表达式语法支持 - * /和括号。重启工具即可生效。注意${}中不能有空格否则解析失败。5.2 批量处理整个脚本目录把 200 个.sql文件一键转成 Oracle 版工具原生只支持单文件-i但加个 shell 循环就搞定#!/bin/bash INPUT_DIR./mysql-scripts OUTPUT_DIR./oracle-scripts mkdir -p $OUTPUT_DIR for sql_file in $INPUT_DIR/*.sql; do if [ -f $sql_file ]; then base_name$(basename $sql_file .sql) java -jar target/db-converter-1.0.0.jar \ -i $sql_file \ -o $OUTPUT_DIR/${base_name}-oracle.sql \ -d oracle \ -Doracle.charsetchar fi done echo ✅ Converted $(ls $INPUT_DIR/*.sql | wc -l) files注意批量时务必加-Doracle.charsetchar避免个别文件漏注释导致混用BYTE/CHAR引发数据截断。5.3 接入 GitLab CI每次 push MySQL 脚本就自动生成 Oracle 版并 diff在.gitlab-ci.yml中添加 joboracle-conversion: stage: build image: maven:3.8-openjdk-11 script: - mvn clean package -Dmaven.test.skiptrue - mkdir -p oracle-output - java -jar target/db-converter-1.0.0.jar -i schema/mysql-ddl.sql -o oracle-output/oracle-ddl.sql -d oracle - git config --global user.name CI Bot - git config --global user.email ciexample.com - git add oracle-output/oracle-ddl.sql - git commit -m [CI] Auto-update Oracle DDL || echo No changes to commit - git push only: - main这样开发提交 MySQL 脚本后Oracle 版本自动更新DBA 拉取最新oracle-output/目录即可部署无需手动触发。6. 我的真实经验别把它当银弹而要当「人机协同」的协作者去年做金融系统迁移我们团队用这个工具处理了 1200 个 SQL 文件。第一周大家兴奋地发现 90% 的建表语句一次通过第二周开始为GROUP_CONCAT怎么转LISTAGG争论不休第三周DBA 发现ON UPDATE CURRENT_TIMESTAMP在 Oracle 触发器里漏了WHEN UPDATING条件导致插入时也触发更新——这恰恰暴露了工具的定位它负责语法骨架你负责业务血肉。我养成三个铁律永远先跑sqlcl或sqlplus的test-oracle.sql做语法校验不等应用连库再报错对每个CREATE PROCEDURE人工检查IN/OUT参数是否转对Oracle 的OUT参数必须用:赋值不能像 MySQL 直接SET把工具生成的 SQL 当初稿用diff -u mysql.sql oracle.sql对比重点盯DEFAULT、CONSTRAINT、INDEX三处——这里藏了最多语义偏差。最深的教训是曾以为CONVERT_TZ()函数能自动转结果工具直接跳过整行上线后报表时间全错 8 小时。后来我们在resources/custom-functions.json里加了一条{ CONVERT_TZ: FROM_TZ(CAST(? AS TIMESTAMP), ?) AT TIME ZONE ? }再配合-Dfunction.mappingcustom-functions.json问题解决。这让我明白工具的价值不在“全自动”而在“可定制、可追溯、可协作”。它不取代 DBA而是把 DBA 从机械改写中解放出来去专注真正需要经验判断的地方——比如TRUNC(SYSDATE)和TRUNC(SYSDATE, DD)的区别比如NLS_DATE_FORMAT对TO_DATE的影响。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑