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

news2025/3/14 9:49:39

在这里插入图片描述

一、背景

遇到需求:将指定数据库表设计,统一导出到一个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方法运行即可。

三、结果截图:

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

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ACL初级总结

ACL–访问控制列表 1.访问控制 在路由器流量流入或者流出的接口上,匹配流量,然后执行相应动作 permit允许 deny拒绝 2.抓取感兴趣流 3.ACL匹配规则 自上而下逐一匹配,若匹配到了则按照对应规则执行动作,而不再向下继续匹配 思科:ACL列表末尾隐含一条拒绝所有的规则 华为:AC…

调优案例一:堆空间扩容提升吞吐量实战记录

&#x1f4dd; 调优案例一&#xff1a;堆空间扩容提升吞吐量实战记录 &#x1f527; 调优策略&#xff1a;堆空间扩容三部曲 # 原配置&#xff08;30MB堆空间&#xff09; export CATALINA_OPTS"$CATALINA_OPTS -Xms30m -Xmx30m"# 新配置&#xff08;扩容至120MB&am…

C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷一)

目录 1. 内存和地址 2. 指针变量和地址 2.1 取地址操作符&#xff08;&&#xff09; 2.2 指针变量 2.3 解引用操作符 &#xff08;*&#xff09; 3. 指针的解引用 3.1 指针 - 整数 3.2 void* 指针 4. const修饰指针 4.1 const修饰变量 4.2 const修饰指针变量 5…

计算机毕业设计:留守儿童的可视化界面

留守儿童的可视化界面mysql数据库创建语句留守儿童的可视化界面oracle数据库创建语句留守儿童的可视化界面sqlserver数据库创建语句留守儿童的可视化界面springspringMVChibernate框架对象(javaBean,pojo)设计留守儿童的可视化界面springspringMVCmybatis框架对象(javaBean,poj…

golang算法二叉树对称平衡右视图

100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a…

Chatbox通过百炼调用DeepSeek

解决方案链接&#xff1a;评测&#xff5c;零门槛&#xff0c;即刻拥有DeepSeek-R1满血版 方案概览 本方案以 DeepSeek-R1 满血版为例进行演示&#xff0c;通过百炼模型服务进行 DeepSeek 开源模型调用&#xff0c;可以根据实际需求选择其他参数规模的 DeepSeek 模型。百炼平台…

【数据结构】6栈

0 章节 3&#xff0e;1到3&#xff0e;3小节。 认知与理解栈结构&#xff1b; 列举栈的操作特点。 理解并列举栈的应用案例。 重点 栈的特点与实现&#xff1b; 难点 栈的灵活实现与应用 作业或思考题 完成学习测试&#xff12;&#xff0c;&#xff1f; 内容达成以下标准(考核…

PyTorch 入门学习

目录 PyTorch 定义 核心作用 应用场景 Pytorch 基本语法 1. 张量的创建 2. 张量的类型转换 3. 张量数值计算 4. 张量运算函数 5. 张量索引操作 6. 张量形状操作 7. 张量拼接操作 8. 自动微分模块 9. 案例-线性回归案例 PyTorch 定义 PyTorch 是一个基于 Python 深…

mov格式视频如何转换mp4?

mov格式视频如何转换mp4&#xff1f;在日常的视频处理中&#xff0c;经常需要将MOV格式的视频转换为MP4格式&#xff0c;以兼容更多的播放设备和平台。下面给大家分享如何将MOV视频转换为MP4&#xff0c;4款视频格式转换工具分享。 一、牛学长转码大师 牛学长转码大师是一款功…

二进制求和(js实现,LeetCode:67)

这道题我的解决思路是先将a和b的长度保持一致以方便后续按位加减 let lena a.length let lenb b.length if (lena ! lenb) {if (lena > lenb) {for (let i 0; i <lena-lenb; i) {b 0 b}} else {for (let i 0; i < lenb-lena; i) {a 0 a}} } 下一步直接进行按…

【C#】使用DeepSeek帮助评估数据库性能问题,C# 使用定时任务,每隔一分钟移除一次表,再重新创建表,和往新创建的表追加5万多条记录

&#x1f339;欢迎来到《小5讲堂》&#x1f339; &#x1f339;这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。&#x1f339; &#x1f339;温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01;&#…

【openGauss】物理备份恢复

文章目录 1. gs_backup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复&#xff08;3&#xff09;手动恢复的办法 2. gs_basebackup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复① 伪造数据目录丢失② 恢复 3. gs_probackup&#xff08;1&#xf…

蓝桥杯备赛-基础练习 day1

1、闰年判断 问题描述 给定一个年份&#xff0c;判断这一年是不是闰年。 当以下情况之一满足时&#xff0c;这一年是闰年:1.年份是4的倍数而不是100的倍数 2&#xff0e;年份是400的倍数。 其他的年份都不是闰年。 输入格式 输入包含一个…

实验四 Python聚类决策树训练与预测 基于神经网络的MNIST手写体识别

一、实验目的 Python聚类决策树训练与预测&#xff1a; 1、掌握决策树的基本原理并理解监督学习的基本思想。 2、掌握Python实现决策树的方法。 基于神经网络的MNIST手写体识别&#xff1a; 1、学习导入和使用Tensorflow。 2、理解学习神经网络的基本原理。 3、学习使用…

【原创】在高性能服务器上,使用受限用户运行Nginx,充当反向代理服务器[未完待续]

起因 在公共高性能服务器上运行OllamaDeepSeek&#xff0c;如果按照默认配置启动Ollama程序&#xff0c;则自己在远程无法连接你启动的Ollama服务。 如果修改配置&#xff0c;则会遇到你的Ollama被他人完全控制的安全风险。 不过&#xff0c;我们可以使用一个方向代理&#…

Spring boot3-WebClient远程调用非阻塞、响应式HTTP客户端

来吧&#xff0c;会用就行具体理论不讨论 1、首先pom.xml引入webflux依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId> </dependency> 别问为什么因为是响应式....…

18 | 实现简洁架构的 Handler 层

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…

coding ability 展开第三幕(滑动指针——基础篇)超详细!!!!

文章目录 前言滑动窗口长度最小的子数组思路 无重复字符的最长子串思路 最大连续1的个数思路 将x减到0的最小操作数思路 总结 前言 前面我们已经把双指针的一些习题练习的差不多啦 今天我们来学习新的算法知识——滑动窗口 让我们一起来探索滑动窗口的魅力吧 滑动窗口 滑动窗口…

如何自己做奶茶,从此告别奶茶店

自制大白兔奶茶&#xff0c;奶香与茶香激情碰撞&#xff0c;每一口都是香浓与甜蜜的双重诱惑&#xff0c;好喝到跺脚&#xff01;丝滑口感在舌尖舞动&#xff0c;仿佛味蕾在开派对。 简单几步就能复刻&#xff0c;成本超低&#xff0c;轻松在家享受奶茶自由。 材料:大白兔奶糖&…

宇树人形机器人开源模型

1. 下载源码 https://github.com/unitreerobotics/unitree_ros.git2. 启动Gazebo roslaunch h1_description gazebo.launch3. 仿真效果 H1 GO2 B2 Laikago Z1 4. VMware: vmw_ioctl_command error Invalid argument 这个错误通常出现在虚拟机环境中运行需要OpenGL支持的应用…