MySql表结构迁移到PostgreSql(pgsql) 方案

本文介绍了一位开发者如何使用Java代码手动迁移MySQL数据库表结构到PostgreSQL,包括获取表结构、类型转换和生成迁移脚本,由于商业限制未使用付费工具而选择自定义解决方案。
摘要由CSDN通过智能技术生成

由于工作需要,要将原有的mysql的表结构和数据都迁移到pgsql里面,因为pgsql支持更大数据存储,还能做AI大模型的向量数据库。
网上找了很多种办法,这种跨数据库的迁移方案都是收费的。只有navicate 工具可以免费迁移,怎耐我们公司禁止使用navicate 。

没有办法,我只能首先java代码实现数据库表结构迁移工作。

下面就是代码,本打算从mysql读取所有的数据库结构直接插入到pgsql里面,但是实际却发现有很多不合理的地方,与改成了将所有的mysql结构生成pgsql 的结构打印出来。使用者可以拷贝打印出来的内容形成一个脚本再使用。

里面有类型转换我中用到我们实际使用到的结果,还有不少没列出来,使用时要注意一下。

import java.sql.*;
import java.util.*;

public class MysqlToPgsqlMigration {
    private static final String MYSQL_URL = "jdbc:mysql://127.0.0.1:3306/test";
    private static final String MYSQL_USER = "root";
    private static final String MYSQL_PASSWORD = "root";

    private static final String POSTGRESQL_URL = "jdbc:postgresql://127.0.0.1:5432/jeecg-boot";
    private static final String POSTGRESQL_USER = "root";
    private static final String POSTGRESQL_PASSWORD = "root1";

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Class.forName("org.postgresql.Driver");

