新接手一个项目,代码里几十上百个 Java 实体类,但数据库表结构文档缺失或过时。或者反向场景:先写了领域模型,需要生成建表语句交给 DBA 执行。这两种情况都涉及同一个问题:怎么从 Java 实体类反推出规范的建表语句?
手动一个个字段对照写 DDL 太耗时,而且容易漏掉索引、唯一约束、注释这些关键信息。这篇文章把反推过程中的决策点拆清楚,并给出可直接落地的检查清单。
场景 1:老项目补文档
接手一个运行了三年的项目,代码里 User.java 有 30 个字段,但数据库表只有 25 个列。多出来的 5 个字段是冗余字段(@Transient 标注),实际不落库。如果直接按 Java 类生成 DDL,会多建 5 个无用列。
排查步骤:
- 先确认哪些字段实际映射到数据库:看实体类上的
@TableField(exist = false)、@Transient、或 MyBatis XML 里的 resultMap。 - 确认字段类型:Java 的
LocalDateTime在 MySQL 里是DATETIME还是TIMESTAMP?BigDecimal是DECIMAL(10,2)还是DECIMAL(19,4)?这些在代码里看不出来,需要查现有表或问 DBA。 - 确认索引和约束:代码里
@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是否带时区:MySQLDATETIME不带时区,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 '姓名'
注释来源:
- 字段名直译:
avatarUrl→头像地址 - 业务文档:需求文档里的字段说明
- 枚举值:
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 查询、存储引擎等