gorm仅映射结构体到数据库,不负责模型设计;需手动定义类型、外键、索引、约束及软删处理,automigrate不删除字段或表。

GORM 本身不负责“设计数据模型”,它只负责把 Go 结构体映射成数据库表;真正决定模型合理性的,是业务逻辑、关系约束和数据库规范。别指望 GormTag 加几行就搞定设计,该画 ER 图、想外键、定索引的地方,一个都不能少。
怎么让 struct 字段正确对应数据库列类型
Go 类型太泛,比如 int 在 MySQL 里可能变成 BIGINT,而你实际只需要 SMALLINT;string 默认映射为 VARCHAR(255),但邮箱字段通常只需 VARCHAR(100),多余长度浪费索引空间。
- 用
type+gormtag 显式控制:比如Age uint8 `gorm:"type:tinyint unsigned"` -
time.Time默认映射为DATETIME,若需带时区(如 PostgreSQL 的TIMESTAMPTZ),得写gorm:"type:timestamptz" - 布尔字段别依赖
bool→TINYINT(1)的默认行为,MySQL 中TINYINT不是标准布尔类型,应用层取值可能错(如读到 2),建议统一用tinyint(1) CHECK (col IN (0,1))并配gorm:"type:tinyint;check:col IN (0,1)"
一对多关系中,外键字段要不要手动声明
要。GORM 的自动外键推导(如 UserID 对应 user.id)只在命名严格匹配时生效,一旦结构体嵌套、字段重命名或使用复合主键,就会静默失败——查不到关联数据,也没有报错。
- 显式加
gorm:"foreignKey:UserID"到子表 struct 字段上,比靠约定更稳 - 父表要加
gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"才能触发级联,否则删用户时订单不会自动删 - 别把外键字段设为
sql.NullInt64就以为能存 NULL——GORM 默认不设NULL,得额外加gorm:"null"
软删除字段 DeletedAt 的坑在哪
启用 gorm.DeletedAt 后,所有 Find/First 查询自动加 WHERE deleted_at IS NULL,这看着省事,实则埋雷:
- 统计总数时
Count也受软删影响,要查真实行数得用Unscoped().Count() - 关联预加载(
Preload)默认不跳过软删记录,可能导致“父存在、子被删”却仍被加载出来 - 如果你的业务真需要硬删(比如 GDPR 数据彻底清除),别只改
DeletedAt,得配合Unscoped().Delete(),且确保事务里先删子再删父,否则外键报错
最常被忽略的一点:GORM 的 AutoMigrate 只建表、改列、加索引,它不会删字段、不会删表、不会改外键约束。上线后改了 struct,别以为跑一遍 AutoMigrate 就万事大吉——旧字段还在,脏数据可能已产生,外键缺失会导致 JOIN 失败。真要动结构,得写 migration 脚本,或者用 db.Migrator().DropColumn() 这类明确操作。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