        try (Connection mysqlConn = DriverManager.getConnection(MYSQL_URL, MYSQL_USER, MYSQL_PASSWORD);
             Connection pgsqlConn = DriverManager.getConnection(POSTGRESQL_URL, POSTGRESQL_USER, POSTGRESQL_PASSWORD)) {

            DatabaseMetaData mysqlMeta = mysqlConn.getMetaData();
            Statement pgsqlStmt = pgsqlConn.createStatement();

            ResultSet tablesRs = mysqlMeta.getTables(null, null, "%", new String[]{"TABLE"});

            while (tablesRs.next()) {
                String tableName = tablesRs.getString("TABLE_NAME");
                // 重建表结构
                StringBuilder createTableSql = new StringBuilder("CREATE TABLE ");
                createTableSql.append(tableName).append(" (\n");

                ResultSet columnsRs = mysqlMeta.getColumns(null, null, tableName, null);
                List<String> primaryKeyColumns = new ArrayList<>();
                List<String> indexKeyColumns = new ArrayList<>();
                List<String> columnComments = new ArrayList<>();
                List<String>  uniqueindexKeyColumns = new ArrayList<>();
                while (columnsRs.next()) {
                    String columnName = columnsRs.getString("COLUMN_NAME");
                    int dataType = columnsRs.getInt("DATA_TYPE");
                    String typeName = mapColumnType(dataType);
                    int size = columnsRs.getInt("COLUMN_SIZE");
                    boolean isNullable = columnsRs.getBoolean("NULLABLE");
                    String columnDef = columnsRs.getString("COLUMN_DEF");
                    String columnComment = columnsRs.getString("REMARKS");

                    createTableSql.append("\t").append(columnName).append(' ').append(typeName);
                    if (!"INTEGER".equalsIgnoreCase(typeName)
                            &&!"int4".equalsIgnoreCase(typeName)
                            &&!"int8".equalsIgnoreCase(typeName)
                            &&!"smallint".equalsIgnoreCase(typeName)
                            &&!"bigint".equalsIgnoreCase(typeName)
                            &&!"text".equalsIgnoreCase(typeName)){
                        if (size > 0) {
                            createTableSql.append('(').append(size).append(')');
                        }
                    }

                    if (!isNullable) {
                        createTableSql.append(" NOT NULL");
                    }

                    if (columnDef != null) {
                        if ("INTEGER".equalsIgnoreCase(typeName)
                                ||"int4".equalsIgnoreCase(typeName)
                                ||"int8".equalsIgnoreCase(typeName)
                                ||"smallint".equalsIgnoreCase(typeName)
                                ||"bigint".equalsIgnoreCase(typeName)
                                ||"NUMERIC".equalsIgnoreCase(typeName)){
                            createTableSql.append(" DEFAULT ").append(columnDef);
                        }else if("TIMESTAMP".equalsIgnoreCase(typeName)){
                            createTableSql.append(" DEFAULT ").append("TO_TIMESTAMP('2024-02-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')");
                        }else{
                            createTableSql.append(" DEFAULT ").append("'" + escapeSingleQuotes(columnDef) + "'");
                        }
                    }

                    columnComments.add(addColumnComment(tableName, columnName, columnComment, pgsqlStmt));

                    createTableSql.append(",\n");

                    // 检查是否为主键
                    ResultSet pkRs = mysqlMeta.getPrimaryKeys(null, null, tableName);
                    while (pkRs.next() && pkRs.getString("COLUMN_NAME").equals(columnName)) {
                        primaryKeyColumns.add(columnName);
                    }
                    pkRs.close();
                    
                }

                // 移除最后一个逗号和换行符
                createTableSql.setLength(createTableSql.length() - 2);

                // 添加主键约束
                if (!primaryKeyColumns.isEmpty()) {
                    createTableSql.append(", \n    PRIMARY KEY (")
                            .append(String.join(",", primaryKeyColumns))
                            .append(")");
                }
                

                createTableSql.append("\n);");
                System.out.println(createTableSql);
                //pgsqlStmt.executeUpdate(createTableSql.toString());

                // 处理索引及联合索引
                Map<String,List<String>> unIndexMap = new HashMap<>();
                ResultSet uniqueindexSet = mysqlMeta.getIndexInfo(null, null, tableName, true, false);
                while (uniqueindexSet.next()) {
                    String indexName = uniqueindexSet.getString("INDEX_NAME"); // 索引名称
                    String columnName = uniqueindexSet.getString("COLUMN_NAME"); // 索引列名
                    /*boolean isNonUnique = uniqueindexSet.getBoolean("NON_UNIQUE"); // 是否非唯一索引(0表示唯一索引,1表示非唯一索引)
                    System.out.println("indexName="+indexName+",columnName="+columnName+",isNonUnique="+isNonUnique);*/
                    if ("PRIMARY".equalsIgnoreCase(indexName)){
                        continue;
                    }
                    List<String> indexCols = unIndexMap.get(indexName);
                    if (indexCols == null){
                        indexCols = new ArrayList<>();
                    }
                    if (!indexCols.contains(columnName)){
                        indexCols.add(columnName);
                    }
                    unIndexMap.put(indexName,indexCols);
                    
                    uniqueindexKeyColumns.add(columnName);
                }
                for (Map.Entry<String, List<String>> entry : unIndexMap.entrySet()) {
                    String indexName = entry.getKey();
                    List<String> list = entry.getValue();
                    if (list.size()>1){ // 联合索引
                        System.out.println("CREATE UNIQUE INDEX "+indexName+" ON "+tableName+"("+String.join(",",list)+");");
                    }else{// 普通索引
                        if (indexName.startsWith("uni")||indexName.startsWith("uk_")){
                            System.out.println("CREATE UNIQUE INDEX "+indexName+" ON "+tableName+"("+list.get(0)+");");
                        }else{
                            System.out.println("CREATE INDEX "+indexName+" ON "+tableName+"("+list.get(0)+");");
                        }
                    }
                }
                uniqueindexSet.close();

                // 处理外键索引
                /*ResultSet foreignKeysRS = mysqlMeta.getImportedKeys(null, null, tableName);
                while (foreignKeysRS.next()) {
                    String fkName = foreignKeysRS.getString("FK_NAME"); // 外键约束名称
                    String fkColumnName = foreignKeysRS.getString("FKCOLUMN_NAME"); // 本表中对应的外键列名
                    String pkTableName = foreignKeysRS.getString("PKTABLE_NAME"); // 主键所在的表名
                    String pkColumnName = foreignKeysRS.getString("PKCOLUMN_NAME"); // 主键列名

                    System.out.println("Foreign Key: " + fkName);
                    System.out.println("From Column: " + fkColumnName + " in Table " + tableName);
                    System.out.println("References Column: " + pkColumnName + " in Table " + pkTableName);
                }
                foreignKeysRS.close();*/
                
                //字段注释
                if (!columnComments.isEmpty()){
                    for (String comment : columnComments) {
                        System.out.println(comment);
                    }
                }
                // 获取表注释并添加
                String tableComment = getTableComment(tableName, mysqlMeta);
                System.out.println(addTableComment(tableName, tableComment, pgsqlStmt));
                System.out.println("\n");
                // 插入数据
                //copyTableData(tableName, mysqlConn, pgsqlConn);
            }

            tablesRs.close();
        }
    }

    private static String addTableComment(String tableName, String comment, Statement stmt) throws SQLException {
        /*if (comment != null && !comment.isEmpty()) {
            stmt.executeUpdate("COMMENT ON TABLE " + tableName + " IS '" + escapeSingleQuotes(comment) + "'");
        }*/
        return "COMMENT ON TABLE " + tableName + " IS '" + escapeSingleQuotes(comment) + "';";
    }

    private static String addColumnComment(String tableName, String columnName, String comment, Statement stmt) throws SQLException {
        return "COMMENT ON COLUMN " + tableName + "." + columnName + " IS '" + escapeSingleQuotes(comment) + "';";
        /*if (comment != null && !comment.isEmpty()) {
            stmt.executeUpdate("COMMENT ON COLUMN " + tableName + "." + columnName + " IS '" + escapeSingleQuotes(comment) + "'");
        }*/
    }

    private static String getTableComment(String tableName, DatabaseMetaData meta) throws SQLException {
        ResultSet rs = meta.getTables(null, null, tableName, null);
        if (rs.next()) {
            return rs.getString("REMARKS");
        }
        return "";
    }

    private static String mapColumnType(int mysqlDataType) {
        // 这里仅提供一种简单的映射方式,实际情况可能更复杂
        switch (mysqlDataType) {
//            case Types.BIT:
//                return "BOOLEAN";
            case Types.TINYINT:
                return "SMALLINT";
            case Types.BIGINT:
                return "BIGINT";
            case Types.INTEGER:
                return "INTEGER";
            case Types.DATE:
                return "timestamp";
            case Types.DOUBLE:
                return "NUMERIC";
            case Types.NUMERIC:
                return "NUMERIC";
            case Types.DECIMAL:
                return "NUMERIC";
            case Types.LONGVARCHAR:
                return "text";
            case Types.TIMESTAMP:
                return "TIMESTAMP";
            case Types.BLOB:
                return "BYTEA";
            // 其他类型...
            default:
                return "varchar";
        }
    }

    private static void copyTableData(String tableName, Connection mysqlConn, Connection pgsqlConn) throws SQLException {
        String selectAllDataSql = "SELECT * FROM " + tableName;
        PreparedStatement mysqlPs = mysqlConn.prepareStatement(selectAllDataSql);
        ResultSet dataRs = mysqlPs.executeQuery();
        ResultSetMetaData metaData = dataRs.getMetaData();
        StringBuilder insertSql = new StringBuilder("INSERT INTO ").append(tableName).append(" VALUES (");

        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            insertSql.append("?");
            if (i < metaData.getColumnCount()) {
                insertSql.append(",");
            }
        }
        insertSql.append(")");

        PreparedStatement pgsqlPs = pgsqlConn.prepareStatement(insertSql.toString());
        while (dataRs.next()) {
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                pgsqlPs.setObject(i, dataRs.getObject(i));
            }
            pgsqlPs.addBatch();
        }
        pgsqlPs.executeBatch();
    }

    private static String escapeSingleQuotes(String str) {
        return str.replace("'", "''");
    }
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用以下步骤将MySQL结构同步到PostgreSQL数据库中: 1. 使用Go语言的MySQL驱动和PostgreSQL驱动分别连接MySQL数据库PostgreSQL数据库。 2. 查询MySQL数据库中需要同步的结构信息,可以使用SHOW CREATE TABLE语句获取结构信息。 3. 解析MySQL结构信息,并将其转换为PostgreSQL结构信息,比如将MySQL的VARCHAR转换为PostgreSQL的TEXT。 4. 使用Go语言的PostgreSQL驱动在PostgreSQL数据库中创建新的结构。 完整代码示例如下: ``` package main import ( "database/sql" "fmt" "log" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" ) func main() { // connect to MySQL mysqlDB, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database") if err != nil { log.Fatal(err) } defer mysqlDB.Close() // connect to PostgreSQL postgresDB, err := sql.Open("postgres", "postgres://user:password@localhost/postgres?sslmode=disable") if err != nil { log.Fatal(err) } defer postgresDB.Close() // query MySQL table structure rows, err := mysqlDB.Query("SHOW CREATE TABLE mytable") if err != nil { log.Fatal(err) } defer rows.Close() // parse MySQL table structure and convert to PostgreSQL table structure var tableName string var createTableStmt string for rows.Next() { err := rows.Scan(&tableName, &createTableStmt) if err != nil { log.Fatal(err) } } if err := rows.Err(); err != nil { log.Fatal(err) } postgresTableStmt := convertMySQLToPostgreSQL(createTableStmt) // create new table in PostgreSQL _, err = postgresDB.Exec(postgresTableStmt) if err != nil { log.Fatal(err) } fmt.Println("Table structure synced from MySQL to PostgreSQL") } func convertMySQLToPostgreSQL(mysqlTableStmt string) string { // TODO: implement conversion logic return mysqlTableStmt } ``` 在上述代码中,需要根据实际情况修改MySQLPostgreSQL的连接信息,以及需要同步的名和结构信息。同时,还需要实现convertMySQLToPostgreSQL函数,用于将MySQL结构信息转换为PostgreSQL结构信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值