2. 模型定义基础

GORM 的模型定义是构建数据边界的起点。一个结构体(struct)对应一张数据库表,结构体中的字段对应表的列。通过标签(Tag)、表名映射、字段权限控制以及主键策略,开发者可以精确控制 ORM 的行为,确保数据在 Go 与数据库之间的映射既安全又高效。

1. 结构体标签:字段元数据声明

GORM 使用结构体标签(Tag)来声明字段的数据库属性。标签格式为 gorm:"key1:value1;key2:value2",多个键值对之间用分号分隔。以下是核心标签及其用途:

标签键 说明 示例
column 指定数据库列名,默认使用蛇形命名 gorm:"column:user_name"
type 指定列的数据类型,如 varchar(100) gorm:"type:varchar(100)"
size 定义字段大小(如字符串长度) gorm:"size:255"
primaryKey 标记为主键 gorm:"primaryKey"
autoIncrement 启用自增 gorm:"autoIncrement"
not null 非空约束 gorm:"not null"
unique 唯一约束 gorm:"unique"
default 设置默认值 gorm:"default:0"
comment 字段注释 gorm:"comment:用户昵称"
index 创建索引,可指定索引名 gorm:"index:idx_name"
最佳实践: 标签中的列名建议使用蛇形命名法(snake_case),与数据库惯例保持一致。同时,type 和 size 应配合使用,避免数据库自动推断类型导致意外。
type User struct {
    ID        uint      `gorm:"primaryKey;autoIncrement;column:id"`
    Name      string    `gorm:"type:varchar(100);not null;column:user_name;comment:用户名"`
    Age       int       `gorm:"default:18;column:age"`
    Email     string    `gorm:"unique;size:255;column:email"`
    CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
    UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
}

2. 表名映射:结构体到数据库表的桥梁

GORM 默认将结构体名转换为蛇形复数形式作为表名(如 User → users)。当需要自定义表名时,有三种方式:

  • 实现 TableName() 接口: 在结构体上定义方法,返回自定义表名。
  • 使用 Table() 方法临时指定: 在查询链中动态切换表。
  • 全局表名前缀: 通过 gorm.Config 中的 NamingStrategy 设置前缀。
// 方式一:实现 TableName 接口
func (User) TableName() string {
    return "t_user"
}

// 方式二:查询时临时指定表名
db.Table("t_user").Where("id = ?", 1).First(&user)

// 方式三:全局前缀配置
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    NamingStrategy: schema.NamingStrategy{
        TablePrefix: "t_",   // 表名前缀
    },
})
提示: 当结构体需要映射到已有数据库表时,优先使用 TableName() 接口。这种方式显式且易于维护,避免在查询链中反复指定表名。

3. 字段权限控制:读写分离与安全边界

GORM 通过标签控制字段的读写权限,防止敏感字段被意外写入或读取。权限控制主要依赖以下标签:

标签 作用 适用场景
- 忽略该字段,不参与任何数据库操作 计算字段、临时缓存
-> 只读字段,仅用于读取,不写入 数据库计算列、视图字段
<- 只写字段,仅用于创建/更新,不读取 密码哈希、审计日志
<-:create 仅在创建时写入 创建时间戳
<-:update 仅在更新时写入 更新时间戳
type Account struct {
    ID           uint   `gorm:"primaryKey"`
    Username     string `gorm:"not null"`
    PasswordHash string `gorm:"<-"`                    // 只写:写入时使用,读取时忽略
    ComputedAge  int    `gorm:"->"`                    // 只读:从数据库计算列读取
    TempToken    string `gorm:"-"`                     // 忽略:仅内存中使用
    CreatedAt    time.Time `gorm:"<-:create"`          // 仅创建时写入
    UpdatedAt    time.Time `gorm:"<-:update"`          // 仅更新时写入
}
安全提醒: 对于密码、密钥等敏感字段,务必使用 gorm:"-" 或 gorm:"<-" 限制其序列化与反序列化路径。同时,在 JSON 序列化时配合 json:"-" 标签,避免数据泄露。

4. 主键与自增ID:数据唯一性的基石

主键是每条记录的唯一标识。GORM 支持多种主键策略,最常用的是自增整数主键和 UUID 主键。自增 ID 由数据库自动生成,适合高并发写入场景;UUID 则适合分布式系统,避免主键冲突。

// 自增整数主键(推荐)
type Product struct {
    ID    uint   `gorm:"primaryKey;autoIncrement;column:id"`
    Name  string `gorm:"not null"`
    Price float64
}

// UUID 主键(分布式场景)
type Order struct {
    ID     string `gorm:"primaryKey;type:char(36);column:id"`
    Amount float64
}

// 复合主键
type OrderItem struct {
    OrderID   uint `gorm:"primaryKey;column:order_id"`
    ProductID uint `gorm:"primaryKey;column:product_id"`
    Quantity  int
}

自增 ID 的注意事项:

  • 类型选择: 推荐使用 uint 或 int64,避免 int 在 32 位系统上的溢出风险。
  • 数据库差异: MySQL 使用 auto_increment,PostgreSQL 使用 SERIAL 或 IDENTITY,GORM 自动适配。
  • 批量插入: 自增 ID 在批量插入后可通过 db.Create(&users) 自动回填到结构体中。
GORM 模型定义核心要素 Go 结构体 结构体标签 表名映射 字段权限控制 主键与自增ID 数据库表 关键说明: • 结构体标签是 GORM 模型的核心元数据,控制列名、类型、约束等属性。 • 表名映射支持接口实现、临时指定和全局前缀三种方式,适应不同数据库设计。 • 字段权限控制通过 - / -> / <- 标签实现读写分离,保障数据安全。 • 主键策略选择自增ID(单机)或UUID(分布式),复合主键用于多对多关联表。

5. 综合示例:一个完整的模型定义

将上述知识点整合到一个实际模型中,展示标签、表名、权限和主键的协同使用:

// models/order.go
package models

import (
    "time"
    "gorm.io/gorm"
)

// Order 订单模型
type Order struct {
    ID          uint           `gorm:"primaryKey;autoIncrement;column:id"`
    OrderNo     string         `gorm:"type:varchar(32);unique;not null;column:order_no;comment:订单号"`
    UserID      uint           `gorm:"not null;index:idx_user_id;column:user_id"`
    TotalAmount float64        `gorm:"type:decimal(10,2);default:0.00;column:total_amount"`
    Status      int8           `gorm:"default:0;column:status;comment:0-待支付 1-已支付 2-已取消"`
    InternalNote string        `gorm:"-"`  // 仅内存使用,不持久化
    CreatedAt   time.Time      `gorm:"autoCreateTime;column:created_at"`
    UpdatedAt   time.Time      `gorm:"autoUpdateTime;column:updated_at"`
    DeletedAt   gorm.DeletedAt `gorm:"index;column:deleted_at"` // 软删除
}

// TableName 自定义表名
func (Order) TableName() string {
    return "t_order"
}
设计要点:
  • 主键策略: 使用 uint 自增主键,适合单库场景。
  • 字段权限: InternalNote 使用 gorm:"-" 完全忽略数据库操作。
  • 时间戳: autoCreateTime 和 autoUpdateTime 自动管理创建/更新时间。
  • 软删除: 嵌入 gorm.DeletedAt 实现逻辑删除,避免物理删除带来的数据丢失。

通过以上模型定义,开发者可以精确控制每个字段的数据库行为,为后续的关联查询和事务操作打下坚实的数据边界基础。