【Gorm】自定义数据类型
很多情况下,我们有存JSON的需求,那么就需要使用到gorm中的自定义数据类型了。自定义的数据类型必须实现 Scanner 和 Valuer 接口,简单地说,在入库的时候转换一下,变成普通字符串记性存储,查询的时候,映射为结构体。
一、JSON 自定义数据类型
JSON数据用的比较多,我们在迁移数据库之前,我们需要先对自定义数据类型进行接口函数的重新定义,只需要定义 Scanner 和 Valuer 函数即可,否则会迁移数据库失败。下面,我们来看一看代码是什么??
type Info struct {
Like []string `json:"like"`
Addr string `json:"addr"`
Age int `json:"age"`
}
// Scan 实现 sql.Scanner 接口, Scan 将 value 扫描到 Jsonb
func (j *Info) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprintf("Failed to unmarshal JSONB value:", value))
}
result := Info{}
err := json.Unmarshal(bytes, &result)
*j = result
return err
}
// value 实现 driver.Valuer 接口, Value 返回 json value
func (j Info) Value() (driver.Value, error) {
return json.Marshal(j)
}
注意:这里千万不要自作聪明,去改指针方法或者接受者方法,Scan是指针方法,Value是接收者方法。
1.1 插入数据
我们可以使用 create 方法去插入数据,代码如下:
type User struct {
ID int64
Name string `gorm:"size:32"`
Info Info `gorm:"type:longtext" json:"info"`
}
DB.Create(&User{
Name: "加油旭杏",
Info: Info{
Age: 20,
Addr: "湖北武汉",
Like: []string{"唱", "跳", "rap"},
},
})
1.2 查询数据
var user User
DB.Take(&user, 1)
fmt.Println(user.Name, user.Info.Like)
二、序列化
如果只是存储 json 数据,完全可以使用 gorm 的序列化,自带 json,我们的自定义类型不需要写上述的两个函数接口,只需要在建立数据库的结构体的字段后添加一个字段即可:`gorm:"serializer:json"`,代码如下:
type User struct {
ID int64
Name string `gorm:"size:32"`
Info Info `gorm:"type:longtext;serializer:json" json:"info"`
}
三、枚举类型
有些情况下,在数据库里面需要存储一些标识状态的内容,比如描述日志的等级,通常有固定的几个值,比如:info,waring,error等,如果直接存储字符串,很明显是浪费空间,所以可以使用自定义类型。枚举类型不需要进行编写两个函数接口,就可以迁移数据库。代码如下:
type Status int8
const (
Running Status = 1
Warning Status = 2
)
type User struct {
ID int64
Name string `gorm:"size:32"`
Status Status `json:"status"`
}
因为返回前端的时候,肯定是会走json序列化的,所以返回给前端的时候就是对应的中文,我们可以实现一个函数,代码如下:
func (s Status) MarshalJSON() (data []byte, err error) {
var str string
switch s {
case Running:
str = "运行中"
case Warning:
str = "警告"
}
return json.Marshal(str)
}
不过这个函数可以再优化一下,只返回中文的数据是比较少的,还需要把他本身的数字也返回出来,我们可以使用map来进行操作,代码如下:
func (s Status) MarshalJSON() (data []byte, err error) {
var str string
switch s {
case Running:
str = "运行中"
case Warning:
str = "警告"
}
return json.Marshal(map[string]any{
"status": int8(s),
"statusTitle": str,
})
}