当前位置: 首页 > news >正文

工具(十二):Java导出MySQL数据库表结构信息到excel

在这里插入图片描述

一、背景

遇到需求:将指定数据库表设计,统一导出到一个Excel中,存档查看。
如果一个一个弄,很复杂,耗时长。

二、写一个工具导出下

废话少絮,上码:

2.1 pom导入

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>5.4.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>5.4.0</version>
		</dependency>

2.2 工具类

这里提供思路和示例

package com.eduer.books.modules.app.controller;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;
import java.sql.*;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * Java导出mysql数据库表结构信息到excel
 * @author wangdy
 * 2025/3/13
 */
public class DatabaseExporter {

    private static final Pattern INVALID_SHEETNAME_CHARS = Pattern.compile("[\\\\/*?\\[\\]:]");
    private static final int MAX_SHEETNAME_LENGTH = 31;

    public static void exportToExcel(String dbName, String jdbcUrl, String username, String password, String outputPath)
            throws Exception {
        Set<String> usedSheetNames = new HashSet<>();

        try (Workbook workbook = new XSSFWorkbook(); Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
            DatabaseMetaData metaData = conn.getMetaData();

            ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});

            while (tables.next()) {
                String catalog = tables.getString("TABLE_CAT");
                String schema = tables.getString("TABLE_SCHEM");
                String tableName = tables.getString("TABLE_NAME");

                // 生成合法的Sheet名称
                String baseSheetName = generateBaseSheetName(catalog, schema, tableName);
                String uniqueSheetName = generateUniqueSheetName(baseSheetName, usedSheetNames);

                Sheet sheet = workbook.createSheet(uniqueSheetName);
                usedSheetNames.add(uniqueSheetName);

                createHeaderRow(sheet);
                processTableColumns(metaData, tableName, sheet);
                autoSizeColumns(sheet, 7);
            }

