数据库表结构演进与迁移实践

项目的数据库表不会一成不变:加字段、加索引、拆表、加软删除……在 GoFrame 工程里,表结构一变,dao/do/entity 就要跟着变。需要先明确一个边界:gf gen dao代码生成工具,不做表结构迁移——它读的是“数据库当前真实的表结构”。因此正确的演进流程是“先改库,再重跑 gen dao”。

演进流程:改库 → 重生成 → 跟进代码

  1. 准备 DDL 脚本(建表/加字段/加索引),在测试库或迁移脚本中执行。
  2. 回到项目根目录执行 gf gen dao(配置见“代码生成”章节),同步 dao/do/entity
  3. 检查生成差异:新字段是否出现在 entitydo 的指针字段是否合理、类型映射是否符合预期。
  4. 业务代码按新结构编译、联调。
-- 以 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 会把注释带进 entitydescription 标签,是最好的文档。

与时间字段相关的演进

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 daoclear 参数删除“库里已不存在的表”对应代码时务必谨慎。

小结

表结构演进的循环是:DDL 进库 → gf gen dao 同步 → 用 do 的指针语义做部分更新 → 随版本发布 SQL。认清“gen dao 只生成、不迁移”的边界,流程就不会乱。更细的 DAO 设计建议以官方文档 goframe.org 为准。

笔记加载中…