# SQLTune
**Repository Path**: finpire/sqltune
## Basic Information
- **Project Name**: SQLTune
- **Description**: SQLTune 是一个强大的 SQL 语句转换工具,核心优势是提供 Spring Boot 零侵入集成方案,能够将一种数据库方言的 SQL 语句无缝转换为另一种数据库方言的 SQL 语句(例如将 Oracle SQL 转换为 MySQL SQL),实现跨数据库的 SQL 兼容性。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 1
- **Created**: 2025-12-05
- **Last Updated**: 2025-12-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SQLTune (ST)
SQLTune 是一个强大的 SQL 语句转换工具,**核心优势是提供 Spring Boot 零侵入集成方案**,能够将一种数据库方言的 SQL 语句无缝转换为另一种数据库方言的 SQL 语句(例如将 Oracle SQL 转换为 MySQL SQL),实现跨数据库的 SQL 兼容性。
## 技术架构与先进性
### 🛠️ 核心技术
- **双引擎解析与转换**:
- **基于 ANTLR4 的语法树 (AST) 转换**:使用 ANTLR4 构建精确的 SQL 解析器,将 SQL 解析为抽象语法树,实现高精度的语义级转换
- **基于 Apache Calcite 的高级 SQL 处理**:集成 Apache Calcite 作为高级 SQL 解析、优化和转换框架,提供更强大的 SQL 语义理解和优化能力
- **Spring Boot 零侵入集成**:通过自定义 JDBC 驱动或数据源代理方式,实现对 Spring Boot 项目的无缝集成
- **插件化架构**:支持动态加载和扩展转换规则,可通过 JAR 文件或类路径加载自定义插件
- **分层设计**:采用 API 层、核心层、扩展层、基础层的分层架构,提供良好的扩展性和可维护性
### 🚀 技术先进性
- **零侵入集成**:无需修改原有 SQL 代码,只需添加依赖和配置即可实现 SQL 自动转换
- **高精度转换**:基于语法树的转换方式能够准确理解 SQL 语义,避免简单字符串替换的局限性
- **灵活扩展**:提供丰富的扩展点,支持自定义转换规则和数据库方言
- **高性能**:转换过程在内存中完成,性能开销极小
- **广泛兼容性**:支持多种数据库方言之间的转换,包括 Oracle、MySQL、PostgreSQL 等
## 快速开始
### 🎯 Spring Boot 零侵入集成(推荐核心用法)
**只需 3 步,无需修改原有 SQL 代码,即可实现 SQL 自动转换:**
#### 1. 添加依赖
在 Spring Boot 项目的 `pom.xml` 中添加依赖:
```xml
com.sqltune
sqltune-spring-boot-starter
1.0.0-SNAPSHOT
```
#### 2. 配置文件
在 `application.properties` 或 `application.yml` 中添加配置:
```properties
# SQLTune 核心配置 - 仅需几行配置即可启用
# 源数据库方言(如:oracle)
sqltune.source-dialect=oracle
# 目标数据库方言(如:mysql)
sqltune.target-dialect=mysql
# 启用 JDBC 自动拦截 - 关键配置,实现零代码修改
# 开启后,所有通过 JDBC 执行的 SQL 都会被自动转换
sqltune.enable-jdbc-intercept=true
# SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
sqltune.optimizer-type=calcite
```
#### 3. 使用原有代码,自动生效
无需修改任何原有 SQL 代码,所有通过 JDBC 执行的 SQL 都会被自动转换:
```java
@Service
public class UserService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public UserService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List getActiveUsers() {
// ✅ 写 Oracle SQL
String oracleSql = "SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10";
// ✅ 无需修改代码,SQL 会被自动转换为 MySQL
// 转换后: SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 10
return jdbcTemplate.query(oracleSql, (rs, rowNum) -> {
// ... 原有代码
});
}
}
```
---
### 🔄 现有项目零开发数据库切换方案
**对于已经上线或开发完成的项目,无需修改任何代码,即可实现数据库的丝滑切换:**
SQLTune 支持两种实现方式,您可以根据需求选择合适的模式:
#### 方式一:数据源代理模式(推荐,无需修改代码)
当`sqltune.enable-jdbc-intercept=true`时,SQLTune 会自动代理所有数据源,无需修改 URL 和驱动类:
1. **添加依赖**:在现有项目的 `pom.xml` 中添加 SQLTune Spring Boot Starter 依赖
2. **修改配置**:
```properties
# 1. 修改数据库连接(使用标准MySQL驱动,无需sqltune前缀)
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 2. 添加 SQLTune 配置
# 源数据库方言:oracle(原有数据库)
sqltune.source-dialect=oracle
# 目标数据库方言:mysql(新数据库)
sqltune.target-dialect=mysql
# 启用JDBC自动拦截 - 关键配置,实现零代码修改
sqltune.enable-jdbc-intercept=true
# SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
sqltune.optimizer-type=calcite
```
3. **启动项目**:无需修改任何业务代码,项目即可正常运行
#### 方式二:自定义 JDBC 驱动模式
使用 SQLTune 自定义 JDBC 驱动来拦截并自动转换 SQL 语句:
1. **添加依赖**:在现有项目的 `pom.xml` 中添加 SQLTune Spring Boot Starter 依赖
2. **修改配置**:
```properties
# 1. 修改数据库连接(使用SQLTune驱动)
spring.datasource.url=jdbc:sqltune:jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.sqltune.spring.boot.starter.jdbc.SqlTuneDriver
# 2. 添加 SQLTune 配置
# 源数据库方言:oracle(原有数据库)
sqltune.source-dialect=oracle
# 目标数据库方言:mysql(新数据库)
sqltune.target-dialect=mysql
# 启用自动转换
sqltune.enable-jdbc-intercept=true
# SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
sqltune.optimizer-type=calcite
```
3. **启动项目**:无需修改任何业务代码,项目即可正常运行
**两种方式的区别:**
- 数据源代理模式:配置更简单,类似 ShardingJDBC,无需修改 URL 和驱动类
- 自定义 JDBC 驱动模式:需要修改 URL 和驱动类,但更直接控制 JDBC 连接
**切换效果**:
- 原有 Oracle 风格的 SQL(如 `SELECT * FROM users WHERE ROWNUM <= 10`)
- 自动转换为 MySQL 风格的 SQL(如 `SELECT * FROM users LIMIT 10`)
- 所有数据库操作完全兼容
#### 支持的切换场景
| 源数据库 | 目标数据库 | 支持程度 |
| ---------- | ---------- | ----------- |
| Oracle | MySQL | ✅ 完全支持 |
| Oracle | PostgreSQL | ✅ 完全支持 |
| MySQL | PostgreSQL | ✅ 完全支持 |
| PostgreSQL | MySQL | ✅ 完全支持 |
| 更多组合 | ... | ✅ 持续扩展 |
**优势**:
- 零开发成本:无需修改任何业务代码
- 零风险:保留原有 SQL 逻辑,避免引入新的 Bug
- 零学习成本:无需了解目标数据库的 SQL 语法差异
- 快速切换:从准备到完成仅需几分钟
- 可回滚:随时可以切换回原数据库
## 环境要求与安装
### 环境要求
- JDK 1.8+
- Maven 3.5+
- Spring Boot 2.0+(使用 Spring Boot 集成时)
- Apache Calcite 1.34.0+(自动通过 Maven 依赖管理)
### 项目安装(可选,仅需本地开发时)
1. 克隆项目:
```bash
git clone https://gitee.com/finpire/sqltune.git
cd sqltune
```
2. 编译项目:
```bash
mvn clean install -DskipTests
```
## 其他使用方式
除了 Spring Boot 零侵入集成外,SQLTune 还提供以下使用方式:
### 1. 完整 Spring Boot 配置(高级功能)
如果需要使用更多高级功能,可以参考以下完整配置:
```properties
# SQLTune 完整配置
# 源数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
sqltune.source-dialect=oracle
# 目标数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
sqltune.target-dialect=mysql
# 是否启用日志(默认:false)
sqltune.enable-log=false
# 是否启用SQL优化(默认:true)
sqltune.enable-optimization=true
# SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
sqltune.optimizer-type=calcite
# 是否启用JDBC自动拦截(默认:false)
sqltune.enable-jdbc-intercept=true
# 序列转换策略:仅在源数据库支持序列(如Oracle、PostgreSQL)且目标数据库为MySQL时生效
# 可选值:AUTO_INCREMENT(使用MySQL自增ID)、NEXTVAL(使用自定义nextval函数)
sqltune.sequence-strategy=AUTO_INCREMENT # 默认值为AUTO_INCREMENT
```
或使用 YAML 格式:
```yaml
sqltune:
# 源数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
source-dialect: oracle
# 目标数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
target-dialect: mysql
enable-log: false
enable-optimization: true
# SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
optimizer-type: calcite
enable-jdbc-intercept: true
# 序列转换策略:仅在源数据库支持序列(如Oracle、PostgreSQL)且目标数据库为MySQL时生效
# 可选值:AUTO_INCREMENT(使用MySQL自增ID)、NEXTVAL(使用自定义nextval函数)
sequence-strategy: AUTO_INCREMENT # 默认值为AUTO_INCREMENT
```
### 2. 配置文件方式
创建配置文件 `sqltune.yaml`:
```yaml
rules:
sql-rewrite:
source-database: oracle
target-database: mysql
optimizer-type: calcite # SQL优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
```
使用 SQLTune 转换 SQL:
```java
import com.sqltune.core.SqlTuneEngine;
import com.sqltune.core.SqlTuneResult;
public class Example {
public static void main(String[] args) {
// 创建SQLTune引擎
SqlTuneEngine engine = SqlTuneEngine.builder()
.configFile("sqltune.yaml")
.build();
// Oracle SQL语句
String oracleSql = "SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10";
// 转换为MySQL SQL
SqlTuneResult result = engine.rewrite(oracleSql);
// 输出转换后的SQL
System.out.println("Original SQL: " + oracleSql);
System.out.println("Rewritten SQL: " + result.getRewrittenSql());
// 输出: SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 10
}
}
```
### 3. 编程方式配置
```java
import com.sqltune.core.SqlTuneEngine;
import com.sqltune.core.SqlTuneResult;
import com.sqltune.config.SqlTuneConfig;
public class Example {
public static void main(String[] args) {
// 创建配置
SqlTuneConfig config = SqlTuneConfig.builder()
.sourceDatabase("oracle")
.targetDatabase("mysql")
.optimizerType("calcite") // 设置优化器类型:basic(基础优化器)或calcite(基于Calcite的高级优化器)
.build();
// 创建SQLTune引擎
SqlTuneEngine engine = SqlTuneEngine.builder()
.config(config)
.build();
// Oracle SQL语句示例
String[] oracleSqls = {
"SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10",
"SELECT id, NVL(name, 'Unknown') AS name FROM users",
"SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2023-01-01'"
};
// 转换为MySQL SQL
for (String sql : oracleSqls) {
SqlTuneResult result = engine.rewrite(sql);
System.out.println("Original SQL: " + sql);
System.out.println("Rewritten SQL: " + result.getRewrittenSql());
System.out.println();
}
}
}
```
### 4. 输出结果示例
```
Original SQL: SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10
Rewritten SQL: SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 10
Original SQL: SELECT id, NVL(name, 'Unknown') AS name FROM users
Rewritten SQL: SELECT id, COALESCE(name, 'Unknown') AS name FROM users
Original SQL: SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2023-01-01'
Rewritten SQL: SELECT * FROM orders WHERE DATE_FORMAT(order_date, '%Y-%m-%d') = '2023-01-01'
```
### 5. Apache Calcite 优化示例
```
# 原始复杂SQL
SELECT o.id, o.customer_id, SUM(oi.quantity * oi.price) AS total_amount
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.status = 'COMPLETED' AND p.category = 'ELECTRONICS'
GROUP BY o.id, o.customer_id
HAVING SUM(oi.quantity * oi.price) > 1000
ORDER BY total_amount DESC
LIMIT 10
# Calcite优化后的SQL
SELECT o.id, o.customer_id, SUM(oi.quantity * oi.price) AS total_amount
FROM orders o
JOIN (
SELECT order_id, quantity, price
FROM order_items
WHERE product_id IN (
SELECT id
FROM products
WHERE category = 'ELECTRONICS'
)
) oi ON o.id = oi.order_id
WHERE o.status = 'COMPLETED'
GROUP BY o.id, o.customer_id
HAVING SUM(oi.quantity * oi.price) > 1000
ORDER BY total_amount DESC
LIMIT 10
```
## 功能特性
### 🌟 核心价值
- **零代码修改**:无需修改原有 SQL 代码,只需添加依赖和配置即可实现 SQL 自动转换
- **快速集成**:Spring Boot 项目只需添加一个依赖并配置几行属性,即可立即启用
- **跨库兼容**:解决不同数据库方言差异,实现一套代码适配多种数据库
- **现成项目 0 开发切换**:对于已有的项目,无需任何开发工作,即可丝滑切换到其他数据库
- **降低迁移成本**:大幅减少数据库迁移时的 SQL 改写工作量,缩短迁移周期
### 🚀 主要功能
- **Spring Boot 无缝集成**:提供 Spring Boot Starter,一行依赖即可集成
- **JDBC 自动拦截**:自动拦截所有 JDBC SQL 执行,无需显式调用转换方法
- **SQL 方言转换**:支持多种数据库方言之间的 SQL 转换(如 Oracle → MySQL、MySQL → PostgreSQL 等)
- **智能函数映射**:自动将源数据库函数映射到目标数据库对应的函数(如 Oracle NVL → MySQL COALESCE)
- **行限制转换**:支持将 Oracle ROWNUM 转换为 MySQL LIMIT 等行限制语法
- **配置灵活**:支持通过配置文件或编程方式自定义转换规则
- **支持复杂 SQL**:能够处理复杂的 SELECT、INSERT、UPDATE、DELETE 语句,包括子查询、连接查询等
- **SQL 优化器**:可选功能,支持对 SQL 语句进行优化处理,提高执行效率
- **健壮的错误处理**:完善的异常捕获机制,确保转换过程的稳定性
- **分层架构**:采用分层设计,具有良好的扩展性和可维护性
## 架构设计
SQLTune 采用分层架构设计:
1. **API 层**:提供对外接口,包括 SQL 转换服务、配置管理等
2. **核心层**:包含 SQL 解析、改写、优化等核心功能
3. **扩展层**:提供各种数据库方言的支持和自定义规则的扩展点
4. **基础层**:包含工具类、通用组件等基础功能
| 层级 | 功能描述 |
| ------ | ------------------------------------------------------------------------------------------------------- |
| API 层 | - SQL 转换服务接口
- 配置管理接口 |
| 核心层 | - SQL 解析器 (Parser)
- SQL 改写器 (Rewriter)
- SQL 优化器 (Optimizer)
- SQL 执行器 (Executor) |
| 扩展层 | - Oracle 方言支持
- MySQL 方言支持
- PostgreSQL 方言支持
- 自定义规则扩展点 |
| 基础层 | - 工具类
- 通用组件
- 异常处理 |
## 支持的数据库
当前支持的数据库方言:
- Oracle
- MySQL
- MariaDB
- 达梦(DM)
- PostgreSQL
- 人大金仓(KINGBASE)
- SQL Server
### 支持的转换对
根据数据库的语法相似性,SQLTune 支持以下转换对:
1. **Oracle <-> MySQL/MariaDB/PostgreSQL/KINGBASE**
2. **DM <-> MySQL/MariaDB/PostgreSQL/KINGBASE**
3. **MySQL <-> MariaDB**
4. **MySQL <-> PostgreSQL/KINGBASE**
5. **MariaDB <-> PostgreSQL/KINGBASE**
6. **PostgreSQL <-> KINGBASE**
**注意**:SQL Server 仅作为源数据库支持部分转换,当前不支持作为目标数据库的全面转换。
## 项目结构
```
sqltune/
├── sqltune-api/ # API 层
├── sqltune-core/ # 核心层
│ └── src/main/antlr4/ # ANTLR4 语法文件目录
├── sqltune-extension/ # 扩展层
├── sqltune-common/ # 基础层
├── sqltune-examples/ # 示例代码
├── sqltune-spring-boot-starter/ # Spring Boot Starter 模块
└── pom.xml # Maven 父工程配置
```
**注意**:sqltune 目录中包含.gitignore 和 LICENSE 文件,是一个独立的 git 仓库。
## 核心组件
### 核心组件
#### SQL 解析器 (Parser)
负责将 SQL 语句解析为抽象语法树(AST),支持多种数据库方言的解析。
解析器基于 ANTLR4 实现,语法文件位于 `sqltune-core/src/main/antlr4/` 目录,包含以下数据库的语法定义:
- MySQL
- MariaDB
- PostgreSQL
- Oracle (PL/SQL)
- SQL Server (TSQL)
#### SQL 改写器 (Rewriter)
根据目标数据库方言的规则,对抽象语法树进行改写,实现 SQL 方言的转换。
#### SQL 优化器
提供双重 SQL 优化能力:
**基础优化器**:
- 移除多余空格和换行
- 移除不必要的括号
- 优化 WHERE 子句条件
- 优化 ORDER BY 子句
- 移除不必要的 DISTINCT
**基于 Apache Calcite 的高级优化器**:
- 基于成本的查询优化(CBO)
- 关系代数优化(如谓词下推、投影下推)
- 连接顺序优化
- 子查询优化
- 窗口函数优化
- 物化视图支持
- 多表连接优化
#### SQL 执行器
可选组件,用于执行转换后的 SQL 语句。
#### 自定义 JDBC 驱动
提供自定义 JDBC 驱动,实现 SQL 语句的自动转换,无需修改原有代码即可实现跨数据库兼容。
## 扩展开发
SQLTune 提供了丰富的扩展点,允许用户自定义转换规则:
1. **自定义改写规则**:实现`SqlRewriteRule`接口
2. **自定义数据库方言**:继承`AbstractDatabaseDialect`类
3. **自定义解析器**:实现`SqlParser`接口
### 插件化架构
SQLTune 支持插件化架构,允许用户动态加载和扩展功能。插件机制提供了一种灵活的方式来扩展 SQLTune 的能力,无需修改核心代码。
#### 插件开发
要开发一个 SQLTune 插件,需要:
1. **创建插件类**:实现`Plugin`接口或继承`AbstractPlugin`类
2. **注册扩展点**:在插件的`doInitialize()`方法中注册自定义组件
**示例插件**:
```java
package com.sqltune.example.plugin;
import com.sqltune.core.plugin.AbstractPlugin;
import com.sqltune.core.rewrite.SqlRewriteRule;
import com.sqltune.core.rewrite.context.RewriteContext;
import com.sqltune.core.parser.SqlParser;
import com.sqltune.core.dialect.AbstractDatabaseDialect;
import com.sqltune.core.registry.RewriteRuleRegistry;
import com.sqltune.core.registry.ParserRegistry;
import com.sqltune.core.registry.DialectRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExamplePlugin extends AbstractPlugin {
private static final Logger LOGGER = LoggerFactory.getLogger(ExamplePlugin.class);
public ExamplePlugin() {
super("ExamplePlugin", "1.0.0", "示例插件,演示插件化架构的使用");
}
@Override
protected void doInitialize() {
LOGGER.info("ExamplePlugin 正在初始化...");
// 1. 注册自定义SQL改写规则
RewriteRuleRegistry.getInstance().registerRule(new CustomRewriteRule());
// 2. 注册自定义数据库方言
DialectRegistry.getInstance().registerDialect("CUSTOM_DB", new CustomDatabaseDialect());
// 3. 注册自定义SQL解析器
ParserRegistry.getInstance().registerParser("CUSTOM_DB", new CustomSqlParser());
LOGGER.info("ExamplePlugin 初始化完成");
}
@Override
protected void doStart() {
LOGGER.info("ExamplePlugin 正在启动...");
// 可以在这里执行插件启动时需要的操作
LOGGER.info("ExamplePlugin 启动完成");
}
@Override
protected void doStop() {
LOGGER.info("ExamplePlugin 正在停止...");
// 可以在这里执行插件停止时需要的操作
LOGGER.info("ExamplePlugin 停止完成");
}
@Override
protected void doDestroy() {
LOGGER.info("ExamplePlugin 正在销毁...");
// 可以在这里执行插件销毁时需要的操作
LOGGER.info("ExamplePlugin 销毁完成");
}
// 示例:自定义SQL改写规则实现
private static class CustomRewriteRule implements SqlRewriteRule {
@Override
public boolean matches(RewriteContext context) {
// 定义规则匹配条件,例如:匹配包含特定函数的SQL
return context.getOriginalSql().contains("CUSTOM_FUNCTION");
}
@Override
public String rewrite(RewriteContext context) {
// 实现自定义改写逻辑
String originalSql = context.getOriginalSql();
return originalSql.replace("CUSTOM_FUNCTION", "STANDARD_FUNCTION");
}
}
// 示例:自定义数据库方言实现
private static class CustomDatabaseDialect extends AbstractDatabaseDialect {
@Override
public String getDialectName() {
return "CUSTOM_DB";
}
@Override
public String getDriverClassName() {
return "com.custom.db.Driver";
}
// 实现其他方言特定方法...
}
// 示例:自定义SQL解析器实现
private static class CustomSqlParser implements SqlParser {
@Override
public String getSupportedDialect() {
return "CUSTOM_DB";
}
@Override
public Object parse(String sql) {
// 实现自定义SQL解析逻辑
// 实际使用中通常基于ANTLR4实现
return new Object(); // 返回抽象语法树(AST)
}
}
}
```
#### 插件加载
SQLTune 支持从 JAR 文件或类路径加载插件:
**从 JAR 文件加载插件**:
```java
PluginManager pluginManager = PluginManager.getInstance();
pluginManager.loadPluginFromJar("/path/to/my-plugin.jar");
```
**从类路径加载插件**:
```java
PluginManager pluginManager = PluginManager.getInstance();
pluginManager.loadPluginFromClasspath("com.sqltune.example.plugin.ExamplePlugin");
```
#### 插件 JAR 结构
插件 JAR 文件需要包含一个`META-INF/plugin.properties`文件,指定插件类名:
```properties
plugin.class=com.sqltune.example.plugin.ExamplePlugin
```
## SQL 转换技术研究
### 1. SQL 转换技术分类
根据当前研究和实践,数据库 SQL 转换技术主要分为以下几类:
#### 1.1 字符串替换式转换
- **原理**:基于正则表达式或字符串匹配进行简单的关键字替换
- **代表工具**:早期的数据库迁移工具
- **特点**:实现简单,但准确性低,无法处理复杂 SQL
#### 1.2 语法树(AST)转换
- **原理**:将 SQL 解析为抽象语法树,然后根据目标数据库规则进行语义级转换
- **代表工具**:SQLTune、CrackSQL 等
- **特点**:转换准确性高,能够处理复杂 SQL 结构
#### 1.3 语义规则映射转换
- **原理**:基于预定义的语义规则映射表进行转换
- **代表工具**:部分商业数据库迁移工具
- **特点**:规则明确,易于扩展,但规则维护成本高
#### 1.4 机器学习辅助转换
- **原理**:利用机器学习模型学习不同数据库 SQL 的转换模式
- **代表技术**:NL2SQL 技术的扩展应用
- **特点**:能够处理未见过的转换场景,但需要大量训练数据
### 2. 相关研究与文献
#### 2.1 学术研究
- **CrackSQL**:首个高效、准确的 SQL 方言翻译系统,采用基于 AST 的转换方法,为数据库迁移和跨数据库分析提供支持
- **NL2SQL 技术**:将自然语言查询转换为 SQL 查询的技术,提高数据库查询的友好性和效率,部分研究已扩展到跨数据库 SQL 转换领域
#### 2.2 工业实践
- **分布式数据库迁移方法**:工业界提出的数据库迁移最佳实践,强调通过自动化工具降低迁移难度和风险
- **数据库厂商迁移工具**:各数据库厂商提供的专有迁移工具,如 Oracle SQL Developer、MySQL Workbench 等,支持特定数据库间的转换
### 3. SQL 转换技术比较
| 转换技术 | 准确性 | 复杂 SQL 支持 | 扩展性 | 性能 | 实现难度 |
| ---------------- | ------ | ------------- | ------ | ---- | -------- |
| 字符串替换式转换 | 低 | 弱 | 中 | 高 | 低 |
| 语法树(AST)转换 | 高 | 强 | 高 | 中 | 高 |
| 语义规则映射转换 | 中 | 中 | 中 | 中 | 中 |
| 机器学习辅助转换 | 中高 | 中 | 高 | 低 | 高 |
### 4. SQLTune 的转换方式分析
SQLTune 采用的是**基于语法树(AST)的静态转换方式**,具体实现流程如下:
1. **SQL 解析**:使用 ANTLR4 将源数据库 SQL 解析为抽象语法树(AST)
2. **AST 改写**:根据目标数据库方言规则,对 AST 进行节点级别的改写
3. **SQL 生成**:将改写后的 AST 重新生成为目标数据库的 SQL 语句
### 5. 优缺点评估
#### 优点:
| 优势 | 描述 |
| ---------------- | --------------------------------------------------------------------- |
| **零侵入集成** | Spring Boot 项目无需修改任何 SQL 代码即可实现自动转换 |
| **高精度转换** | 基于语法树的转换方式能够准确理解 SQL 语义,避免简单字符串替换的局限性 |
| **灵活扩展** | 提供丰富的扩展点,支持自定义转换规则和数据库方言 |
| **支持复杂 SQL** | 能够处理包含子查询、连接查询等复杂 SQL 语句的转换 |
| **高性能** | 转换过程在内存中完成,性能开销极小 |
#### 缺点:
| 局限 | 描述 |
| -------------------- | ------------------------------------------------------------- |
| **依赖语法定义** | 需要为每种支持的数据库提供完整的 ANTLR4 语法定义 |
| **复杂函数支持有限** | 对于某些数据库特有的复杂函数,转换支持可能不够完善 |
| **存储过程支持有限** | 目前主要支持 SQL 查询语句转换,复杂存储过程的转换能力有待提升 |
| **版本兼容性挑战** | 不同数据库版本的语法差异可能影响转换准确性 |
## 调试技巧
SQLTune 内置了详细的调试信息,可以帮助开发人员定位和解决转换问题:
```java
// 启用调试日志(通过日志配置或代码)
System.setProperty("logging.level.com.sqltune", "DEBUG");
// 或者使用编程方式检查类加载情况
Class> rewriterClass = Class.forName("com.sqltune.extension.rewriter.GenericSqlRewriter");
System.out.println("Rewriter class: " + rewriterClass.getName());
System.out.println("Rewriter class loader: " + rewriterClass.getClassLoader());
System.out.println("Rewriter class location: " + rewriterClass.getProtectionDomain().getCodeSource().getLocation());
```
除了 Spring Boot 零侵入集成外,SQLTune 还提供以下使用方式:
### 1. 完整 Spring Boot 配置(高级功能)
如果需要使用更多高级功能,可以参考以下完整配置:
```properties
# SQLTune 完整配置
# 源数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
sqltune.source-dialect=oracle
# 目标数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
sqltune.target-dialect=mysql
# 是否启用日志(默认:false)
sqltune.enable-log=false
# 是否启用SQL优化(默认:true)
sqltune.enable-optimization=true
# 是否启用JDBC自动拦截(默认:false)
sqltune.enable-jdbc-intercept=true
# 序列转换策略:仅在源数据库支持序列(如Oracle、PostgreSQL)且目标数据库为MySQL时生效
# 可选值:AUTO_INCREMENT(使用MySQL自增ID)、NEXTVAL(使用自定义nextval函数)
sqltune.sequence-strategy=AUTO_INCREMENT # 默认值为AUTO_INCREMENT
```
或使用 YAML 格式:
```yaml
sqltune:
# 源数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
source-dialect: oracle
# 目标数据库方言(支持:oracle、mysql、mariadb、dm、kingbase、postgresql、sqlserver)
target-dialect: mysql
enable-log: false
enable-optimization: true
enable-jdbc-intercept: true
# 序列转换策略:仅在源数据库支持序列(如Oracle、PostgreSQL)且目标数据库为MySQL时生效
# 可选值:AUTO_INCREMENT(使用MySQL自增ID)、NEXTVAL(使用自定义nextval函数)
sequence-strategy: AUTO_INCREMENT # 默认值为AUTO_INCREMENT
```
### 2. 配置文件方式
创建配置文件 `sqltune.yaml`:
```yaml
rules:
sql-rewrite:
source-database: oracle
target-database: mysql
```
使用 SQLTune 转换 SQL:
```java
import com.sqltune.core.SqlTuneEngine;
import com.sqltune.core.SqlTuneResult;
public class Example {
public static void main(String[] args) {
// 创建SQLTune引擎
SqlTuneEngine engine = SqlTuneEngine.builder()
.configFile("sqltune.yaml")
.build();
// Oracle SQL语句
String oracleSql = "SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10";
// 转换为MySQL SQL
SqlTuneResult result = engine.rewrite(oracleSql);
// 输出转换后的SQL
System.out.println("Original SQL: " + oracleSql);
System.out.println("Rewritten SQL: " + result.getRewrittenSql());
// 输出: SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 10
}
}
```
### 3. 编程方式配置
```java
import com.sqltune.core.SqlTuneEngine;
import com.sqltune.core.SqlTuneResult;
import com.sqltune.config.SqlTuneConfig;
public class Example {
public static void main(String[] args) {
// 创建配置
SqlTuneConfig config = SqlTuneConfig.builder()
.sourceDatabase("oracle")
.targetDatabase("mysql")
.build();
// 创建SQLTune引擎
SqlTuneEngine engine = SqlTuneEngine.builder()
.config(config)
.build();
// Oracle SQL语句示例
String[] oracleSqls = {
"SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10",
"SELECT id, NVL(name, 'Unknown') AS name FROM users",
"SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2023-01-01'"
};
// 转换为MySQL SQL
for (String sql : oracleSqls) {
SqlTuneResult result = engine.rewrite(sql);
System.out.println("Original SQL: " + sql);
System.out.println("Rewritten SQL: " + result.getRewrittenSql());
System.out.println();
}
}
}
```
### 4. 输出结果示例
```
Original SQL: SELECT * FROM users WHERE status = 'ACTIVE' AND ROWNUM <= 10
Rewritten SQL: SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 10
Original SQL: SELECT id, NVL(name, 'Unknown') AS name FROM users
Rewritten SQL: SELECT id, COALESCE(name, 'Unknown') AS name FROM users
Original SQL: SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2023-01-01'
Rewritten SQL: SELECT * FROM orders WHERE DATE_FORMAT(order_date, '%Y-%m-%d') = '2023-01-01'
```
## 环境要求与安装
### 环境要求
- JDK 1.8+
- Maven 3.5+
- Spring Boot 2.0+(使用 Spring Boot 集成时)
### 项目安装(可选,仅需本地开发时)
1. 克隆项目:
```bash
git clone https://gitee.com/finpire/sqltune.git
cd sqltune
```
2. 编译项目:
```bash
mvn clean install -DskipTests
```
## 贡献
欢迎提交 Issue 和 Pull Request!
## 许可证
SQLTune 采用 Apache License 2.0 许可证。
## 联系方式
如有问题或建议,请通过以下方式联系:
- Email: 602349518@qq.com
- GitHub Issues: https://gitee.com/finpire/sqltune/issues