必须实现scanner和valuer接口,因为gorm无法直接处理复杂go类型(如[]int、map或自定义struct),需通过scanner将数据库原始值(如[]byte)反序列化为go值,通过valuer将go值序列化为数据库可存的基础类型;缺一即报错或丢数据。

为什么必须实现 Scanner 和 Valuer 接口
GORM 本身不直接理解 Go 中的复杂类型(比如 []int、map[string]interface{} 或自定义 struct),数据库字段只能存基础类型(TEXT、JSON、BLOB 等)。所以读写时必须有人“翻译”:入库前把 Go 值转成数据库能收的格式,查出后把原始字节还原成 Go 值。Scanner 负责后者,Valuer 负责前者。漏掉任一接口,GORM 就会报 unsupported driver type 或静默丢数据。
[]int 类型的 Scan 方法常见错误
最常踩的坑是忽略空值和类型断言失败:
- 没处理
nil或空[]byte—— 反序列化会 panic,应显式赋空切片:*al = []int{} - 直接用
value.([]byte)断言,但 PostgreSQL 的 JSONB 字段可能返回string(尤其用 lib/pq 旧驱动),需兼容:if bs, ok := value.([]byte); ok { ... } else if s, ok := value.(string); ok { ... } - 没检查
json.Unmarshal错误就直接赋值,导致结构错乱却无提示
Value 方法返回值类型必须匹配字段定义
返回值不是随便写字符串或字节切片就行,得看数据库字段类型:
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
- MySQL 的
JSON字段:推荐返回[]byte(如json.Marshal(al)),避免字符串双引号被额外转义 - PostgreSQL 的
JSONB字段:同样用[]byte;若返回string,某些驱动会自动加单引号导致解析失败 - 通用
TEXT字段:可返回string,但要注意前端传空数组[]时,json.Marshal([]int{})得到的是"[]",不是空字符串
示例中若字段定义为 avatar TEXT,但 Value 返回了 []byte,GORM 会尝试调用其 String() 方法,结果可能是乱码或截断。
比起手写 Scanner/Valuer,优先用 serializer:json
除非你需要深度控制(比如加密存储、字段过滤、兼容旧格式),否则 GORM v1.25+ 内置的 serializer 标签更安全省事:
- 字段声明写成
Avatar []int `gorm:"column:avatar;serializer:json"`,GORM 自动处理编解码 - 它默认对零值(如
nilslice)序列化为null,而手写逻辑常误序列化为空数组[] - 不依赖你实现接口,也就绕过了类型断言、空值、驱动差异等所有底层陷阱
真正需要手写接口的场景其实很少:比如字段要存为 base64 编码的 JSON,或读取时需做字段映射转换,或兼容已有非标准 JSON 格式。其他情况,serializer:json 是更稳的选择。










