Go 自定义类型在 GORM 中的 MySQL 序列化方案

1. 背景

在实际开发中,我们经常需要将结构体中的某些字段以 JSON 形式存储到 MySQL 的 text 或 json 字段中。Go 的 GORM 框架支持通过实现 driver.Valuer 和 sql.Scanner 接口,实现自定义类型的序列化和反序列化。

2. 关键接口

  • driver.Valuer:用于将 Go 类型序列化为数据库可存储的类型。
  • sql.Scanner:用于将数据库中的数据反序列化为 Go 类型。

3. 典型实现

以 TypedSlice[T] 为例,实现如下:
 
 
Value 实现 driver.Valuer 接口(只需实现非指针类型)
Scan 实现 sql.Scanner 接口(必须实现指针类型)
 
 
> 注意:Go 语言不允许同一个类型(包括指针和非指针)有两个同名方法,因此 Value 只实现一次(非指针即可),Scan 只实现指针类型。

4. GORM 字段定义

在你的 Model 结构体中,字段应这样声明:
type MyModel struct {
    ID        uint   `gorm:"primarykey"`
    Concepts  custom_type.TypedSlice[string] `gorm:"column:concepts;type:text" json:"concepts"`
}
 
注意:gorm:"type:text" 保证 MySQL 字段类型为 text,能存储较长的 JSON 字符串。

5. 数据库表结构

 
CREATE TABLE my_model (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  concepts TEXT
);

6. 使用 Demo

插入数据

 
m := MyModel{
    Concepts: custom_type.TypedSlice[string]{"A", "B", "C"},
}
db.Create(&m)

查询数据

 
var m MyModel
db.First(&m, 1)
fmt.Println(m.Concepts) // 输出: [A B C]

更新数据

 
db.Model(&m).Update("concepts", custom_type.TypedSlice[string]{"X", "Y"})

7. 常见问题

  • Value/Scan 未被调用:确保类型和指针类型分别实现了接口(Value 用非指针,Scan 用指针)。
  • MySQL 字段类型不对:务必加上 gorm:"type:text"
  • Scan 报类型不支持:Scan 方法要兼容 string 和 []byte 两种类型。
  • Value 实现 driver.Valuer 接口(只需实现非指针类型)
  • Scan 实现 sql.Scanner 接口(必须实现指针类型)
posted @ 2025-06-04 00:15  若-飞  阅读(101)  评论(0)    收藏  举报