Java工具类对比MySQL数据库表字段

表做对比

  1. 字段类型
  2. 字段长度
  3. 注释

可以自定义扩展对比内容

public class SqlTableDifference {

    public static void main(String[] args) {
        List<String> strings = compareTables("jdbc:mysql://xxxx:xxx/xxx", "root", "xxx!", "xxx",
                "jdbc:mysql://xxxx:xxx/xxx", "root", "xxx!", "xxx");

        for (String ddl : strings) {
            System.out.println(ddl);
        }
    }

    /**
     * 比较两个表的结构差异,并生成DDL语句
     *
     * @param jdbcUrl1      第一个数据源的JDBC URL
     * @param jdbcUser1     第一个数据源的用户名
     * @param jdbcPassword1 第一个数据源的密码
     * @param table1        第一个表名
     * @param jdbcUrl2      第二个数据源的JDBC URL
     * @param jdbcUser2     第二个数据源的用户名
     * @param jdbcPassword2 第二个数据源的密码
     * @param table2        第二个表名
     * @return 包含DDL语句的列表
     */
    public static List<String> compareTables(String jdbcUrl1, String jdbcUser1, String jdbcPassword1, String table1,
                                             String jdbcUrl2, String jdbcUser2, String jdbcPassword2, String table2) {
        List<Map<String, Object>> table1Fields = getTableFields(jdbcUrl1, jdbcUser1, jdbcPassword1, table1);
        List<Map<String, Object>> table2Fields = getTableFields(jdbcUrl2, jdbcUser2, jdbcPassword2, table2);

        List<String> ddlStatements = new ArrayList<>();

        // 对比字段差异
        // 检查在table1中存在但在table2中不存在的字段,并生成相应的DDL语句添加到table2中
        for (Map<String, Object> field1 : table1Fields) {
            Map<String, Object> field2 = getFieldByName(2, table2Fields, (String) field1.get("name"));
            if (field2 != null) {
                // 比较类型是否相同
                if (!field1.get("type").equals(field2.get("type"))) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s %s", table2, field1.get("name"), field1.get("type")) + ";");
                }
                // 比较长度是否相同
                if (field1.get("type").equals(field2.get("type")) && !field1.get("length").equals(field2.get("length"))) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s %s(%d)", table2, field1.get("name"), field1.get("type"), field1.get("length")) + ";");
                }
                // 比较注释是否相同
                if (!Objects.equals(field1.get("comment"), field2.get("comment"))) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s COMMENT '%s'", table2, field1.get("name"), field1.get("comment")) + ";");
                }
            } else {
                // table2中不存在field1,在table2中添加字段的DDL语句
                ddlStatements.add(String.format("ALTER TABLE %s ADD COLUMN %s %s(%d) COMMENT '%s'",
                        table2, field1.get("name"), field1.get("type"), field1.get("length"), field1.get("comment")) + ";");
            }
        }

        // 处理在table2中存在但在table1中不存在的字段,生成删除字段的DDL语句
        for (Map<String, Object> field2 : table2Fields) {
            if (getFieldByName(2, table1Fields, (String) field2.get("name")) == null) {
                ddlStatements.add(String.format("ALTER TABLE %s DROP COLUMN %s", table2, field2.get("name")) + ";");
            }
        }

        return ddlStatements;
    }

    // 获取表的字段信息
    private static List<Map<String, Object>> getTableFields(String jdbcUrl, String jdbcUser, String jdbcPassword, String tableName) {
        List<Map<String, Object>> fields = new ArrayList<>();
        try (
                Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(String.format("SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, COLUMN_COMMENT " +
                        "FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", getDatabaseName(jdbcUrl), tableName))
        ) {
            while (rs.next()) {
                String name = rs.getString("COLUMN_NAME");
                String type = rs.getString("DATA_TYPE");
                int length = rs.getInt("CHARACTER_MAXIMUM_LENGTH");
                String comment = rs.getString("COLUMN_COMMENT");
                int numPrecision = rs.getInt("NUMERIC_PRECISION");
                int numScale = rs.getInt("NUMERIC_SCALE");

                // 将字段信息存入Map
                Map<String, Object> fieldInfo = new HashMap<>();
                fieldInfo.put("name", name);
                fieldInfo.put("type", type);
                fieldInfo.put("length", length);
                fieldInfo.put("max", numPrecision);
                fieldInfo.put("min", numScale);
                fieldInfo.put("comment", comment == null ? "" : comment);
                fields.add(fieldInfo);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return fields;
    }

    // 根据字段名从列表中获取字段信息
    private static Map<String, Object> getFieldByName(int type, List<Map<String, Object>> fields, String name) {
        // 兼容一下数据同步生成sql采用code
        if (type == 1) {
            for (Map<String, Object> field : fields) {
                if (field.get("code").equals(name)) {
                    return field;
                }
            }
        } else {
            // 直接查出来的mysql 用name字段表示
            for (Map<String, Object> field : fields) {
                if (field.get("name").equals(name)) {
                    return field;
                }
            }
        }

        return null;
    }

    // 从 JDBC URL 中提取数据库名称
    private static String getDatabaseName(String jdbcUrl) {
        String[] parts = jdbcUrl.split("/");
        String dbNamePart = parts[parts.length - 1];
        return dbNamePart.split("\\?")[0];  // 去除可能包含的查询参数部分
    }


    /**
     * 生成的sql 与 数据库表字段作对比
     *
     * @param fieldList     生成sql
     * @param jdbcUrl2      url
     * @param jdbcUser2     user
     * @param jdbcPassword2 password
     * @param table2        表
     * @return ddl
     */
    public static List<String> checkColum(List<Map> fieldList, String jdbcUrl2, String jdbcUser2, String jdbcPassword2, String table2) {

        List<Map<String, Object>> table1Fields = new ArrayList<>();
        for (int i = 0; i < fieldList.size(); i++) {
            Map<String, Object> map = fieldList.get(i);
            table1Fields.add(map);
        }

        List<Map<String, Object>> table2Fields = getTableFields(jdbcUrl2, jdbcUser2, jdbcPassword2, table2);

        List<String> ddlStatements = new ArrayList<>();

        for (Map<String, Object> field1 : table1Fields) {
            Map<String, Object> field2 = getFieldByName(2, table2Fields, (String) field1.get("code"));
            if (field2 != null) {
                // 比较类型是否相同
                if (!field1.get("dbType").equals(field2.get("type"))) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s %s", table2, field1.get("name"), field1.get("dbType")) + ";");
                }
                // 比较长度是否相同
                if (field1.get("dbType").equals("decimal")) {
                    if (field1.get("length").equals(field2.get("max").toString()) && field1.get("accuracy").equals(field2.get("min").toString())) {
                        continue;
                    } else {
                        ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s %s(%d,%d)", table2, field1.get("code"), field1.get("dbType"), Integer.parseInt(field1.get("length").toString()), Integer.parseInt(field1.get("accuracy").toString())) + ";");
                    }
                } else if (field1.get("dbType").equals("date") && field2.get("type").equals("date")) {
                    // 如果是日期直接 跳过
                    continue;
                } else if (field1.get("dbType").equals(field2.get("type")) && !field1.get("length").equals(field2.get("length").toString())) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s %s(%d)", table2, field1.get("code"), field1.get("dbType"), Integer.parseInt(field1.get("length").toString())) + ";");
                }

                // 比较注释是否相同
                if (!Objects.equals(field1.get("name"), field2.get("comment"))) {
                    ddlStatements.add(String.format("ALTER TABLE %s MODIFY %s COMMENT '%s'", table2, field1.get("name"), field1.get("comment")) + ";");
                }
            } else {
                // table2中不存在field1,在table2中添加字段的DDL语句
                if (field1.get("dbType").equals("decimal")) {
                    ddlStatements.add(String.format("ALTER TABLE %s ADD COLUMN %s %s(%d,%d) COMMENT '%s'", table2, field1.get("code"), field1.get("dbType"), Integer.parseInt(field1.get("length").toString()), Integer.parseInt(field1.get("accuracy").toString())));
                } else {
                    ddlStatements.add(
                            String.format("ALTER TABLE %s ADD COLUMN %s %s(%d) COMMENT '%s'", table2, field1.get("code"), field1.get("type"), Integer.parseInt(field1.get("length").toString()), field1.get("comment")) + ";");
                }
            }
        }

        // 处理在table2中存在但在table1中不存在的字段,生成删除字段的DDL语句
        for (Map<String, Object> field2 : table2Fields) {
            if (getFieldByName(1, table1Fields, (String) field2.get("name")) == null) {
                ddlStatements.add(String.format("ALTER TABLE %s DROP COLUMN %s", table2, field2.get("name")) + ";");
            }
        }

        return ddlStatements;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值