This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/GORM/02-模型定义.md
T
2026-04-28 20:02:42 +08:00

5.7 KiB
Raw Blame History

tags, create time
tags create time
GORM
Go
ORM
模型
struct tag
字段映射
主键
表名
2026-04-28 00:00

模型定义

概述

模型(Model)是 GORM 操作的核心入口——一个普通的 Go struct 被 GORM「看见」后,就可以直接进行数据库操作。本章讲解如何通过 struct tag、命名约定和接口来自定义 GORM 对模型的解读。

什么是 Model?

[!definition] Model Model 是一个包含以下任一条件的 struct:

  1. 有主键字段
  2. 定义了 TableName() 方法
  3. 被 AutoMigrate、Create、Where 等方法引用
type User struct {
    ID   uint   `gorm:"primaryKey"`
    Name string `gorm:"size:64;not null"`
}
// 这就是一个最简单的 GORM Model

表名规则

默认命名策略

GORM 使用 Table 方法来推导表名:

代码行为 结果
默认 struct 名的蛇形复数形式(User → users)
实现 TableName() string 返回的字符串
使用 db.Table("xxx") 查询时使用的表名(不会修改 Model 的 TableName)
func (User) TableName() string {
    return "sys_user" // 显式指定表名
}

[!tip] 多环境表名 不同环境下表名前缀不同?可以用环境变量 + TableName() 实现动态切换:

func (User) TableName() string {
    if os.Getenv("ENV") == "test" {
        return "test_sys_user"
    }
    return "sys_user"
}

字段 Tag 详解

完整 Tag 语法

gorm:"column:name;type:bigint;not null;default:0;uniqueIndex;index;comment:用户ID"
Tag 关键字 作用 示例
column 映射列名 column:user_id
type 列类型覆盖 type:varchar(128)
not null NOT NULL 约束 not null
default 默认值 default:'unknown'
primaryKey 主键 primaryKey
autoIncrement 自增 autoIncrement
uniqueIndex 唯一索引 uniqueIndex:idx_name
index 普通索引 index:idx_status
comment 注释(MySQL) comment:用户名
<- 字段写入控制 <-:true / <-:false / <-:create / <-:update
-> 只读/只写 ->:true(只读) / <-:false(不写入)
- 忽略此字段 -

写入权限控制

type Product struct {
    ID        uint      `gorm:"primaryKey;autoIncrement"`
    Name      string    `gorm:"not null;<-:create"`          // 仅创建时可写入
    Price     float64   `gorm:"not null;<-:true;->:true"`    // 读写均可(默认)
    CreatedAt time.Time `gorm:"<-:create;->:true;autoTime"`  // 仅创建时自动填充
    UpdatedAt time.Time `gorm:"<-:update;->:true;autoTime"`  // 仅更新时自动填充
    ViewCount int       `gorm:"->:false;<-:false"`           // 完全忽略(非 DB 字段)
}

[!example] 方向记忆法 <- 表示数据流向数据库(写入),-> 表示数据从数据库流出(读取)。箭头方向就是数据的方向。

字段类型映射

Go 类型 推荐 DB 类型 说明
int, int64 BIGINT GORM 默认以 int64 处理
uint, uint64 BIGINT UNSIGNED —
string VARCHAR(n) 需手动指定 size
bool TINYINT(1) / BOOLEAN —
float64 DOUBLE —
time.Time DATETIME / TIMESTAMP 配合 autoTime tag
[]byte BLOB / BYTEA 二进制数据
json.RawMessage JSON JSON 序列化字段

autoTime 自动时间戳

type Article struct {
    ID        uint      `gorm:"primaryKey"`
    Title     string    `gorm:"size:128;not null"`
    CreatedAt time.Time `gorm:"autoTime;createTime"`  // 创建时自动填充
    UpdatedAt time.Time `gorm:"autoTime;updateTime"`  // 更新时自动填充
}

[!note] createTime / updateTime 这两个子 tag 是 GORM v2 的增强功能,配合 autoTime 使用。单独 autoTime 只更新时间,加上 createTime 后才会在插入时设置 CreatedAt。

主键策略

默认策略:ID uint

type User struct {
    ID   uint   // GORM 默认将名为 ID 的字段视为主键
    Name string
}

UUID 主键

import "github.com/google/uuid"

type Order struct {
    ID   uuid.UUID `gorm:"type:char(36);primaryKey"`
    Code string    `gorm:"uniqueIndex;not null"`
}

// 在 Create 前生成 UUID
order.ID = uuid.New()
db.Create(&order)

复合主键

type OrderItem struct {
    OrderID   uint `gorm:"primaryKey"`
    ProductID uint `gorm:"primaryKey"`
    Quantity  int  `gorm:"not null"`
}
// 生成表结构:PRIMARY KEY (order_id, product_id)

[!tip] 复合主键注意事项 使用复合主键时,First() 和 Take() 将无法工作(因为它们期望单主键),必须使用 Where() 精确定位。

自定义类型作为字段

当内置类型不够用时,可以实现 GORM 接口:

type MyType int

func (m MyType) GORMDataType() string {
    return "INT"
}

func (m MyType) Value() (driver.Value, error) {
    return int(m), nil  // ScannerValuer 接口的 Value 方法
}

模型设计流程图

flowchart TD
    A[定义 struct] --> B{有无 ID 字段?}
    B -->|有| C["ID 作主键<br/>uint/int64"]
    B -->|无| D{自定义 TableName?}
    D -->|实现了| E[按 TableName 返回值]
    D -->|未实现| F["struct 名蛇形复数"]
    C --> G[添加字段 tag]
    E --> G
    G --> H{需要特殊类型?}
    H -->|是| I[实现 GORMDataType / Valuer]
    H -->|否| J[完成]
    I --> J
    
    style A fill:#4FC08D,color:#fff
    style C fill:#3B82F6,color:#fff
    style I fill:#EAB308,color:#fff
    style J fill:#A0AEC0,color:#fff

关联笔记