2. GORM 模型标签基础:gorm 标签语法、常用标签与作用域
GORM 通过结构体标签(Struct Tag)将 Go 结构体字段映射到数据库表的列。掌握标签语法与常用标签,是编写契约校验规则的前提。本章从标签语法入手,逐一拆解 column、type、size、primaryKey 等核心标签,并说明标签的作用域与优先级。
2.1 gorm 标签语法
GORM 标签写在结构体字段的 gorm 命名空间内,多个标签用分号分隔,标签名与值之间用冒号连接。基本格式如下:
type User struct {
ID uint `gorm:"column:id;type:bigint;primaryKey"`
Name string `gorm:"column:name;type:varchar(100);size:100;not null"`
}
标签语法要点:
- 命名空间:必须使用
gorm:"...",其他命名空间(如json、validate)不会被 GORM 解析。 - 分隔符:多个标签之间使用英文分号
;分隔,最后一个标签后不加分号。 - 键值对:标签名与值之间用英文冒号
:连接,值不需要引号包裹。 - 布尔标签:部分标签(如
primaryKey、autoIncrement)只写标签名即可,不需要值。 - 大小写敏感:标签名对大小写不敏感,但建议统一使用小写驼峰或全小写。
gorm:"column:user_name" json:"userName"。
2.2 常用标签详解
下表列出 GORM 最常用的标签及其作用:
| 标签名 | 作用 | 示例 | 说明 |
|---|---|---|---|
column |
指定数据库列名 | column:user_id |
默认使用字段名的蛇形命名 |
type |
指定列的数据类型 | type:varchar(255) |
覆盖 GORM 自动推断的类型 |
size |
指定列长度(仅对部分类型有效) | size:100 |
常用于 varchar、int 等类型 |
primaryKey |
标记为主键 | primaryKey |
布尔标签,无需值 |
autoIncrement |
自增 | autoIncrement |
布尔标签,通常与主键配合 |
not null |
非空约束 | not null |
布尔标签 |
unique |
唯一约束 | unique |
布尔标签 |
default |
默认值 | default:0 |
值可以是数字、字符串或表达式 |
comment |
列注释 | comment:用户ID |
仅用于建表/迁移 |
index |
创建索引 | index:idx_name |
可指定索引名称 |
2.2.1 column 标签
column 标签用于显式指定数据库列名。如果不写,GORM 默认将 Go 字段名转换为蛇形命名(如 UserName → user_name)。当需要与现有表结构对齐或使用非标准命名时,必须使用 column 标签。
type Product struct {
ProductID uint `gorm:"column:product_id"`
ProductName string `gorm:"column:product_name"`
}
2.2.2 type 标签
type 标签直接指定数据库列的数据类型,包括长度和精度。GORM 会根据 Go 类型自动推断(如 string → varchar(255)),但使用 type 标签可以精确控制:
type Order struct {
Amount float64 `gorm:"type:decimal(10,2)"`
Status string `gorm:"type:enum('pending','paid','shipped')"`
CreatedAt time.Time `gorm:"type:datetime(3)"`
}
type 标签会完全覆盖 GORM 的自动类型推断。如果写错类型,可能导致迁移失败或运行时错误。建议仅在需要特殊类型(如 enum、json、decimal)时使用。
2.2.3 size 标签
size 标签用于指定列的长度,主要影响 varchar、int 等可变长度类型。当与 type 标签同时存在时,size 会追加到类型定义中:
type Article struct {
Title string `gorm:"type:varchar;size:200"` // 生成 varchar(200)
Content string `gorm:"size:500"` // 生成 varchar(500)
Views int `gorm:"size:10"` // 生成 int(10)
}
如果 type 标签已经包含长度(如 type:varchar(200)),则 size 标签会被忽略。
2.2.4 primaryKey 标签
primaryKey 是一个布尔标签,标记该字段为主键。GORM 默认将名为 ID 的字段视为主键,但使用 primaryKey 标签可以显式指定任意字段为主键:
type Employee struct {
EmpNo uint `gorm:"primaryKey"`
FullName string
}
支持复合主键:
type Membership struct {
UserID uint `gorm:"primaryKey"`
GroupID uint `gorm:"primaryKey"`
JoinedAt time.Time
}
2.3 标签作用域
GORM 标签的作用域决定了标签在哪些操作中生效。理解作用域有助于避免标签冲突和意外行为。
下表按操作类型划分标签作用域:
| 操作类型 | 生效标签 | 不生效标签 |
|---|---|---|
| 建表/迁移(AutoMigrate) | column、type、size、primaryKey、autoIncrement、not null、unique、default、comment、index |
无 |
| CRUD 读写 | column、primaryKey、autoIncrement |
type、size、not null、unique、default、comment、index |
| 查询条件构建 | column、primaryKey |
其他所有标签 |
| 关联预加载 | foreignKey、references、constraint |
列类型相关标签 |
column 和 primaryKey 是全局生效的标签,在所有操作中都会被解析。type、size、not null 等约束类标签仅在 AutoMigrate 和 Create 表时生效,不影响已有表的读写操作。
2.4 标签优先级与覆盖规则
当多个标签对同一属性产生冲突时,GORM 按以下优先级处理:
- 显式标签优先:结构体标签中的值始终优先于 GORM 默认推断。
- 后写覆盖前写:同一标签多次出现时,最后一个生效。
- type 覆盖 size:如果
type标签已包含长度信息,size标签被忽略。 - 字段名 vs column:如果未写
column标签,GORM 使用字段名的蛇形命名;写了则完全使用column指定的名称。
// 示例:标签覆盖
type Config struct {
Key string `gorm:"column:config_key;column:cfg_key;size:50;size:100"`
// 最终 column = "cfg_key"(后写覆盖),size = 100(后写覆盖)
}
2.5 标签与契约校验的关系
在《GORM 契约校验》课程中,标签是定义字段约束的第一道防线。通过 not null、unique、default、size 等标签,可以在模型层声明数据契约,配合后续的校验器实现“定义即校验”。
下图展示了标签在契约校验流程中的位置:
2.6 常见陷阱与最佳实践
- 陷阱 1:type 与 size 混用导致类型重复——避免同时写
type:varchar(200)和size:200,只选其一。 - 陷阱 2:忘记写 column 标签导致列名不符合预期——如果数据库已有表且列名非蛇形,务必显式指定
column。 - 陷阱 3:primaryKey 标签与 autoIncrement 不匹配——主键自增需要同时写
primaryKey和autoIncrement,缺一不可。 - 最佳实践 1:所有字段都显式写出
column标签,提高可读性。 - 最佳实践 2:将标签按逻辑分组排列,先写
column,再写type/size,最后写约束标签。 - 最佳实践 3:使用
comment标签为关键字段添加说明,便于团队协作。
// 推荐写法示例
type User struct {
ID uint `gorm:"column:id;type:bigint;primaryKey;autoIncrement;comment:用户ID"`
Username string `gorm:"column:username;type:varchar;size:50;not null;unique;comment:用户名"`
Email string `gorm:"column:email;type:varchar;size:100;not null;unique;comment:邮箱"`
Age int `gorm:"column:age;type:int;size:3;default:0;comment:年龄"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;comment:创建时间"`
}