首页 > 数据库 >年轻人的第一个车轱辘——比较两个数据库中表结构的不同并导出为Excel文件

年轻人的第一个车轱辘——比较两个数据库中表结构的不同并导出为Excel文件

时间:2023-10-16 14:23:17浏览次数:35  
标签:Collectors String stream Excel 数据库 List private 车轱辘 table

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

相关文章

  • mongo_db数据库
    数据库数据服务器下,每次创建一个数据库。userdatabase_name;("没有就会创建---有的话就会切换")并且每个数据库下面又单独的用户。 1--:创建用户:先use到指定的数据库。db.createUser({user:"ems",pwd:"Rxd123456!",//orcleartextpasswordroles:[{......
  • electron 本地数据库sqlite
    背景某些不会频繁变动,但是数据量可能比较大,查询也比较频繁地数据,例如通讯录等。如果每次都查询服务器的数据库可能造成服务器压力过大,考虑在本地做数据库存储,有更新时从服务器同步数据到本地数据库,用户操作查询则使用本地数据库查询。方案进入页面时(或者其他适当时机)请求服务......
  • 常用JDBC数据库驱动包和类名
    MySQL数据库:  1)驱动包:https://mvnrepository.com/artifact/mysql/mysql-connector-java(下载路径)  2)驱动类名:com.mysql.jdbc.Driver  3)JDBC的URL:jdbc:mysql://IP地址:端口号/数据库名字   注:端口号缺省为:3306    SQLserver数据库:  1)驱动包:https......
  • 高效解决方案:Java轻松批量查找与替换Excel文本
    摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。前言在Excel中,我们经常会使用快捷键Ctrl+F和Ctrl+H对查找的内容进行替换。但是这种情况对于单个的Excel还是很适合的,那如果遇到Excel......
  • WINCCV7.5SP2无法安装SQL数据库的解决
    这一篇学习笔记我在新浪博客记录过,地址是WINCCV7.5SP2无法安装SQL数据库的解决_来自金沙江的小鱼_新浪博客(sina.com.cn)在这里我再记录一遍。今天在办公室一台纯净安装的windows10电脑上安装wincc7.5sp2过程中,SQL安装阶段提醒无法安装,然后整个安装就结束了。WINCC7.5SP2安装......
  • SQL Server数据库多种方式查找重复记录
    示例:表stuinfo,有三个字段recno(自增),stuid,stuname 建该表的Sql语句如下: CREATETABLE[StuInfo]([recno][int]IDENTITY(1,1)NOTNULL,[stuid][varchar](10)COLLATEChinese_PRC_CI_ASNOTNULL,[stuname][varchar](10)COLLATEChinese_PRC_CI_ASNOTNULL)ON[PRIMAR......
  • 创建数据库及工作表
    1.创建数据库:  createdatabasedatabase数据库名;示例:创建课程学习数据库SCTcreatedatabaseSCT;2.创建Table:Createtable表名(列名数据类型[PrimarykeyPrimarykey|Unique[Notnull]);Primarykey:主键约束。每个表只能创建一个主键约......
  • 了解 MySQL 数据库的三大日志(redo log、undo log、binary log)
    前言MySQL中有以下几种日志,包括:redolog(重做日志)undolog(回滚日志)binarylog(二进制日志)errorlog(错误日志)slowquerylog(慢查询日志)generallog(一般查询日志)relaylog(中继日志)事务的特性:原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子......
  • Ubuntu18.04下使用PHP7.2的连接sql server数据库
     一、简介SQLServer是由Microsoft开发和推广的关系数据库管理系统(DBMS),它最初是由Microsoft、Sybase和Ashton-Tate三家公司共同开发的,并于1988年推出了第一个OS/2版本。MicrosoftSQLServer近年来不断更新版本,1996年,Microsoft推出了SQLServer6.5版本;1998年,SQLServer7.0版本......
  • 【精品】使用druid 获取数据库表的信息
    Maven依赖<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifac......