Go结构体映射MySQL JSON字段需显式声明gorm:"type:json",推荐用json.RawMessage延迟解析或自定义类型实现Scanner/Valuer接口;直接用string虽简单但需手动序列化/反序列化,易出错。

Go 结构体怎么映射 MySQL 的 JSON 字段?
直接用 string 最省事,但得自己序列化/反序列化;用自定义类型(如 json.RawMessage 或封装的 struct)更安全,能避免字段错位或静默丢数据。
常见错误是把 JSON 字段声明成 map[string]interface{} 或普通 struct 后直接塞进 GORM 模型——GORM 不会自动帮你做 JSON 编解码,运行时要么报错,要么存成空字符串或 null。
-
string类型:适合只读、不校验结构、或前端传来的原始 JSON 字符串需原样落库(比如配置快照) -
json.RawMessage:保留原始字节,延迟解析,适合字段结构不确定或想在业务层统一处理 - 具体 struct(如
Milestones []Milestone):需要配合Scan/Value方法实现driver.Valuer和sql.Scanner接口,否则 GORM 写不进去
GORM 的 JSON 字段必须加 type:json tag 吗?
必须。MySQL 8.0+ 原生支持 JSON 类型,但 GORM 不会自动推断——不显式声明 gorm:"type:json",它默认按 TEXT 处理,插入时可能被截断,查询时也拿不到 JSON 函数支持(比如 JSON_EXTRACT)。
示例:
type Writer struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
Milestones json.RawMessage `gorm:"column:milestones;type:json"`
Metadata string `gorm:"column:metadata;type:json"` // 这里用 string 也行,但 type:json 不能少
}漏掉 type:json 会导致:CREATE TABLE 语句里生成的是 TEXT 而非 JSON,后续用 JSON_CONTAINS 等函数会报错。
前端传 JSON 字段,Gin 绑定后怎么写进数据库?
别让 c.ShouldBindJSON 直接绑定到含 JSON 字段的模型上——Gin 默认只处理 json tag,不触发 GORM 的 Valuer/Scanner 逻辑。中间多一层转换更可控。
- 先绑定到一个纯 JSON 的中间结构体(带
jsontag),验证后再转成 GORM 模型 - 如果用了
json.RawMessage,要确保赋值时不是 nil(nil写库会变成NULL,而空对象{}是合法 JSON) - 写库前手动调用
json.Marshal转成[]byte或string,再赋给模型字段(尤其用string类型时)
典型坑:前端传 {"milestones":null},Gin 绑定后 Milestones 是 nil,GORM 写库变成 NULL;但业务可能期望默认为 [],得在绑定后补默认值。
JSON 字段能用 GORM 的 Where 查询吗?
能,但语法和普通字段不同。MySQL 的 JSON 字段必须用函数操作,比如 JSON_CONTAINS、JSON_EXTRACT,GORM 不提供开箱即用的链式方法。
推荐写法是用 Where("JSON_CONTAINS(?, ?)", "milestones", `"\"backend\""`),或者更安全的原生 SQL 查询:
db.Raw("SELECT * FROM writers WHERE JSON_CONTAINS(milestones, ?)", `"\"devops\""`).Scan(&writers)注意:JSON_EXTRACT(milestones, '$.role') 返回带引号的字符串(如 "senior"),比较时要加双引号;用 ->> 运算符(MySQL 5.7+)可自动去引号,但 GORM 不支持该语法缩写,得手写。
真正容易被忽略的点:JSON 字段没法建传统 B-tree 索引,查深层嵌套字段性能差——真要高频查询某个 key,建议额外抽字段冗余存储,别全靠 JSON 解析。


















