数据库表结构演进与迁移实践
项目的数据库表不会一成不变:加字段、加索引、拆表、加软删除……在 GoFrame 工程里,表结构一变,dao/do/entity 就要跟着变。需要先明确一个边界:gf gen dao 是代码生成工具,不做表结构迁移——它读的是“数据库当前真实的表结构”。因此正确的演进流程是“先改库,再重跑 gen dao”。
演进流程:改库 → 重生成 → 跟进代码
- 准备 DDL 脚本(建表/加字段/加索引),在测试库或迁移脚本中执行。
- 回到项目根目录执行
gf gen dao(配置见“代码生成”章节),同步dao/do/entity。 - 检查生成差异:新字段是否出现在
entity、do的指针字段是否合理、类型映射是否符合预期。 - 业务代码按新结构编译、联调。
-- 以 article 表为例:新增浏览量字段并加索引
ALTER TABLE `article`
ADD COLUMN `view_count` int unsigned NOT NULL DEFAULT 0 COMMENT '浏览量' AFTER `status`,
ADD INDEX `idx_category_id` (`category_id`);
# 表结构已变更,重新生成代码
gf gen dao
# 输出会提示新增/更新的表;entity/do 被整体覆盖
重新生成后,internal/model/entity/article.go 中会出现 ViewCount uint 字段,do.Article 中为 *uint 指针形式(便于表达“不更新该字段”),DAO 文件外层保持不动。
用 do 表达部分更新
表结构演进后最常见的代码改动是“更新接口只改部分字段”。do 的指针字段让“零值也算赋值、未设置就不参与更新”变得可表达:
// 只更新 title 与 view_count,不影响其他字段
_, err := dao.Article.Ctx(ctx).
Data(do.Article{
Title: g.NewVar("新标题"),
ViewCount: g.NewVar(viewCount + 1),
}).
Where("id", 1).
Update()
if err != nil {
return err
}
指针/包装类型字段为 nil 时(此处未列出即未设置)不参与生成的 SQL,这正是官方推荐用指针属性和 do 对象实现灵活修改接口的原因。
版本化迁移的组织建议
框架没有强制的迁移框架,官方工程常见做法是“SQL 文件随版本管理 + 发布时执行”:
project/
├── hack/ # CLI 生成配置等
├── manifest/
│ └── sql/ # 按版本组织的 DDL/DML 脚本
│ ├── v1.0.0_init.sql
│ └── v1.1.0_article_view_count.sql
└── internal/
├── dao/ model/ ... # gen dao 产物,随表结构更新
- 发布单里明确“先执行某 SQL,再发新版本程序”,顺序反了会出现运行时字段不匹配。
- 破坏性变更(删列、改类型)先做兼容性评估:老程序还在跑时,新增字段用“先加后删”两步走。
- 数据库字段注释写清楚,
gen dao会把注释带进entity与description标签,是最好的文档。
与时间字段相关的演进
GoFrame ORM 支持自动时间维护(配置 createdAt/updatedAt/deletedAt 列名),常用约定:
created_at/updated_at:由gen dao生成的表结构模板普遍带DEFAULT CURRENT_TIMESTAMP,写入交给数据库。- 软删除:保留
deleted_at列并配合 ORM 的时间维护/查询过滤使用,删除变更新,便于恢复与审计。
按官方文档,若启用了 ORM 时间维护,字段名与行为需与配置一致(例如 TimeMaintainDisabled 可关闭该特性),具体取舍以官方文档 goframe.org 为准。
注意点
- 永远不要手改
entity/do:下次gf gen dao会被覆盖,改表结构源头才是正路。 dao外层文件(可扩展区)不受覆盖影响,但重生成后注意与新internal基础文件保持同步。- 生产环境 DDL 要在低峰执行并评估锁表影响,加字段尽量用“可瞬间完成的 ALTER”。
- 用
gf gen dao的clear参数删除“库里已不存在的表”对应代码时务必谨慎。
小结
表结构演进的循环是:DDL 进库 → gf gen dao 同步 → 用 do 的指针语义做部分更新 → 随版本发布 SQL。认清“gen dao 只生成、不迁移”的边界,流程就不会乱。更细的 DAO 设计建议以官方文档 goframe.org 为准。