From 683585c5275ea746eae7cfa0ee2ac4d0a2507cf7 Mon Sep 17 00:00:00 2001 From: diantu Date: Tue, 31 Jan 2023 15:33:23 +0800 Subject: [PATCH] =?UTF-8?q?mysql->oracle=E5=BB=BA=E8=A1=A8=E8=AF=AD?= =?UTF-8?q?=E5=8F=A5=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mysql/MySqlToOracleOutputVisitor.java | 118 ++++++++++++++++++ .../util/MySqlSQLDataTypeTransformUtil.java | 104 +++++++++++++++ .../dialect/oracle/function/OracleUtil.java | 25 ++++ .../db/framework/utils/SQLTransformUtils.java | 19 +++ .../db/service/database/MysqlServiceImpl.java | 9 +- .../service/database/OracleServiceImpl.java | 1 - 6 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/mysql/util/MySqlSQLDataTypeTransformUtil.java create mode 100644 zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/oracle/function/OracleUtil.java diff --git a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/mysql/MySqlToOracleOutputVisitor.java b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/mysql/MySqlToOracleOutputVisitor.java index 5845772a..2be4ff6d 100644 --- a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/mysql/MySqlToOracleOutputVisitor.java +++ b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/mysql/MySqlToOracleOutputVisitor.java @@ -1,6 +1,31 @@ package com.zyplayer.doc.db.framework.db.sql.dialect.mysql; +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePrimaryKey; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUnique; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.util.FnvHash; +import com.zyplayer.doc.db.framework.db.sql.dialect.mysql.function.MySqlToOracleFunctionTransform; +import com.zyplayer.doc.db.framework.db.sql.dialect.mysql.util.MySqlSQLDataTypeTransformUtil; +import com.zyplayer.doc.db.framework.db.sql.dialect.oracle.function.OracleUtil; +import com.zyplayer.doc.db.framework.utils.MapCacheUtil; + +import java.util.List; +import java.util.Objects; /** * mysql转oracle遍历实现 @@ -10,6 +35,11 @@ import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; */ public class MySqlToOracleOutputVisitor extends MySqlOutputVisitor { + private final MySqlToOracleFunctionTransform functionTransform = new MySqlToOracleFunctionTransform(); + + // 目标数据库类型 + private final DbType distDbType = DbType.oracle; + public MySqlToOracleOutputVisitor(Appendable appender) { super(appender); } @@ -18,5 +48,93 @@ public class MySqlToOracleOutputVisitor extends MySqlOutputVisitor { super(appender, parameterized); } + /** + * mysql建表语句遍历 + * @param x + * @return + */ + @Override + public boolean visit(MySqlCreateTableStatement x) { + OracleCreateTableStatement oracleCreateTableStatement = new OracleCreateTableStatement(); + oracleCreateTableStatement.setTableSource(x.getTableSource()); + String tableName = x.getTableSource().getName().getSimpleName(); + oracleCreateTableStatement.setName(String.valueOf(x.getName()).replaceAll("\"", "")); + oracleCreateTableStatement.setName(String.valueOf(x.getName()).replaceAll("`", "")); + oracleCreateTableStatement.setComment(x.getComment()); + for(SQLTableElement sqlTableElement : x.getTableElementList()){ + if( sqlTableElement instanceof SQLColumnDefinition) { + SQLColumnDefinition sqlColumnDefinition = ((SQLColumnDefinition)sqlTableElement); + String columnName = sqlColumnDefinition.getName().getSimpleName().replaceAll("\"", ""); + columnName = columnName.replaceAll("`", ""); + sqlColumnDefinition.setName(columnName); + if(OracleUtil.containsKeyWords(columnName)){ + sqlColumnDefinition.setName("\""+columnName+"\""); + } + sqlColumnDefinition.setDataType(MySqlSQLDataTypeTransformUtil.transformMySqlToOracle(SQLParserUtils.createExprParser(sqlColumnDefinition.getDataType().toString(), DbType.mysql).parseDataType())); + if(sqlColumnDefinition.getDefaultExpr() != null) { + SQLExpr expr = sqlColumnDefinition.getDefaultExpr(); + if(expr instanceof SQLMethodInvokeExpr) { + functionTransform.methodInvoke((SQLMethodInvokeExpr) sqlColumnDefinition.getDefaultExpr()); + }else if(expr instanceof SQLIdentifierExpr) { + SQLIdentifierExpr sqlIdentifierExpr = (SQLIdentifierExpr) expr; + final long nameHashCode64 = sqlIdentifierExpr.nameHashCode64(); + if(nameHashCode64 == FnvHash.Constants.SYSTIMESTAMP) { + SQLIdentifierExpr xx = sqlIdentifierExpr.clone(); + xx.setName("CURRENT_TIMESTAMP"); + xx.setParent(sqlIdentifierExpr.getParent()); + sqlColumnDefinition.setDefaultExpr(xx); + if(sqlColumnDefinition.getColumnName().contains("UPDATE_TIME") + || sqlColumnDefinition.getColumnName().contains("MDFY_TIME")) { + sqlColumnDefinition.setOnUpdate(xx); + } + } + }else if(expr instanceof OracleSysdateExpr) { + SQLIdentifierExpr xx = new SQLIdentifierExpr("CURRENT_TIMESTAMP"); + xx.setParent(expr.getParent()); + sqlColumnDefinition.setDefaultExpr(xx); + if(sqlColumnDefinition.getColumnName().contains("UPDATE_TIME") + || sqlColumnDefinition.getColumnName().contains("MDFY_TIME")) { + sqlColumnDefinition.setOnUpdate(xx); + } + } + } + //@TODO 自增转换(oracle没有AUTO_INCREMENT) + if(sqlColumnDefinition.isAutoIncrement()){ + sqlColumnDefinition.setAutoIncrement(false); + //SQLIdentifierExpr xx = new SQLIdentifierExpr("sys_guid()"); + //xx.setName("sys_guid()"); + //sqlColumnDefinition.setDefaultExpr(xx); + } + //@TODO 注释待转换 + if(sqlColumnDefinition.getComment()!= null){ + sqlColumnDefinition.setComment((String) null); + } + sqlColumnDefinition.setDbType(distDbType); + MapCacheUtil.getInstance().addCacheData(tableName.toUpperCase() + ":" + columnName.toUpperCase(), sqlColumnDefinition.toString().replaceAll(sqlColumnDefinition.getColumnName(), "")); + oracleCreateTableStatement.getTableElementList().add(sqlColumnDefinition); + }else if(sqlTableElement instanceof MySqlPrimaryKey){ + OraclePrimaryKey oraclePrimaryKey = new OraclePrimaryKey(); + List list = ((MySqlPrimaryKey) sqlTableElement).getIndexDefinition().getColumns(); + for(int i=0;i argumentns = x.getArguments(); + SQLDataType dataType; + if (nameHash == FnvHash.Constants.SMALLINT + //|| nameHash == FnvHash.Constants.MEDIUMINT + || nameHash == FnvHash.Constants.INT + || nameHash == FnvHash.Constants.BIGINT + || nameHash == FnvHash.Constants.TINYINT) { + if(argumentns.size() > 0){ + int len; + SQLExpr arg0 = argumentns.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toMySqlString(x)); + } + dataType = new SQLDataTypeImpl("NUMBER",len); + } else { + dataType = new SQLCharacterDataType("NUMBER"); + } + + } else if (nameHash == FnvHash.Constants.FLOAT + || nameHash == FnvHash.Constants.DOUBLE + || nameHash == FnvHash.Constants.DECIMAL) { + dataType = new SQLDataTypeImpl("DECIMAL"); + int precision = 0; + if (argumentns.size() > 0) { + precision = ((SQLIntegerExpr) argumentns.get(0)).getNumber().intValue(); + dataType = new SQLDataTypeImpl("DECIMAL",precision); + } + + int scale = 0; + if (argumentns.size() > 1) { + scale = ((SQLIntegerExpr) argumentns.get(1)).getNumber().intValue(); + dataType = new SQLDataTypeImpl("DECIMAL",precision,scale); + } + + } else if (nameHash == FnvHash.Constants.DATE + || nameHash == FnvHash.Constants.DATETIME) { + dataType = new SQLDataTypeImpl("DATE"); + + } else if (nameHash == FnvHash.Constants.TIMESTAMP) { + dataType = new SQLDataTypeImpl("TIMESTAMP"); + + } else if (nameHash == FnvHash.Constants.VARCHAR + ||nameHash == FnvHash.Constants.CHAR) { + if(argumentns.size() > 0){ + int len; + SQLExpr arg0 = argumentns.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toMySqlString(x)); + } + dataType = new SQLDataTypeImpl("VARCHAR2",len); + }else{ + dataType = new SQLDataTypeImpl("VARCHAR2"); + } + + } else if (nameHash == FnvHash.Constants.LONGTEXT) { + argumentns.clear(); + dataType = new SQLCharacterDataType("CLOB"); + + } else if (nameHash == FnvHash.Constants.BOOLEAN) { + dataType = new SQLDataTypeImpl("NUMBER",1); + + } else { + dataType = x; + } + + if (dataType != x) { + dataType.setParent(x.getParent()); + } + + return dataType; + } +} diff --git a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/oracle/function/OracleUtil.java b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/oracle/function/OracleUtil.java new file mode 100644 index 00000000..94df6ba4 --- /dev/null +++ b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/db/sql/dialect/oracle/function/OracleUtil.java @@ -0,0 +1,25 @@ +package com.zyplayer.doc.db.framework.db.sql.dialect.oracle.function; + +import com.alibaba.druid.sql.dialect.oracle.parser.OracleLexer; +import org.springframework.util.StringUtils; + +/** + * oracle sql语句相关工具类 + * + * @author diantu + * @since 2023年1月30日 + */ +public class OracleUtil { + + /** + * 是否包含关键词 + * @param name + * @return + */ + public static boolean containsKeyWords(String name) { + if (StringUtils.isEmpty(name)) { + return false; + } + return OracleLexer.DEFAULT_ORACLE_KEYWORDS.getKeywords().containsKey(name.toUpperCase()); + } +} diff --git a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/utils/SQLTransformUtils.java b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/utils/SQLTransformUtils.java index a97f7379..d04eafb3 100644 --- a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/utils/SQLTransformUtils.java +++ b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/framework/utils/SQLTransformUtils.java @@ -3,6 +3,7 @@ package com.zyplayer.doc.db.framework.utils; import com.alibaba.druid.DbType; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLStatement; +import com.zyplayer.doc.db.framework.db.sql.dialect.mysql.MySqlToOracleOutputVisitor; import com.zyplayer.doc.db.framework.db.sql.dialect.oracle.OracleToMySqlOutputVisitor; import java.io.BufferedReader; @@ -37,6 +38,24 @@ public class SQLTransformUtils { return mysqlSql; } + /** + * mysql sql语句转换为oracle sql语句 + * @param sql + * @return + */ + public static String translateMySqlToOracle(String sql) { + List stmtList = SQLUtils.toStatementList(sql, DbType.mysql); + StringBuilder out = new StringBuilder(); + MySqlToOracleOutputVisitor visitor = new MySqlToOracleOutputVisitor(out, false); + + for(int i = 0; i < stmtList.size(); ++i) { + ((SQLStatement)stmtList.get(i)).accept(visitor); + } + + String oracleSql = out.toString(); + return oracleSql; + } + /** * 将字CLOB转成STRING类型 * @param clob diff --git a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/MysqlServiceImpl.java b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/MysqlServiceImpl.java index 4336a26f..96c33cee 100644 --- a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/MysqlServiceImpl.java +++ b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/MysqlServiceImpl.java @@ -12,6 +12,7 @@ import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteParam; import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteResult; import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteType; import com.zyplayer.doc.db.framework.db.mapper.mysql.MysqlMapper; +import com.zyplayer.doc.db.framework.utils.SQLTransformUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -109,11 +110,11 @@ public class MysqlServiceImpl extends DbBaseService { List> tableDdlList = baseMapper.getTableDdl(dbName, tableName); TableDdlVo tableDdlVo = new TableDdlVo(); tableDdlVo.setCurrent(DatabaseProductEnum.MYSQL.name().toLowerCase()); - tableDdlVo.setMysql("// 生成失败"); - tableDdlVo.setOracle("// TODO 等待大佬来实现转换"); - // TODO 将建表语句转换为其他数据库的,还不知道怎么做,先这样留着,看有没大佬来实现 if (CollectionUtils.isNotEmpty(tableDdlList)) { - tableDdlVo.setMysql(tableDdlList.get(0).get("Create Table") + ";"); + String mysqlSql = tableDdlList.get(0).get("Create Table") + ";"; + tableDdlVo.setMysql(mysqlSql); + tableDdlVo.setOracle(SQLTransformUtils.translateMySqlToOracle(mysqlSql)); + // TODO sqlserver等数据库同理 } return tableDdlVo; } diff --git a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/OracleServiceImpl.java b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/OracleServiceImpl.java index daeacf61..c78add2b 100644 --- a/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/OracleServiceImpl.java +++ b/zyplayer-doc-db/src/main/java/com/zyplayer/doc/db/service/database/OracleServiceImpl.java @@ -84,7 +84,6 @@ public class OracleServiceImpl extends DbBaseService { } catch (IOException e) { throw new RuntimeException(e); } - //String oracleSql = tableDdlList.get(0).get("CREATETABLE") + ";"; tableDdlVo.setOracle(oracleSql); //oracle建表语句转换为mysql建表语句 String mysqlSql = SQLTransformUtils.translateOracleToMySql(oracleSql);