表做对比
- 字段类型
- 字段长度
- 注释
可以自定义扩展对比内容
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;
}
}