1. 引言
在实际的数据库开发和管理中,经常需要动态获取数据库表的字段信息,包括字段名、数据类型、长度、是否允许为空、默认值以及字段注释等。这种需求在以下场景中特别常见:
- 代码生成工具:根据数据库表结构自动生成实体类
- 数据字典管理:自动生成数据库文档
- 动态表单系统:根据表结构动态生成表单
- 数据校验:根据字段类型和长度进行动态校验
本文将详细介绍如何使用 Java、MyBatis 和 MySQL 实现查询数据库表字段信息的完整解决方案。
2. 环境准备
2.1 项目依赖
首先,在 pom.xml 中添加必要的依赖:
xml
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Lombok(可选,简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.2 数据库配置
在 application.yml 或 application.properties 中配置数据库连接:
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3. 实体类设计
3.1 字段信息实体类
创建 ColumnInfo 实体类,用于封装字段信息:
java
package com.example.demo.entity;
import lombok.Data;
/**
* 数据库字段信息实体类
*/
@Data
public class ColumnInfo {
/**
* 字段名
*/
private String columnName;
/**
* 数据类型
*/
private String dataType;
/**
* 字段类型(包含长度信息)
*/
private String columnType;
/**
* 字段注释
*/
private String columnComment;
/**
* 是否为主键
*/
private String columnKey;
/**
* 是否允许为空
*/
private String isNullable;
/**
* 默认值
*/
private String columnDefault;
/**
* 字符最大长度
*/
private Long characterMaximumLength;
/**
* 数字精度
*/
private Integer numericPrecision;
/**
* 数字小数位数
*/
private Integer numericScale;
/**
* 额外信息(如 auto_increment)
*/
private String extra;
}
3.2 表信息实体类
创建 TableInfo 实体类,用于封装表信息:
java
package com.example.demo.entity;
import lombok.Data;
import java.util.List;
/**
* 数据库表信息实体类
*/
@Data
public class TableInfo {
/**
* 表名
*/
private String tableName;
/**
* 表注释
*/
private String tableComment;
/**
* 字段列表
*/
private List<ColumnInfo> columns;
}
4. Mapper 接口与 XML 配置
4.1 Mapper 接口
创建 TableMapper 接口:
java
package com.example.demo.mapper;
import com.example.demo.entity.ColumnInfo;
import com.example.demo.entity.TableInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface TableMapper {
/**
* 查询指定数据库中的所有表名
* @param databaseName 数据库名
* @return 表名列表
*/
List<String> getTableNames(@Param("databaseName") String databaseName);
/**
* 查询指定表的字段信息
* @param databaseName 数据库名
* @param tableName 表名
* @return 字段信息列表
*/
List<ColumnInfo> getColumnInfo(
@Param("databaseName") String databaseName,
@Param("tableName") String tableName
);
/**
* 查询指定表的注释
* @param databaseName 数据库名
* @param tableName 表名
* @return 表注释
*/
String getTableComment(
@Param("databaseName") String databaseName,
@Param("tableName") String tableName
);
/**
* 获取完整的表信息(包含字段列表)
* @param databaseName 数据库名
* @param tableName 表名
* @return 表信息
*/
TableInfo getTableInfo(
@Param("databaseName") String databaseName,
@Param("tableName") String tableName
);
}
4.2 Mapper XML 配置
创建 TableMapper.xml 文件:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.TableMapper">
<!-- 查询指定数据库中的所有表名 -->
<select id="getTableNames" resultType="java.lang.String">
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = #{databaseName}
AND TABLE_TYPE = 'BASE TABLE'
ORDER BY TABLE_NAME
</select>
<!-- 查询指定表的字段信息(包含注释) -->
<select id="getColumnInfo" resultType="com.example.demo.entity.ColumnInfo">
SELECT
COLUMN_NAME AS columnName,
DATA_TYPE AS dataType,
COLUMN_TYPE AS columnType,
COLUMN_COMMENT AS columnComment,
COLUMN_KEY AS columnKey,
IS_NULLABLE AS isNullable,
COLUMN_DEFAULT AS columnDefault,
CHARACTER_MAXIMUM_LENGTH AS characterMaximumLength,
NUMERIC_PRECISION AS numericPrecision,
NUMERIC_SCALE AS numericScale,
EXTRA AS extra
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #{databaseName}
AND TABLE_NAME = #{tableName}
ORDER BY ORDINAL_POSITION
</select>
<!-- 查询指定表的注释 -->
<select id="getTableComment" resultType="java.lang.String">
SELECT TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = #{databaseName}
AND TABLE_NAME = #{tableName}
</select>
<!-- 获取完整的表信息 -->
<select id="getTableInfo" resultMap="tableInfoMap">
SELECT
t.TABLE_NAME,
t.TABLE_COMMENT,
c.COLUMN_NAME,
c.DATA_TYPE,
c.COLUMN_TYPE,
c.COLUMN_COMMENT,
c.COLUMN_KEY,
c.IS_NULLABLE,
c.COLUMN_DEFAULT,
c.CHARACTER_MAXIMUM_LENGTH,
c.NUMERIC_PRECISION,
c.NUMERIC_SCALE,
c.EXTRA
FROM INFORMATION_SCHEMA.TABLES t
LEFT JOIN INFORMATION_SCHEMA.COLUMNS c
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
AND t.TABLE_NAME = c.TABLE_NAME
WHERE t.TABLE_SCHEMA = #{databaseName}
AND t.TABLE_NAME = #{tableName}
ORDER BY c.ORDINAL_POSITION
</select>
<resultMap id="tableInfoMap" type="com.example.demo.entity.TableInfo">
<result property="tableName" column="TABLE_NAME"/>
<result property="tableComment" column="TABLE_COMMENT"/>
<collection property="columns" ofType="com.example.demo.entity.ColumnInfo">
<result property="columnName" column="COLUMN_NAME"/>
<result property="dataType" column="DATA_TYPE"/>
<result property="columnType" column="COLUMN_TYPE"/>
<result property="columnComment" column="COLUMN_COMMENT"/>
<result property="columnKey" column="COLUMN_KEY"/>
<result property="isNullable" column="IS_NULLABLE"/>
<result property="columnDefault" column="COLUMN_DEFAULT"/>
<result property="characterMaximumLength" column="CHARACTER_MAXIMUM_LENGTH"/>
<result property="numericPrecision" column="NUMERIC_PRECISION"/>
<result property="numericScale" column="NUMERIC_SCALE"/>
<result property="extra" column="EXTRA"/>
</collection>
</resultMap>
</mapper>
5. Service 层实现
创建 TableService 服务类:
java
package com.example.demo.service;
import com.example.demo.entity.ColumnInfo;
import com.example.demo.entity.TableInfo;
import com.example.demo.mapper.TableMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TableService {
@Autowired
private TableMapper tableMapper;
/**
* 获取数据库中的所有表名
* @param databaseName 数据库名
* @return 表名列表
*/
public List<String> getAllTables(String databaseName) {
return tableMapper.getTableNames(databaseName);
}
/**
* 获取指定表的所有字段信息
* @param databaseName 数据库名
* @param tableName 表名
* @return 字段信息列表
*/
public List<ColumnInfo> getTableColumns(String databaseName, String tableName) {
return tableMapper.getColumnInfo(databaseName, tableName);
}
/**
* 获取完整的表信息
* @param databaseName 数据库名
* @param tableName 表名
* @return 表信息
*/
public TableInfo getTableInfo(String databaseName, String tableName) {
return tableMapper.getTableInfo(databaseName, tableName);
}
/**
* 获取数据库所有表的完整信息
* @param databaseName 数据库名
* @return 所有表信息列表
*/
public List<TableInfo> getAllTableInfos(String databaseName) {
List<String> tableNames = getAllTables(databaseName);
return tableNames.stream()
.map(tableName -> getTableInfo(databaseName, tableName))
.toList();
}
/**
* 生成数据字典(Markdown格式)
* @param databaseName 数据库名
* @return Markdown格式的数据字典
*/
public String generateDataDictionary(String databaseName) {
List<TableInfo> tables = getAllTableInfos(databaseName);
StringBuilder markdown = new StringBuilder();
markdown.append("# 数据库字典 - ").append(databaseName).append("\n\n");
for (TableInfo table : tables) {
markdown.append("## ").append(table.getTableName())
.append(" - ").append(table.getTableComment()).append("\n\n");
markdown.append("| 字段名 | 类型 | 是否为空 | 默认值 | 主键 | 注释 |\n");
markdown.append("|--------|------|----------|--------|------|------|\n");
for (ColumnInfo column : table.getColumns()) {
markdown.append("| ")
.append(column.getColumnName()).append(" | ")
.append(column.getColumnType()).append(" | ")
.append(column.getIsNullable()).append(" | ")
.append(column.getColumnDefault() != null ? column.getColumnDefault() : "").append(" | ")
.append(column.getColumnKey() != null ? column.getColumnKey() : "").append(" | ")
.append(column.getColumnComment()).append(" |\n");
}
markdown.append("\n");
}
return markdown.toString();
}
}
6. Controller 层实现
创建 TableController 控制器:
java
package com.example.demo.controller;
import com.example.demo.entity.ColumnInfo;
import com.example.demo.entity.TableInfo;
import com.example.demo.service.TableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/table")
public class TableController {
@Autowired
private TableService tableService;
/**
* 获取数据库中的所有表名
* @param databaseName 数据库名
* @return 表名列表
*/
@GetMapping("/tables")
public List<String> getTables(@RequestParam String databaseName) {
return tableService.getAllTables(databaseName);
}
/**
* 获取指定表的所有字段信息
* @param databaseName 数据库名
* @param tableName 表名
* @return 字段信息列表
*/
@GetMapping("/columns")
public List<ColumnInfo> getColumns(
@RequestParam String databaseName,
@RequestParam String tableName) {
return tableService.getTableColumns(databaseName, tableName);
}
/**
* 获取完整的表信息
* @param databaseName 数据库名
* @param tableName 表名
* @return 表信息
*/
@GetMapping("/info")
public TableInfo getTableInfo(
@RequestParam String databaseName,
@RequestParam String tableName) {
return tableService.getTableInfo(databaseName, tableName);
}
/**
* 获取数据库所有表的完整信息
* @param databaseName 数据库名
* @return 所有表信息列表
*/
@GetMapping("/all")
public List<TableInfo> getAllTables(@RequestParam String databaseName) {
return tableService.getAllTableInfos(databaseName);
}
/**
* 生成数据字典(Markdown格式)
* @param databaseName 数据库名
* @return Markdown格式的数据字典
*/
@GetMapping("/dictionary")
public String generateDictionary(@RequestParam String databaseName) {
return tableService.generateDataDictionary(databaseName);
}
/**
* 下载数据字典文件
* @param databaseName 数据库名
* @return 数据字典文件
*/
@GetMapping("/dictionary/download")
public ResponseEntity<byte[]> downloadDictionary(@RequestParam String databaseName) {
String markdown = tableService.generateDataDictionary(databaseName);
byte[] content = markdown.getBytes(StandardCharsets.UTF_8);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
headers.setContentDispositionFormData("attachment",
databaseName + "_data_dictionary.md");
headers.setContentLength(content.length);
return new ResponseEntity<>(content, headers, HttpStatus.OK);
}
}
7. 使用示例
7.1 测试代码
创建测试类 TableServiceTest:
java
package com.example.demo;
import com.example.demo.service.TableService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TableServiceTest {
@Autowired
private TableService tableService;
@Test
void testGetTableColumns() {
String databaseName = "your_database";
String tableName = "user";
// 获取表的所有字段信息
var columns = tableService.getTableColumns(databaseName, tableName);
columns.forEach(column -> {
System.out.println("字段名: " + column.getColumnName());
System.out.println("类型: " + column.getColumnType());
System.out.println("注释: " + column.getColumnComment());
System.out.println("是否为空: " + column.getIsNullable());
System.out.println("主键: " + column.getColumnKey());
System.out.println("默认值: " + column.getColumnDefault());
System.out.println("---");
});
}
@Test
void testGenerateDataDictionary() {
String databaseName = "your_database";
// 生成数据字典
String dictionary = tableService.generateDataDictionary(databaseName);
System.out.println(dictionary);
// 保存到文件
try {
Files.writeString(
Paths.get("data_dictionary.md"),
dictionary,
StandardCharsets.UTF_8
);
System.out.println("数据字典已保存到 data_dictionary.md");
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.2 API 调用示例
启动应用后,可以通过以下 API 进行测试:
bash
# 1. 获取所有表名
curl "http://localhost:8080/api/table/tables?databaseName=your_database"
# 2. 获取指定表的字段信息
curl "http://localhost:8080/api/table/columns?databaseName=your_database&tableName=user"
# 3. 获取完整的表信息
curl "http://localhost:8080/api/table/info?databaseName=your_database&tableName=user"
# 4. 获取所有表的完整信息
curl "http://localhost:8080/api/table/all?databaseName=your_database"
# 5. 生成数据字典
curl "http://localhost:8080/api/table/dictionary?databaseName=your_database"
# 6. 下载数据字典文件
curl -o "data_dictio