gorm中嵌入gorm.model虽自动添加id等字段并设为primarykey,但仅当业务主键恰为整型id且列名为id时可用;否则须显式加gorm:"primarykey"标签,并配合column:指定列名,避免迁移失败或查询异常。

gorm.Model 嵌入时主键默认是 ID,但不能直接依赖它
如果你用 gorm.Model 嵌入结构体,GORM 会自动添加 ID、CreatedAt 等字段,并把 ID 设为 primaryKey。这省事,但仅当你的业务主键恰好叫 ID、类型是整型、且数据库列名也是 id 时才真正可用。一旦表里主键是 user_id 或 uuid 字符串,嵌入后不加标签就会出错——比如迁移失败、查询返回空、或插入时主键被忽略。
常见错误现象:db.Create(&u) 后 u.ID 是 0;db.First(&u, 1) 找不到记录;AutoMigrate 报 “primary key not found”。
- 必须显式声明主键:哪怕字段名就是
ID,也建议写gorm:"primaryKey",避免隐式行为干扰调试 - 主键类型不限于
int:支持uint、string(如 UUID)、int64,但类型必须和数据库列一致 - 列名不等于字段名时,必须补
column:标签,例如UserUUID string `gorm:"primaryKey;column:user_uuid"`
自定义主键名(如 user_id、order_no)必须用 gorm:"primaryKey"
GORM 不会根据字段名(如 UserID)自动识别主键,也不会按蛇形规则推导列名。它只认 gorm:"primaryKey" 这个标签。没加,就不是主键;加了,才参与建表、查询、更新逻辑。
使用场景:老系统表结构已定,主键列是 person_id;或想用业务唯一标识(如订单号 order_no)作主键。
- 字段名可以是
PersonID,但标签必须写全:PersonID uint `gorm:"primaryKey;column:person_id"` - 如果主键是字符串类型,注意数据库是否支持该列作为主键(如 MySQL 的 VARCHAR 主键需指定长度)
-
AutoMigrate会按标签建索引,但不会自动加NOT NULL—— 若数据库要求非空,得额外加not null标签
复合主键在 GORM 中不被原生支持
GORM v2 没有官方复合主键语法。你无法用单个结构体字段标签声明 (a_id, b_id) 是联合主键。强行用 gorm:"primaryKey" 标两个字段,会导致迁移报错或行为未定义。
可行替代方案只有两种:
- 用数据库原生方式建表(如 SQL 手动执行
PRIMARY KEY (a_id, b_id)),GORM 结构体中只设一个字段为primaryKey(通常选逻辑上更稳定的那个),其余字段去掉标签,靠业务层保证唯一性 - 引入合成主键字段(如
ID uint自增),再对业务字段加唯一约束:gorm:"uniqueIndex:idx_a_b"
别试图用 embedded 或多个 primaryKey 标签绕过——GORM 解析器会忽略第二个,或导致字段映射混乱。
主键字段的零值处理容易被忽略
当你调用 db.Create(&u) 时,如果主键字段是整型且值为 0,GORM 默认当作“待生成”,交给数据库自增;但如果主键是字符串(如 UUID string),且值为空字符串 "",GORM 会把它原样插进数据库,而不是跳过或报错。
这意味着:
- 字符串主键必须在代码中确保非空,或用指针类型
*string+ 零值判断 - 想让数据库生成 UUID,得用钩子(
BeforeCreate)赋值,不能依赖 GORM 自动填充 - 主键字段若用了
sql.NullString,要注意其Valid字段为false时,GORM 仍可能插入NULL—— 而多数主键列不允许 NULL
最稳妥的做法:主键字段不设默认零值,初始化时显式赋值,或在钩子里统一生成。











