我的習慣用MySQL workbench EER數據建模,而後生成SQL語句到數據庫中執行,這樣表之間的關係比較直觀。mysql
像下面這樣:git
畫圖github
正向工程,生成DDL語句:sql
忽略生成外鍵,以及外鍵索引啥的:數據庫
生成的DDL語句:app
到數據庫執行。工具
最近團隊微調,我被調整到另外一個小團隊。前兩天接了個新需求,因而我依然使用MySQL workbench EER建模,結果好不容易建模完成了,卻被告知這個項目用的數據庫是PostgreSQL!post
因而就面臨以下幾種選擇:開發工具
我選擇了本身轉換SQL語句。postgresql
既然要轉換SQL語句,我心想,業界確定有相關的工具啊。因而上萬能的GayHub搜了下,還真有,列出來:
然而試用後,心裏是崩潰的……生成出來的DDL要麼有誤,要麼沒有註釋。
考慮到個人訴求其實很是簡單,只是個DDL語句轉換而已,本身開發一個也不難。並且以前研讀Mybatis通用Mapper源碼時,知道Java世界裏有個jsqlparser
的工具。
花了10分鐘簡單瞭解了下jsqlparser
後,就開擼開發工具了……花了20分鐘,第一版寫完了,而後和該項目的同事又花了20分鐘驗證了下,最終肯定了以下的版本。代碼貼出來:
加依賴:
<dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>1.2</version> </dependency>
寫代碼:
public class MysqlDdl2PgDdlUtil { public static void main(String[] args) throws IOException, JSQLParserException { // 你的MySQL DDL路徑 String mysqlDDLPath = "/Users/reno/Downloads/mysql.sql"; String dDLs = FileUtils.readFileToString(new File(mysqlDDLPath)); System.out.println(dDLs); System.out.println("++++++++++開始轉換SQL語句+++++++++++++"); Statements statements = CCJSqlParserUtil.parseStatements(dDLs); statements.getStatements() .stream() .map(statement -> (CreateTable) statement).forEach(ct -> { Table table = ct.getTable(); List<ColumnDefinition> columnDefinitions = ct.getColumnDefinitions(); List<String> comments = new ArrayList<>(); List<ColumnDefinition> collect = columnDefinitions.stream() .peek(columnDefinition -> { List<String> columnSpecStrings = columnDefinition.getColumnSpecStrings(); int commentIndex = getCommentIndex(columnSpecStrings); if (commentIndex != -1) { int commentStringIndex = commentIndex + 1; String commentString = columnSpecStrings.get(commentStringIndex); String commentSql = genCommentSql(table.toString(), columnDefinition.getColumnName(), commentString); comments.add(commentSql); columnSpecStrings.remove(commentStringIndex); columnSpecStrings.remove(commentIndex); } columnDefinition.setColumnSpecStrings(columnSpecStrings); }).collect(Collectors.toList()); ct.setColumnDefinitions(collect); String createSQL = ct.toString() .replaceAll("`", "\"") .replaceAll("BIGINT UNIQUE NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY") .replaceAll("BIGINT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY") .replaceAll("BIGINT NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY") .replaceAll("INT NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY") .replaceAll("INT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY") .replaceAll("IF NOT EXISTS", "") .replaceAll("TINYINT", "SMALLINT") .replaceAll("DATETIME", "TIMESTAMP") .replaceAll(", PRIMARY KEY \\(\"id\"\\)", ""); // 若是存在表註釋 if (createSQL.contains("COMMENT")) { createSQL = createSQL.substring(0, createSQL.indexOf("COMMENT")); } System.out.println(createSQL + ";"); comments.forEach(t -> System.out.println(t.replaceAll("`", "\"") + ";")); }); } /** * 得到註釋的下標 * * @param columnSpecStrings columnSpecStrings * @return 下標 */ private static int getCommentIndex(List<String> columnSpecStrings) { for (int i = 0; i < columnSpecStrings.size(); i++) { if ("COMMENT".equalsIgnoreCase(columnSpecStrings.get(i))) { return i; } } return -1; } /** * 生成COMMENT語句 * * @param table 表名 * @param column 字段名 * @param commentValue 描述文字 * @return COMMENT語句 */ private static String genCommentSql(String table, String column, String commentValue) { return String.format("COMMENT ON COLUMN %s.%s IS %s", table, column, commentValue); } }
如代碼所示,目前是藉助jsqlparser
的SQL解析能力配合字符串替換的方式生成PostgreSQL的。
轉換前的DDL:
-- ----------------------------------------------------- -- Table `user` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `user` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT 'id', `username` VARCHAR(16) NOT NULL COMMENT '用戶名', `email` VARCHAR(255) NULL COMMENT '郵件', `password` VARCHAR(32) NOT NULL COMMENT '密碼', `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間', PRIMARY KEY (`id`)); -- ----------------------------------------------------- -- Table `movie` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `movie` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT 'Id', `name` VARCHAR(255) NOT NULL COMMENT '名稱', `user_id` INT NOT NULL COMMENT 'user.id', PRIMARY KEY (`id`)) COMMENT = '電影表';
轉換後的DDL:
CREATE TABLE "user" ( "id" BIGSERIAL PRIMARY KEY, "username" VARCHAR(16) NOT NULL, "email" VARCHAR(255) NULL, "password" VARCHAR(32) NOT NULL, "create_time" TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ); COMMENT ON COLUMN "user"."id" IS 'id'; COMMENT ON COLUMN "user"."username" IS '用戶名'; COMMENT ON COLUMN "user"."email" IS '郵件'; COMMENT ON COLUMN "user"."password" IS '密碼'; COMMENT ON COLUMN "user"."create_time" IS '建立時間'; CREATE TABLE "movie" ( "id" BIGSERIAL PRIMARY KEY, "name" VARCHAR(255) NOT NULL, "user_id" INT NOT NULL ); COMMENT ON COLUMN "movie"."id" IS 'Id'; COMMENT ON COLUMN "movie"."name" IS '名稱'; COMMENT ON COLUMN "movie"."user_id" IS 'user.id';
效果仍是不錯的,基本達到了個人要求。
目前工具代碼比較屎,若是想要改進,應該是要讓工具理解MySQL DDL的詞法,而後構建成例如Table、Column、Comment、Constraint、Index等對象例如:
class Table { private String name; private Column column; } class Column { private String name; private String type; // 約束,例如非空等 private Set<Constraint> constraints; // 索引 private Index index; } class Index { private String name; private String type; } enum Constraint { NOT_NULL,...; }
而後抽象一個方言枚舉,併爲不一樣的方言製做一個DDL Generator Handler,而後根據不一樣的方言生成不一樣數據庫平臺的DDL語句。
爲何不改進?由於沒有時間,工具是爲工做服務的,目前能達到個人目的,就沒動力修改了,將來有需求再改進吧。
http://www.itmuch.com/work/mysql-ddl-2-pgsql-ddl/ ,轉載請說明出處。