            try (FileOutputStream outputStream = new FileOutputStream(outputPath)) {
                workbook.write(outputStream);
            }
        }
    }

    private static String generateBaseSheetName(String catalog, String schema, String tableName) {
        // 优先使用schema信息,MySQL中一般用catalog表示数据库
        String prefix = "";
        if (schema != null && !schema.isEmpty()) {
            prefix = schema;
        } else if (catalog != null && !catalog.isEmpty()) {
            prefix = catalog;
        }

        String rawName = prefix.isEmpty()
                ? tableName
                : prefix + "_" + tableName;

        // 替换非法字符并格式化
        return formatSheetName(rawName);
    }

    private static String formatSheetName(String rawName) {
        // 1. 替换非法字符
        String sanitized = INVALID_SHEETNAME_CHARS.matcher(rawName).replaceAll("_");
        // 2. 去除首尾特殊字符
        sanitized = sanitized.replaceAll("^[\\s']+", "").replaceAll("[\\s']+$", "");
        // 3. 压缩连续下划线
        sanitized = sanitized.replaceAll("_{2,}", "_");
        // 4. 截断长度
        return sanitized.length() > MAX_SHEETNAME_LENGTH
                ? sanitized.substring(0, MAX_SHEETNAME_LENGTH)
                : sanitized;
    }

    private static String generateUniqueSheetName(String baseName, Set<String> usedNames) {
        if (!usedNames.contains(baseName)) {
            return baseName;
        }

        int suffix = 1;
        String candidateName;
        do {
            String suffixStr = "_" + suffix++;
            int maxBaseLength = MAX_SHEETNAME_LENGTH - suffixStr.length();
            candidateName = (baseName.length() > maxBaseLength
                    ? baseName.substring(0, maxBaseLength)
                    : baseName) + suffixStr;
        } while (usedNames.contains(candidateName));

        return candidateName;
    }

    private static void processTableColumns(DatabaseMetaData metaData, String tableName, Sheet sheet)
            throws SQLException {
        ResultSet columns = metaData.getColumns(null, null, tableName, null);
        Set<String> primaryKeys = getPrimaryKeys(metaData, tableName);

        int rowNum = 1;
        while (columns.next()) {
            Row row = sheet.createRow(rowNum++);
            fillRowData(columns, primaryKeys, row);
        }
        columns.close();
    }

    // 以下方法保持不变(createHeaderRow, createHeaderStyle, getPrimaryKeys, fillRowData, autoSizeColumns)

    private static void createHeaderRow(Sheet sheet) {
        Row headerRow = sheet.createRow(0);
        String[] headers = {"字段名称", "字段类型", "长度", "是否主键", "允许空值", "默认值", "字段注释"};

        CellStyle headerStyle = createHeaderStyle(sheet.getWorkbook());

        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }
    }

    private static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    private static Set<String> getPrimaryKeys(DatabaseMetaData metaData, String tableName) throws SQLException {
        Set<String> primaryKeys = new HashSet<>();
        ResultSet pkResultSet = metaData.getPrimaryKeys(null, null, tableName);

        while (pkResultSet.next()) {
            primaryKeys.add(pkResultSet.getString("COLUMN_NAME"));
        }
        pkResultSet.close();
        return primaryKeys;
    }

    private static void fillRowData(ResultSet columns, Set<String> primaryKeys, Row row) throws SQLException {
        String columnName = columns.getString("COLUMN_NAME");
        String typeName = columns.getString("TYPE_NAME");
        int columnSize = columns.getInt("COLUMN_SIZE");
        String isNullable = columns.getString("IS_NULLABLE");
        String defaultValue = columns.getString("COLUMN_DEF");
        String remarks = columns.getString("REMARKS");

        row.createCell(0).setCellValue(columnName);
        row.createCell(1).setCellValue(typeName);
        row.createCell(2).setCellValue(columnSize);
        row.createCell(3).setCellValue(primaryKeys.contains(columnName) ? "是" : "否");
        row.createCell(4).setCellValue("YES".equalsIgnoreCase(isNullable) ? "是" : "否");
        row.createCell(5).setCellValue(defaultValue != null ? defaultValue : "");
        row.createCell(6).setCellValue(remarks != null ? remarks : "");
    }

    private static void autoSizeColumns(Sheet sheet, int columnCount) {
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);
        }
    }

    public static void main(String[] args) {
        try {
            String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/books-service?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
            String username = "root";
            String password = "xxxxxxx";
            String outputPath = "数据库表结构.xlsx";

            exportToExcel("books-service", jdbcUrl, username, password, outputPath);
            System.out.println("导出成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

main方法运行即可。

三、结果截图

在这里插入图片描述
在这里插入图片描述

四、扩展:导出到同一个sheet页

以上呢是将每个表导出到每个sheet页,有时候的需求是导出到同一个sheet页。可以用如下代码进行:

4.1 导出数据库表结构到excel中的同一个sheet页,并合并表名和表注释

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.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * Java导出mysql数据库表结构信息到excel中的同一个sheet页,并合并表名和表注释
 * @author wangdy
 * 2025/3/14
 */
public class DatabaseExporterToOneSheet {

    private static final Pattern INVALID_SHEETNAME_CHARS = Pattern.compile("[\\\\/*?\\[\\]:]");
    private static final int MAX_SHEETNAME_LENGTH = 31;

    public static void exportToExcel(String dbName, String jdbcUrl, String username, String password, String outputPath)
            throws Exception {
        Connection conn = null;
        Workbook workbook = new XSSFWorkbook();

        Sheet sheet = workbook.createSheet(dbName);
        try {
            conn = DriverManager.getConnection(jdbcUrl, username, password);
            DatabaseMetaData metaData = conn.getMetaData();

            ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
            createHeaderRow(sheet);
            int rowNum = 1;
            int startRow = 1;
            int endRow = 1;
            while (tables.next()) {
                // String catalog = tables.getString("TABLE_CAT");
                // String schema = tables.getString("TABLE_SCHEM");
                String tableName = tables.getString("TABLE_NAME");
                String tableRemarks = tables.getString("REMARKS");

                ResultSet columns = metaData.getColumns(null, null, tableName, null);
                Set<String> primaryKeys = getPrimaryKeys(metaData, tableName);
                startRow = rowNum;
                while (columns.next()) {
                    Row row = sheet.createRow(rowNum++);
                    fillRowData(tableName, tableRemarks, columns, primaryKeys, row);
                }
                endRow = rowNum - 1;
                // 合并该表表名和表注释:new CellRangeAddress(0, 0, 0, 3)表示合并从第0行第0列到第0行第3列的区域。
                sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, 0, 0));
                sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, 1, 1));
                columns.close();
            }
            autoSizeColumns(sheet, 9);

            try (FileOutputStream outputStream = new FileOutputStream(outputPath)) {
                workbook.write(outputStream);
            }
        } finally {
            if (conn != null) {
                conn.close();
            }
            workbook.close();
        }
    }

    private static String generateBaseSheetName(String catalog, String schema, String tableName) {
        // 优先使用schema信息,MySQL中一般用catalog表示数据库
        String prefix = "";
        if (schema != null && !schema.isEmpty()) {
            prefix = schema;
        } else if (catalog != null && !catalog.isEmpty()) {
            prefix = catalog;
        }

        String rawName = prefix.isEmpty()
                ? tableName
                : prefix + "_" + tableName;

        // 替换非法字符并格式化
        return formatSheetName(rawName);
    }

    private static String formatSheetName(String rawName) {
        // 1. 替换非法字符
        String sanitized = INVALID_SHEETNAME_CHARS.matcher(rawName).replaceAll("_");
        // 2. 去除首尾特殊字符
        sanitized = sanitized.replaceAll("^[\\s']+", "").replaceAll("[\\s']+$", "");
        // 3. 压缩连续下划线
        sanitized = sanitized.replaceAll("_{2,}", "_");
        // 4. 截断长度
        return sanitized.length() > MAX_SHEETNAME_LENGTH
                ? sanitized.substring(0, MAX_SHEETNAME_LENGTH)
                : sanitized;
    }

    private static void createHeaderRow(Sheet sheet) {
        Row headerRow = sheet.createRow(0);
        String[] headers = {"表名称", "表注释", "字段名称", "字段类型", "长度", "是否主键", "允许空值", "默认值", "字段注释"};

        CellStyle headerStyle = createHeaderStyle(sheet.getWorkbook());

        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }
    }

    private static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    private static Set<String> getPrimaryKeys(DatabaseMetaData metaData, String tableName) throws SQLException {
        Set<String> primaryKeys = new HashSet<>();
        ResultSet pkResultSet = metaData.getPrimaryKeys(null, null, tableName);

        while (pkResultSet.next()) {
            primaryKeys.add(pkResultSet.getString("COLUMN_NAME"));
        }
        pkResultSet.close();
        return primaryKeys;
    }

    private static void fillRowData(String tableName, String tableRemarks, ResultSet columns, Set<String> primaryKeys, Row row) throws SQLException {
        String columnName = columns.getString("COLUMN_NAME");
        String typeName = columns.getString("TYPE_NAME");
        int columnSize = columns.getInt("COLUMN_SIZE");
        String isNullable = columns.getString("IS_NULLABLE");
        String defaultValue = columns.getString("COLUMN_DEF");
        String remarks = columns.getString("REMARKS");

        row.createCell(0).setCellValue(tableName);
        row.createCell(1).setCellValue(tableRemarks);
        row.createCell(2).setCellValue(columnName);
        row.createCell(3).setCellValue(typeName);
        row.createCell(4).setCellValue(columnSize);
        row.createCell(5).setCellValue(primaryKeys.contains(columnName) ? "是" : "否");
        row.createCell(6).setCellValue("YES".equalsIgnoreCase(isNullable) ? "是" : "否");
        row.createCell(7).setCellValue(defaultValue != null ? defaultValue : "");
        row.createCell(8).setCellValue(remarks != null ? remarks : "");
    }

    private static void autoSizeColumns(Sheet sheet, int columnCount) {
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i, true);
        }
    }

    public static void main(String[] args) {
        try {
            String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/books-service?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
            String username = "root";
            String password = "xxxxxx";
            String outputPath = "数据库表结构.xlsx";

            exportToExcel("books-service", jdbcUrl, username, password, outputPath);
            System.out.println("导出成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.2 结果

结果如下图所示,非常Nice。
在这里插入图片描述

END

http://www.dtcms.com/a/67436.html

相关文章:

  • 使用DeepSeek完成一个简单嵌入式开发
  • Apache Tomcat漏洞,对其进行升级
  • 前端及后端实现csv文件下载功能
  • 鸿蒙Next开发与实战经验总结
  • MicroPython 智能硬件开发完整指南
  • 《网络应用监测:Netinside的助力企业网络运维》
  • 使用Beanshell前置处理器对Jmeter的请求body进行加密
  • Ubuntu20.04安装运行DynaSLAM
  • cocos creator 3.8如何在代码中打印drawcall,fps
  • 交换机HSRP与VRRP的全面深入对比
  • 在 Windows 系统下使用 VMware 安装 Ubuntu 24.04 LTS 系统
  • 【设计模式】建造者模式——工厂模式
  • 文件解析漏洞靶场解析全集详解
  • 一次ORACLE 10G数据库REDO LOG损坏报错的解决办法ORA-00354: corrupt redo log block header
  • 纳米压印技术制备AR眼镜的参考步骤
  • React-dnd 拖拽排序,指定拖拽触发节点,拖拽预览图
  • Python开发合并多个PDF文件
  • 后端 - java - - 重写与重载的区别
  • Chatbox通过百炼调用DeepSeek
  • 医疗APP开发如何实现跨机构数据互通
  • 【HeadFirst系列之HeadFirstJava】第17天之深入解析 Java 包与 JAR:从代码组织到应用发布全流程(含实战)
  • FX-std::vector排序
  • SQLite Truncate Table
  • Selenium Manager和webdriver manager的区别与联系
  • chebykan阅读收尾
  • 数组逆序重存放(信息学奥赛一本通-1105)
  • 版本控制器Git(5)
  • 问deepseek: OpenFOAM并行分区后,是如何实现ldumatrix矩阵向量乘法计算逻辑的?
  • 05延迟任务精准发布文章(redis实现延迟任务、分布式锁)
  • 学习文章:Spring Boot 中如何使用 `@Async` 实现异步处理