Java + MyBatis + MySQL 查询数据库表字段信息(含注释)完整实现

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.ymlapplication.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