MySQL 脚本转 H2
原文地址:https://alphahinex.github.io/2022/05/08/mysql2h2/
coverdescription: "mysql2h2-converter 项目介绍"
date: 2022.05.08 10:26
categories:
- Java
- Database
tags: [MySQL, H2, Java, Database]
keywords: MySQL, H2, mysql2h2, parser, converter
MySQL
引用 MySQL 官网 的介绍:
The world's most popular open source database
只要接触过数据库,应该就知道 MySQL,现行的两个主要版本为 5.7 和 8.0,本文主要关注通常所说的 SQL 脚本,即 SQL Statements
,两个版本的文档链接如下:
H2
H2 即 H2 Database,是一个使用 Java 编写的嵌入式的内存关系型数据库,官网 中介绍的主要特点如下:
- Very fast, open source, JDBC API
- Embedded and server modes; disk-based or in-memory databases
- Transaction support, multi-version concurrency
- Browser based Console application
- Encrypted databases
- Fulltext search
- Pure Java with small footprint: around 2.5 MB jar file size
- ODBC driver
关于为什么使用 Java 数据库,官网中也给出了 自己的理解。对于 Java 应用来讲,需要使用数据库时,只需要添加一个依赖,这简直是太方便了,尤其是在执行测试用例的时候,减少一个依赖,能够进一步降低运行测试用例的难度。
H2 的 SQL Statements
文档在 这里 。
mysql2h2-converter
那么问题来了,如果我的应用是基于 MySQL 构建的,相关数据库脚本都是 MySQL 的,如果想基于 H2 运行或使用 H2 作为测试用例执行环境的数据库,有没有什么简单的方法呢?毕竟二者的语法是有区别的,无法完全兼容。
使用 ETL 类的工具,将 MySQL 的库导出并转换至 H2 库是一种方案,不过这种方案只解决了某个时间点数据库状态的问题,操作繁琐,后续的变更同步也是个麻烦。
如果你的应用有 MySQL 的 SQL 脚本,可以试试 mysql2h2-converter
,能够将 MySQL 的脚本,转换成 H2 可执行的脚本。想象一下:在执行测试用例的环境,测试用例可以借助 mysql2h2-converter
读取代码库中的 MySQL 脚本,转换并自动在 H2 内存数据库中执行,无需提前搭建测试库即可完成数据库相关测试用例的运行,香不香?
介绍
mysql2h2-converter
项目最初由 @bgranvea 创建,2015 年后不再维护;@andrewparmet fork 进行了 完善,并在 Maven 中央库中发布了 v0.2.1
版本,但在 2018 年之后也没再进行维护了。
@AlphaHinex fork
enhance 分支 在 v0.2.1 版本的基础上,进行了一些完善,支持了如下语句的解析:
-- 字段约束中的 UNIQUE INDEX
-- 字段类型中的 datetime(0) 形式(之前仅支持 datetime 形式)
-- 表的 CHARACTER SET, COLLATE
CREATE TABLE test (
t1 int(10) NOT NULL AUTO_INCREMENT,
t2 int(10) NOT NULL,
t3 varchar(55) DEFAULT '',
t4 datetime DEFAULT '',
t5 datetime(0) DEFAULT '',
PRIMARY KEY (t1) USING BTREE,
UNIQUE KEY u1 (t1,t2),
KEY k1 (t2),
CONSTRAINT c1 FOREIGN KEY (t2) REFERENCES test2 (t2) ON DELETE CASCADE,
UNIQUE INDEX `UNIQUE_NAME_NAMESPACES` (`NAME`,`NAMESPACE`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE test (t1 int(10)) ENGINE=InnoDB CHARACTER SET=utf8mb4;
-- ALTER TABLE MODIFY
ALTER TABLE test MODIFY c1 VARCHAR(255) NULL;
ALTER TABLE test MODIFY COLUMN c1 VARCHAR(255) NULL;
ALTER TABLE test MODIFY c1 VARCHAR(255) NULL FIRST;
ALTER TABLE test MODIFY COLUMN c1 VARCHAR(255) NULL AFTER c0;
ALTER TABLE test MODIFY c1 VARCHAR(256) AFTER c0, MODIFY c2 VARCHAR(256);
-- SET
SET NAMES utf8mb4;
-- UPDATE
UPDATE test SET t1=1,t2='test',t3=5.0 WHERE t4=1;
UPDATE test SET t1=CONCAT(t1,'-")-', "'",'-',t2) WHERE t4=1;
-- DELETE
DELETE FROM test;
DELETE FROM test WHERE t1=1;
DELETE FROM test WHERE t1='abc' AND t2=1;
DELETE FROM test WHERE t1="2" OR t2=1;
DELETE FROM test WHERE t1 IN ('1','2','3');
DELETE FROM test WHERE t1<>'' && t2 IS NOT NULL;
用法
command line
在项目根路径使用 mvn package
进行构建(converter/target/mysql2h2-converter-tool-0.2.2.jar
),或直接下载发布包 mysql2h2-converter-tool-0.2.2.jar,然后使用如下命令进行转换:
$ java -jar /path/to/mysql2h2-converter-tool-0.2.2.jar input_mysql.sql > output_h2.sql
library
因为之前的版本都不再维护了,所以并没有将上述扩展提交给原始项目,并修改了 group 和 version 以便区分。作为类库使用时,可以通过 JitPack 进行引用。调用时,可参照如下代码:
private static void convertAndCreate(Statement stmt, String sqlDump) throws SQLException, ParseException {
Iterator<SqlStatement> sourceIterator = SQLParserManager.parseScript(new StringReader(sqlDump));
Iterator<SqlStatement> converted = H2Converter.convertScript(sourceIterator);
while (converted.hasNext()) {
stmt.execute(converted.next().toString());
}
}
原理
mysql2h2-converter
项目使用 JavaCC 编写了 SQL 语法描述文件 sqlgrammar.jj,通过 JavaCC 编译生成了 MySQL 的 SQL 语句解析器,作为 parser 模块的基础;在 converter 模块中,对解析出的 MySQL 语句进行了 H2 语法的转换,并提供了在命令行中执行的主类。
关于 JavaCC 的用法,可参考 JavaCC 实战 。
sqlgrammar.jj
目前并未支持 MySQL 的所有语法,但在 converter
模块的 测试资源 中,包含了大量的 MySQL 脚本,基本能够满足通常场景的脚本转换需求。