新接手一个项目,代码里几十上百个 Java 实体类,但数据库表结构文档缺失或过时。或者反向场景:先写了领域模型,需要生成建表语句交给 DBA 执行。这两种情况都涉及同一个问题:怎么从 Java 实体类反推出规范的建表语句?

手动一个个字段对照写 DDL 太耗时,而且容易漏掉索引、唯一约束、注释这些关键信息。这篇文章把反推过程中的决策点拆清楚,并给出可直接落地的检查清单。

场景 1:老项目补文档

接手一个运行了三年的项目,代码里 User.java 有 30 个字段,但数据库表只有 25 个列。多出来的 5 个字段是冗余字段(@Transient 标注),实际不落库。如果直接按 Java 类生成 DDL,会多建 5 个无用列。

排查步骤:

  1. 先确认哪些字段实际映射到数据库:看实体类上的 @TableField(exist = false)、@Transient、或 MyBatis XML 里的 resultMap。
  2. 确认字段类型:Java 的 LocalDateTime 在 MySQL 里是 DATETIME 还是 TIMESTAMP?BigDecimal 是 DECIMAL(10,2) 还是 DECIMAL(19,4)?这些在代码里看不出来,需要查现有表或问 DBA。
  3. 确认索引和约束:代码里 @TableField("email") 上有没有 @TableId?email 列有没有唯一索引?这些只有查数据库元数据才知道。
-- 查现有表的索引和约束
SHOW INDEX FROM user;
SELECT COLUMN_NAME, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_NAME = 'user';

场景 2:领域模型先行生成 DDL

新项目先写领域模型,再生成建表语句。这时没有历史包袱,但决策点更多:

字段类型怎么定?

Java 类型 MySQL 推荐 PostgreSQL 推荐 说明
Long BIGINT BIGINT 主键、大数值
Integer INT INTEGER 常规数值
String VARCHAR(n) VARCHAR(n) n 按业务上限定,不要无脑 255
String(长文本) TEXT TEXT 超过 65535 用 LONGTEXT
BigDecimal DECIMAL(19,4) NUMERIC(19,4) 金额、精度敏感
Double DOUBLE DOUBLE PRECISION 浮点,精度不敏感场景
LocalDateTime DATETIME(6) TIMESTAMP(6) 推荐带微秒
LocalDate DATE DATE 纯日期
Boolean TINYINT(1) BOOLEAN MySQL 无原生 BOOLEAN
byte[] BLOB BYTEA 二进制

关键决策:

  • String 的 VARCHAR 长度:按业务实际最大长度定,不要统一 255。手机号 VARCHAR(20),邮箱 VARCHAR(100),地址 VARCHAR(500)。
  • LocalDateTime 是否带时区:MySQL DATETIME 不带时区,TIMESTAMP 带时区但范围有限(1970-2038)。推荐用 DATETIME(6) 存 UTC,应用层处理时区转换。
  • BigDecimal 精度:金额统一 DECIMAL(19,4),利率 DECIMAL(10,6),按业务约定。

索引和唯一约束怎么定?

代码里看不出来的信息:

  • 主键:@TableId(type = IdType.AUTO) 是自增,ASSIGN_ID 是雪花算法。DDL 里对应 AUTO_INCREMENT 或 BIGINT PRIMARY KEY。
  • 唯一索引:@TableField("email") 上有没有 @TableField(unique = true)?没有的话要查业务逻辑确认。
  • 普通索引:@TableField("status") 上有没有 @TableField(index = true)?MyBatis-Plus 没有标准注解,需要看 Mapper XML 或数据库。
  • 外键:@TableField("order_id") 上有没有 @ManyToOne?JPA 有,MyBatis 没有,需要看业务代码。

建议:生成 DDL 时把索引和约束单独列出来,人工确认后再执行:

CREATE TABLE user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
    name VARCHAR(50) NOT NULL COMMENT '姓名',
    email VARCHAR(100) NOT NULL COMMENT '邮箱',
    status TINYINT(1) DEFAULT 1 COMMENT '状态:0-禁用 1-正常',
    create_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
    update_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
    UNIQUE KEY uk_email (email),
    KEY idx_status (status),
    KEY idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

注释规范

字段注释是 DDL 的灵魂,但 Java 代码里通常没有。反推时要补全:

// Java 实体类(无注释)
private String name;

// DDL(补注释)
`name` VARCHAR(50) NOT NULL COMMENT '姓名'

注释来源:

  1. 字段名直译:avatarUrl → 头像地址
  2. 业务文档:需求文档里的字段说明
  3. 枚举值:status 字段注释里写清楚 0/1 分别代表什么

用工具加速反推

手动反推几十上百个实体类不现实,用 Java 转 SQL 工具 生成初稿,再人工补全索引、约束、注释。

工具生成的 DDL 是基础版本,人工补全后才是可执行的版本:

-- 工具生成(基础版)
CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255),
    email VARCHAR(255),
    status TINYINT,
    create_time DATETIME
);

-- 人工补全(可执行版)
CREATE TABLE user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
    name VARCHAR(50) NOT NULL COMMENT '姓名',
    email VARCHAR(100) NOT NULL COMMENT '邮箱',
    status TINYINT(1) DEFAULT 1 COMMENT '状态:0-禁用 1-正常',
    create_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
    UNIQUE KEY uk_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

常见坑

  • 「Java 类里有的字段都要建列」:@Transient、@TableField(exist = false) 标注的字段不落库,直接生成会多建无用列。
  • 「String 统一 VARCHAR(255)」:手机号 255 字符浪费空间,长文本 255 又不够。按业务实际长度定。
  • 「LocalDateTime 直接映射 DATETIME」:MySQL DATETIME 默认不带微秒,时间精度丢失。要用 DATETIME(6)。
  • 「索引和约束靠猜」:代码里看不出唯一索引和普通索引,必须查数据库元数据或业务文档。
  • 「注释为空」:DDL 没注释,三个月后自己都不知道字段是干嘛的。

检查清单

生成 DDL 后执行前,对照检查:

  • [ ] 所有 @Transient / @TableField(exist = false) 字段已排除
  • [ ] 每个字段的类型与业务语义匹配(金额用 DECIMAL,不是 DOUBLE)
  • [ ] VARCHAR 长度按业务上限定,不是统一 255
  • [ ] 时间字段用 DATETIME(6) 或 TIMESTAMP(6),保留微秒
  • [ ] 主键策略明确(AUTO_INCREMENT / 雪花 ID / UUID)
  • [ ] 唯一约束已加(email、手机号、身份证号等)
  • [ ] 普通索引已加(高频查询字段:status、create_time 等)
  • [ ] 每个字段有 COMMENT 注释,表有 COMMENT 注释
  • [ ] 字符集 utf8mb4,存储引擎 InnoDB(MySQL)

相关工具与速查

  • Java 转 SQL 工具:粘贴 Java 实体类生成建表语句,支持 MySQL / PostgreSQL / Oracle / SQL Server / SQLite 五种方言,自动处理类型映射
  • SQL 通用语法速查:CREATE TABLE、ALTER TABLE、索引、约束等基础语法
  • MySQL 命令速查:SHOW INDEX、information_schema 查询、存储引擎等