package com.example.demo;
import lombok.Data;
import org.apache.commons.collections4.ListUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 比较两个数据库中表结构的不同,并导出为excel(以链接一为标准)
*/
public class DatabaseInfo {
private static final short SEA_GREEN = IndexedColors.SEA_GREEN.getIndex();
private static final short LIGHT_GREEN = IndexedColors.LIGHT_GREEN.getIndex();
private static final short GOLD = IndexedColors.GOLD.getIndex();
public static void main(String[] args) {
String jdbcUrl1 = "jdbcUrl1 ";
String username1 = "username1 ";
String password1 = "password1 ";
String dataSourceName1 = "dataSourceName1 ";
String jdbcUrl2 = "jdbcUrl2 ";
String username2 = "username2 ";
String password2 = "password2 ";
String dataSourceName2 = "dataSourceName2 ";
String excelPath = "tables_diff.xlsx";
getDiffExcel(jdbcUrl1, username1, password1, dataSourceName1, jdbcUrl2, username2, password2, dataSourceName2, excelPath);
}
private static void getDiffExcel(String jdbcUrl1, String username1, String password1, String dataSourceName1, String jdbcUrl2, String username2, String password2, String dataSourceName2, String excelPath) {
List<TableEntity> dev = getTableStructure(jdbcUrl1, username1, password1, dataSourceName1);
List<TableEntity> test = getTableStructure(jdbcUrl2, username2, password2, dataSourceName2);
List<TableEntity> diff = getDifferentTables(dev, test);
toExcel(diff, excelPath);
}
/**
* 将表结构集合转成excel
* @param tableList 表结构集合
* @param excelPath 文件路径
*/
private static void toExcel(List<TableEntity> tableList, String excelPath) {
Workbook workbook = new XSSFWorkbook();
CellStyle secondStyle = createCellStyle(workbook, GOLD);
for (TableEntity table : tableList) {
if (table.getStatus() == 0) continue;
CellStyle headerStyle = createCellStyle(workbook, SEA_GREEN);
String tableName = table.getTableName();
if (tableName.matches(".*_[0-9]+")) continue;
String tableComment = table.getRemarks();
Sheet sheet = workbook.createSheet(tableName);
//设置列宽
sheet.setColumnWidth(0, 30 * 256);
sheet.setColumnWidth(1, 50 * 256);
sheet.setColumnWidth(2, 30 * 256);
sheet.setColumnWidth(3, 10 * 256);
sheet.setColumnWidth(4, 10 * 256);
sheet.setColumnWidth(5, 10 * 256);
sheet.setColumnWidth(6, 10 * 256);
// Create header row
Row headerRow = sheet.createRow(0);
// 设置第一行的样式和名称
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 7));
Cell mergedCell = headerRow.createCell(0);
String title = tableName + "(" + tableComment + ")";
if (table.getStatus() == 2) {
setDeleteStyle(headerStyle, workbook);
title = title + "(已删除)";
} else if (table.getStatus() == 1) {
title = title + "(新增)";
}
mergedCell.setCellValue(title);
mergedCell.setCellStyle(headerStyle);
// 设置第二行的样式和每列的名称
Row fieldRow = sheet.createRow(1);
createCell(fieldRow, 0, "英文", secondStyle);
createCell(fieldRow, 1, "中文", secondStyle);
createCell(fieldRow, 2, "类型", secondStyle);
createCell(fieldRow, 3, "主键否", secondStyle);
createCell(fieldRow, 4, "能否为空", secondStyle);
createCell(fieldRow, 5, "自增", secondStyle);
createCell(fieldRow, 6, "索引", secondStyle);
createCell(fieldRow, 7, "存在修改", secondStyle);
int rowNumber = 2;
for (ColumnEntity column : table.getColumns()) {
CellStyle dataStyle = createCellStyle(workbook, (short) 1);
String fieldName = column.getColumnName();
String fieldComment = column.getColumnComment();
String fieldType = column.getColumnType();
String isPrimaryKey = column.isPrimaryKey() ? "主键" : "n";
String isNullable = column.isNullable() ? "y" : "不能为空";
String isAutoIncrement = column.isAutoIncrement() ? "自增" : "n";
String indexName = column.isIndex() ? "有索引" : "";
int status = column.getStatus();
String diff = "";
if (status == 1) {
dataStyle = createCellStyle(workbook, LIGHT_GREEN);
diff = "新增字段";
}
if (status == 2) {
setDeleteStyle(dataStyle, workbook);
diff = "删除字段";
}
if (status == 3) {
dataStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
diff = "类型修改";
}
Row row = sheet.createRow(rowNumber++);
createCell( row, 0, fieldName, dataStyle);
createCell( row, 1, fieldComment, dataStyle);
createCell( row, 2, fieldType, dataStyle);
createCell( row, 3, isPrimaryKey, dataStyle);
createCell( row, 4, isNullable, dataStyle);
createCell( row, 5, isAutoIncrement, dataStyle);
createCell( row, 6, indexName, dataStyle);
createCell( row, 7, diff, dataStyle);
}
try (FileOutputStream fileOut = new FileOutputStream(excelPath)) {
workbook.write(fileOut);
} catch (Exception e) {
System.out.println("e = " + e);
}
}
System.out.println("Excel file generated successfully.");
}
/**
* 创建单元格
* @param fieldRow 行对象
* @param row 列
* @param value 值
* @param style 风格
*/
private static void createCell(Row fieldRow, int row, String value, CellStyle style) {
Cell cell7 = fieldRow.createCell(row);
cell7.setCellStyle(style);
cell7.setCellValue(value);
}
/**
* 设置单元格风格未删除(颜色为灰色,加删除线)
* @param cellStyle
* @param workbook
*/
private static void setDeleteStyle(CellStyle cellStyle, Workbook workbook) {
Font font = workbook.createFont();
font.setStrikeout(true);
cellStyle.setFont(font);
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
}
/**
* 获取存在不同的表的表结构
* @param master 标准表
* @param slave 对比表
* @return 存在不同的表的表结构
*/
private static List<TableEntity> getDifferentTables(List<TableEntity> master, List<TableEntity> slave) {
//获取新增或删除的表的表结构
List<TableEntity> tableDiff = getTableDiff(master, slave);
//获取新增或删除字段或字段类型存在不同的表的表结构
List<TableEntity> columnDiff = getColumnDiff(master, slave);
return ListUtils.union(tableDiff, columnDiff);
}
/**
* 获取新增或删除字段或字段类型存在不同的表的表结构
* @param master 标准表
* @param slave 对比表
* @return 新增或删除字段或字段类型存在不同的表的表结构
*/
private static List<TableEntity> getColumnDiff(List<TableEntity> master, List<TableEntity> slave) {
//在两个库中都存在的表,获取其在主库中的表结构
List<TableEntity> masterDiff = master.stream().filter(e -> {
List<String> tableNames = slave.stream().map(TableEntity::getTableName).collect(Collectors.toList());
return tableNames.contains(e.getTableName());
}).collect(Collectors.toList());
//在两个库中都存在的表,获取其在从库中的表结构
List<TableEntity> slaveDiff = slave.stream().filter(e -> {
List<String> tableNames = master.stream().map(TableEntity::getTableName).collect(Collectors.toList());
return tableNames.contains(e.getTableName());
}).collect(Collectors.toList());
for (TableEntity table : masterDiff) {
//主库中的表字段结构集合
List<ColumnEntity> masterColumns = table.getColumns();
//从库中的表字段结构集合
List<ColumnEntity> slaveColumns = slaveDiff.stream().filter(e -> e.getTableName().equals(table.getTableName())).collect(Collectors.toList()).get(0).getColumns();
//被删除的字段集合
List<ColumnEntity> deleteColumns = slaveColumns.stream().filter(e -> {
List<String> names = masterColumns.stream().map(ColumnEntity::getColumnName).collect(Collectors.toList());
return !names.contains(e.getColumnName());
}).collect(Collectors.toList());
deleteColumns.forEach(e -> e.setStatus(2));
//新增的字段集合
List<ColumnEntity> addColumns = masterColumns.stream().filter(e -> {
List<String> names = slaveColumns.stream().map(ColumnEntity::getColumnName).collect(Collectors.toList());
return !names.contains(e.getColumnName());
}).collect(Collectors.toList());
addColumns.forEach(e -> e.setStatus(1));
//类型被修改的字段集合
List<ColumnEntity> diffColumn = masterColumns.stream().filter(e -> {
List<String> names = slaveColumns.stream().map(ColumnEntity::getColumnName).collect(Collectors.toList());
return names.contains(e.getColumnName());
}).filter(e -> {
String slaveColumnType = slaveColumns.stream().filter(f -> f.getColumnName().equals(e.getColumnName())).collect(Collectors.toList()).get(0).getColumnType();
return !e.getColumnType().equals(slaveColumnType);
}).collect(Collectors.toList());
diffColumn.forEach(e -> {
e.setStatus(3);
String slaveColumnType = slaveColumns.stream().filter(f -> f.getColumnName().equals(e.getColumnName())).collect(Collectors.toList()).get(0).getColumnType();
e.setColumnType(slaveColumnType + " -> " + e.getColumnType());
});
//无修改的字段集合
List<ColumnEntity> noDiff = masterColumns.stream().filter(e -> {
List<String> names = slaveColumns.stream().map(ColumnEntity::getColumnName).collect(Collectors.toList());
return names.contains(e.getColumnName());
}).filter(e -> {
String slaveColumnType = slaveColumns.stream().filter(f -> f.getColumnName().equals(e.getColumnName())).collect(Collectors.toList()).get(0).getColumnType();
return e.getColumnType().equals(slaveColumnType);
}).collect(Collectors.toList());
//若无修改的字段集合长度与主表字段集合长度不同,则该表存在修改
if (noDiff.size() != masterColumns.size()) {
table.setStatus(3);
}
//合并新增字段集合、删除字段集合、修改字段集合和无修改字段集合,为表的字段结构集合
List<ColumnEntity> column = Stream.of(addColumns, deleteColumns, diffColumn, noDiff).flatMap(Collection::stream).collect(Collectors.toList());
table.setColumns(column);
}
return masterDiff;
}
/**
* 获取新增或删除的表的表结构
* @param master 标准表
* @param slave 对比表
* @return 新增或删除的表的表结构
*/
private static List<TableEntity> getTableDiff(List<TableEntity> master, List<TableEntity> slave) {
//已删除的表的表结构集合
List<TableEntity> delete = slave.stream().filter(e -> {
List<String> masterNames = master.stream().map(TableEntity::getTableName).collect(Collectors.toList());
return !masterNames.contains(e.getTableName());
}).collect(Collectors.toList());
//状态为2:已删除
delete.forEach(e -> e.setStatus(2));
//新增表的表结构集合
List<TableEntity> add = master.stream().filter(e -> {
List<String> slaveNames = slave.stream().map(TableEntity::getTableName).collect(Collectors.toList());
return !slaveNames.contains(e.getTableName());
}).collect(Collectors.toList());
//表状态为1:新增
add.forEach(e -> e.setStatus(1));
//合并两个集合
return ListUtils.union(delete, add);
}
/**
* 获取数据库中所有表的表结构
* @param jdbcUrl url
* @param username 用户名
* @param password 密码
* @param dataSourceName 数据库名
* @return 表结构
*/
private static List<TableEntity> getTableStructure(String jdbcUrl, String username, String password, String dataSourceName) {
List<TableEntity> tableList = new ArrayList<>();
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
String query = "SELECT table_name, table_comment FROM information_schema.tables WHERE table_schema = '" + dataSourceName + "'";
Statement stmt = connection.createStatement();
ResultSet tables = stmt.executeQuery(query);
while (tables.next()) {
TableEntity table = new TableEntity();
String tableName = tables.getString("table_name");
String tableComment = tables.getString("table_comment");
table.setTableName(tableName);
table.setRemarks(tableComment);
String structureQuery = "SHOW FULL COLUMNS FROM " + tableName;
List<ColumnEntity> columnList = new ArrayList<>();
try (Statement structureStmt = connection.createStatement(); ResultSet columns = structureStmt.executeQuery(structureQuery)) {
while (columns.next()) {
//字段名
String columnName = columns.getString("Field");
//字段类型
String columnType = columns.getString("Type");
//字段注释
String columnComment = columns.getString("Comment");
//是否主键
boolean isPrimaryKey = columns.getString("Key").equals("PRI");
//能否为空
boolean isNullable = columns.getString("Null").equals("YES");
//是否自增
boolean isAutoIncrement = columns.getString("Extra").equals("auto_increment");
//是否有索引
boolean isIndex = columns.getString("Key").equals("MUL");
ColumnEntity column = new ColumnEntity();
column.setColumnName(columnName);
column.setColumnType(columnType);
column.setColumnComment(columnComment);
column.setPrimaryKey(isPrimaryKey);
column.setNullable(isNullable);
column.setAutoIncrement(isAutoIncrement);
column.setIndex(isIndex);
columnList.add(column);
}
}
table.setColumns(columnList);
tableList.add(table);
}
tables.close();
return tableList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private static CellStyle createCellStyle(Workbook workbook, short colorIndex) {
CellStyle style = workbook.createCellStyle();
if (colorIndex != 1) {
style.setFillForegroundColor(colorIndex);
}
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setAlignment(HorizontalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
}
@Data
class TableEntity {
private String tableName;
private String remarks;
List<ColumnEntity> columns;
/**
* 1:新增;2:删除,3:修改
*/
private int status = 0;
}
@Data
class ColumnEntity {
private String columnName;
private String columnType;
private String columnComment;
private boolean isPrimaryKey = false;
private boolean isNullable = true;
private boolean isAutoIncrement = false;
private boolean isIndex = false;
/**
* 1:新增;2:删除,3:修改
*/
private int status = 0;
}
标签:Collectors,String,stream,Excel,数据库,List,private,车轱辘,table
From: https://www.cnblogs.com/guardian0769/p/17767242.html