很多情况下,我们有存json的需求
那么就需要使用到gorm中的自定义数据类型了
自定义的数据类型必须实现 Scanner 和 Valuer 接口
简单说就是,在入库的时候转换一下,变成普通字符串进行存储,查询的时候,映射为结构体
JSON自定义数据类型
json数据用的比较多
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.Sprint("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是接受者方法
插入数据
type User struct {
ID int64
Name string `gorm:"size:32"`
Info Info `gorm:"type:longtext" json:"info"`
}
DB.Create(&User{
Name: "枫枫",
Info: Info{
Age: 21,
Addr: "湖南长沙",
Like: []string{"唱", "跳", "rap"},
},
})
查询数据
var user User
DB.Take(&user, 1)
fmt.Println(user.Name, user.Info.Like)
序列化
如果只是存储json数据,完全可以使用gorm的序列化,自带json
type User struct {
ID int64
Name string `gorm:"size:32"`
Info Info `gorm:"type:longtext;serializer:json" json:"info"`
}
当然也可以自定义序列化器,参考如下:
序列化 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
枚举类型
有些情况下,在数据库里面需要存储一些标识状态的内容
比如描述日志的等级,通常有固定的那么几个值,例如info、warning、error
如果直接存储字符串,很明显是浪费空间
所以可以使用自定义类型
type Status int8
const (
Running Status = 1
Warning Status = 2
)
func (s Status) MarshalJSON() (data []byte, err error) {
var str string
switch s {
case Running:
str = "运行中"
case Warning:
str = "警告"
}
return json.Marshal(str)
}
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(map[string]any{
"status": int8(s),
"statusTitle": str,
})
}