联合主键在gorm中需显式标注多个primary_key标签,不可依赖自增id;automigrate据此生成复合主键ddl,但sqlite仅认首个字段;查询须传完整结构体或map,create不自动填充主键,关联预加载受限。

联合主键在 GORM 中必须显式声明为 primary_key,且不能依赖自增 ID;否则 AutoMigrate 会失败或生成错误表结构。
struct tag 中必须同时标注多个 primary_key
GORM 不识别“复合主键”这个概念,只认字段级的 primary_key 标签。只要两个及以上字段都带 primary_key,它就会生成联合主键 DDL。
- PostgreSQL 和 MySQL 均支持,但 SQLite 会忽略(仅用第一个标记字段当主键)
- 字段顺序影响索引顺序,建议按高频查询条件从左到右排列
- 不能混用
autoIncrement或serial类型字段——联合主键里所有字段都得是手动赋值的
示例:
type OrderItem struct {
OrderID uint `gorm:"primary_key"`
ProductID uint `gorm:"primary_key"`
Quantity int
}
这样迁移后,PostgreSQL 会生成:PRIMARY KEY ("order_id", "product_id")
Where 查询联合主键必须传完整结构体或 map
直接用 db.First(&v, id) 这种单参数方式会失败——GORM 不知道该把 id 映射到哪个字段。
- 推荐用结构体初始化后传指针:
db.Where(&OrderItem{OrderID: 123, ProductID: 456}).First(&item) - 也可用 map:
db.Where(map[string]interface{}{"order_id": 123, "product_id": 456}).First(&item),注意 key 是数据库列名(小写+下划线) - 避免用字符串条件如
"order_id = ? AND product_id = ?",易出错且无法复用预编译
联合主键表不支持 Create 自动填充 ID
因为没有单一主键字段可被 GORM 自动赋值,Create 后结构体里的主键字段不会被回填(比如 PostgreSQL 的 RETURNING 无效)。
- 插入前必须确保
OrderID和ProductID都已赋值 - 如果依赖数据库生成(如序列),需手动用
db.Exec执行 INSERT + RETURNING,再 Scan 回结构体 -
Save行为等同于Create或Update,但对联合主键表来说,它只按字段值做 UPSERT,不触发主键生成逻辑
关联查询和 Preload 对联合主键模型支持有限
GORM 的 Preload 默认按主键字段匹配,而联合主键没有单一外键字段可映射,容易报 invalid association。
- 想查「订单 + 其所有 OrderItem」,不能直接
db.Preload("Items").First(&order),除非你定义了明确的外键关系 - 更可靠的方式是分两步:先查主表,再用
Where拼IN条件查子表(例如db.Where("order_id IN ?", orderIDs).Find(&items)) - 如果硬要用关联,需在子模型中加
foreignKey和references标签,并确保引用字段类型与父表主键一致
联合主键真正麻烦的地方不在定义,而在后续所有涉及“按主键操作”的环节——GORM 的很多快捷方法默认假设存在一个 ID 字段,一旦打破这个假设,就得退回到更底层、更显式的写法。











