主键与自增

主键是数据库表的唯一标识。GORM 对主键有完善的约定和配置支持,包括自增主键、复合主键、UUID 主键等场景。

默认主键

名为 ID 的字段自动成为主键:

type User struct {
    ID   uint  // 自动成为主键,自增
    Name string
}

生成的 SQL:

CREATE TABLE `users` (
    `id` bigint unsigned NOT NULL AUTO_INCREMENT,
    `name` longtext,
    PRIMARY KEY (`id`)
);

自定义主键

用其他字段作为主键:

type User struct {
    UserID uint `gorm:"primaryKey"`
    Name   string
}

复合主键

多个字段组成主键:

type OrderItem struct {
    OrderID uint `gorm:"primaryKey"`
    ItemID  uint `gorm:"primaryKey"`
    Qty     int
}

生成的 SQL:

CREATE TABLE `order_items` (
    `order_id` bigint unsigned NOT NULL,
    `item_id` bigint unsigned NOT NULL,
    `qty` bigint,
    PRIMARY KEY (`order_id`, `item_id`)
);

自增配置

默认自增

整数类型的主键默认自增:

type User struct {
    ID   uint  // 自增主键
    Name string
}

关闭自增

type User struct {
    ID   uint `gorm:"primaryKey;autoIncrement:false"`
    Name string
}

自定义自增起始值

GORM 不直接支持,需要用原生 SQL:

db.Exec("ALTER TABLE users AUTO_INCREMENT = 1000")

UUID 主键

用 UUID 作为主键:

import "github.com/google/uuid"

type User struct {
    ID   uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
    Name string
}

PostgreSQL 内置 gen_random_uuid() 函数。MySQL 需要用触发器或应用层生成。

应用层生成 UUID:

type User struct {
    ID   uuid.UUID `gorm:"type:uuid;primaryKey"`
    Name string
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.ID == uuid.Nil {
        u.ID = uuid.New()
    }
    return nil
}

字符串主键

type User struct {
    ID   string `gorm:"primaryKey;size:36"`
    Name string
}

创建时手动设置:

user := User{
    ID:   uuid.New().String(),
    Name: "张三",
}
db.Create(&user)

雪花 ID 主键

import "github.com/bwmarrin/snowflake"

type User struct {
    ID   int64 `gorm:"primaryKey"`
    Name string
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
    if u.ID == 0 {
        node, _ := snowflake.NewNode(1)
        u.ID = node.Generate().Int64()
    }
    return nil
}

获取插入后的主键

创建记录后,主键值会回填到结构体:

user := User{Name: "张三"}
db.Create(&user)
fmt.Println(user.ID)  // 输出插入后的主键值

LastInsertId

GORM v2 不直接暴露 LastInsertId,但可以通过回填获取:

user := User{Name: "张三"}
result := db.Create(&user)
fmt.Println(user.ID)  // 新记录的主键
fmt.Println(result.RowsAffected)  // 影响行数

主键与关联

主键类型影响关联关系的定义:

// 整数主键
type User struct {
    ID    uint
    Name  string
    Posts []Post
}

type Post struct {
    ID     uint
    UserID uint  // 外键类型要匹配
    User   User
}

// UUID 主键
type User struct {
    ID   uuid.UUID
    Name string
    Posts []Post
}

type Post struct {
    ID     uuid.UUID
    UserID uuid.UUID  // 外键类型要匹配
    User   User
}

无主键表

有些场景不需要主键(如关联表):

type UserRole struct {
    UserID uint `gorm:"primaryKey"`
    RoleID uint `gorm:"primaryKey"`
}

实际上还是复合主键,只是没有单独的自增字段。

小结

主键配置要点:

场景配置方式
默认主键字段名 ID
自定义主键gorm:"primaryKey"
复合主键多个字段加 primaryKey 标签
关闭自增autoIncrement:false
UUID 主键gorm:"type:uuid;primaryKey"
字符串主键gorm:"primaryKey;size:36"

选择主键类型要考虑业务需求:

  • 自增整数:简单高效,适合大多数场景
  • UUID:分布式友好,但索引性能稍差
  • 雪花 ID:分布式友好,整数类型索引性能好