工具(十二):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.mzph.cn/news/898199.shtml

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

相关文章

Postman 新手入门指南:从零开始掌握 API 测试

Postman 新手入门指南&#xff1a;从零开始掌握 API 测试 一、Postman 是什么&#xff1f; Postman 是一款功能强大的 API 开发与测试工具&#xff0c;支持 HTTP 请求调试、自动化测试、团队协作等功能。无论是开发人员还是测试工程师&#xff0c;都可以用它快速验证接口的正确…

运维工具推荐 -- 宝塔面板:一键部署服务器

标题&#xff1a;宝塔面板&#xff1a;一键部署服务器&#xff0c;轻松管理你的云端世界 引言 在数字化时代&#xff0c;服务器管理对于个人开发者、中小企业或站长来说既是机遇也是挑战。手动配置服务器环境耗时费力&#xff0c;而 宝塔面板 作为一款 免费开源、功能全面 的服…

【软件工程】03_软件需求分析

3.1 系统分析 1. 系统分析概述 系统分析是一组统称为计算机系统工程的活动。它着眼于所有的系统元素,而非仅仅局限于软件。系统分析主要探索软件项目的目标、市场预期、主要的技术指标等,其目的在于帮助决策者做出是否进行软件项目立项的决定。 2. 可行性分析(Feasibility …

WD5202L超低成本 Buck 电源芯片的特性与应用电路解析, 将市电转换为 5V 电压

WD5202L&#xff1a;超低成本 Buck 电源芯片的特性与应用电路解析 在现代电子设备的小型化、低成本化趋势下&#xff0c;对电源管理芯片的性能、成本和尺寸提出了严苛要求。WD5202L 作为一款超低成本的 Buck 电源芯片&#xff0c;凭借其独特的特性&#xff0c;在众多应用场景中…

UART转AHB模块ModelSim仿真

一、简介 UART转AHB模块用于实现一种简单的通过上位机控制FPGA内部寄存器的方式。上位机通过串口助手发送读写寄存器的指令&#xff0c;UART转AHB模块接收指令后解析出地址&#xff0c;命令&#xff0c;数据信息&#xff0c;然后转成AHB总线格式输出。这时UART转AHB模块相当于A…

Qt5.15.2实现Qt for WebAssembly与示例

目录 1.什么是Qt for WebAssembly&#xff1f; 1.1 什么是 WebAssembly&#xff1f; 1.2 WebAssembly 的优势 1.3 什么是 Qt for WebAssembly&#xff1f; 1.4 Qt for WebAssembly 的特点 1.5 编译过程 1.6 运行时环境 注意&#xff01;&#xff01;&#xff01;注意&am…

AGI大模型(8):提示词的安全与防护

1 前言 著名的「奶奶漏洞」&#xff0c;⽤套路把 AI 绕懵。 2 常⻅的提示词攻击技术 2.1 同类型⽬标劫持 同类⽬标劫持攻击&#xff0c;特别是在同类型任务的背景下&#xff0c;涉及到攻击者通过⾮法⼿段控制模型&#xff0c;并迫使其执行与原始任务性质相同但⽬标不同的操作…

使用redis客户端中对于json数据格式的存储和读取

代码背景&#xff1a; 现在有一个json格式的数据&#xff0c;但是由于redis客户端上面没办法直接创建/导入json的数据格式。 故考虑现在redis客户端上先存储一个名为"old_order"的string类型的的源数据。 思路&#xff1a; 由于直接使用redisTemplate获取自动导入…

专题三搜索插入位置

1.题目 题目分析&#xff1a; 给一个目标值&#xff0c;然后要在排序的整数数组中&#xff0c;找到跟目标值一样的&#xff0c;如果没有就把这个值插入进去&#xff0c;然后返回插入后的下标。 2.算法原理 根据题目的时间复杂度可以知道要用二分&#xff0c;开始划分区域&…

Redis监控:从睁眼瞎到千里眼的进化史

各位在Redis迷雾中摸黑的探险家们&#xff01;今天我们要给Redis装上"天眼系统"——从连自己内存爆了都不知道的睁眼瞎&#xff0c;进化到连每秒哪个键被摸了几次都门儿清的监控狂魔&#xff01;准备好迎接《Redisの楚门世界》了吗&#xff1f;&#x1f441;️ 第一幕…

双缓冲机制(含原理、优势、实现方式、应用场景)

双缓冲机制 一、双缓冲机制的原理二、双缓冲的典型应用场景三、双缓冲的优势四、双缓冲的实现方式1. 硬件级双缓冲2. 软件级双缓冲3. 性能提升对比 五、双缓冲的挑战与解决方案六、总结 双缓冲机制是一种通过使用两个缓冲区&#xff08;Buffer A 和 Buffer B&#xff09;来优化…

Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解

目录 一、 进程创建 fork函数 二、进程的终止&#xff1a; 1. 想明白&#xff1a;终止是在做什么&#xff1f; 2.进程终止的3种情况&#xff1f; a.退出码是什么&#xff1f;存在原因&#xff1f;为什么int main&#xff08;&#xff09;return 0? b.第三种进程终止的情况…

深入了解Linux —— git三板斧

版本控制器git 为了我们方便管理不同版本的文件&#xff0c;就有了版本控制器&#xff1b; 所谓的版本控制器&#xff0c;就是能够了解到一个文件的历史记录&#xff08;修改记录&#xff09;&#xff1b;简单来说就是记录每一次的改动和版本迭代的一个管理系统&#xff0c;同…

STM32---FreeRTOS事件标志组

一、简介 事件标志位&#xff1a;用一个位&#xff0c;来表示事件是否发生 事件标志组&#xff1a;一组事件标志位的集合&#xff0c;可以简单的理解时间标志组&#xff0c;就是一个整体。 事件标志租的特点&#xff1a; 它的每一个位表示一个时间&#xff08;高8位不算&…

在centOS Linux系统搭建自动化构建工具Jenkins

前言 在工作中发现公司使用Jenkins实现自动化部署项目方案&#xff0c;于是闲着自己也捣鼓一下&#xff0c;网上查阅相关部署资料&#xff0c;顺便记录操作步骤&#xff0c;所以有了下面这篇的文章。 部署完之后&#xff0c;安装前端项目所需环境&#xff0c;比如node环境&am…

Git下载安装(保姆教程)

目录 1、Git下载 2、Git安装&#xff08;windows版&#xff09; &#xff08;1&#xff09;启动安装程序 &#xff08;2&#xff09;阅读许可协议 &#xff08;3&#xff09;选择安装路径 &#xff08;4&#xff09;选择组件 &#xff08;5&#xff09;选择开始菜单文件夹…

深入理解嵌入式开发中的三个重要工具:零长度数组、container_of 和 typeof

在嵌入式开发中,内核开发者经常需要处理复杂的数据结构和动态内存分配。零长度数组、container_of 宏和 typeof 是内核开发中三个非常重要的工具,它们在结构体管理、内存操作和类型处理中发挥着关键作用。本文将详细探讨这三个工具的功能、应用场景及其在内核开发中的重要性。…

【react】react中的<></>和React Fragment的用法及区别详解

目录 1、<>是什么 2、为什么要使用<>&#xff1f; 3、如何使用<>&#xff1f; 基本用法 需要传递属性时&#xff08;如key&#xff09; 使用效果 注意事项 总结 4、React Fragment 与空标签&#xff08;<>&#xff09;详解 1. Fragment 的用…

【人工智能】使用Python实现时间序列异常检测:从基础到深度学习模型的全方位探索

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 时间序列异常检测是数据分析领域中的重要课题,广泛应用于金融、医疗、工业监控等多个行业。本篇文章深入探讨了时间序列异常检测的基本技术…

Keytool常见问题全解析:从环境配置到公钥提取

引言 在Android开发、跨平台应用构建&#xff08;如UniApp&#xff09;或服务端证书管理中&#xff0c;keytool 是一个不可或缺的工具。然而&#xff0c;许多开发者在使用 keytool 时&#xff0c;常因环境配置、路径权限、密码问题等导致操作失败。本文基于真实问题场景&#…