Golang GORM 自定义数据类型
项目中使用 postgres 数据库,相对于mysql来说,pg的支持的数据类型更多,特别是 json,jsonb的数据类型,可以减少数据表的创建。比如:对于使用1对N的关联关系,可以将N放在json字段中。
但是带来的问题是在 GORM的模型中,不能简单的使用基础类型,int,string来表述,当然也可以使用 json.RawMessage来表示,但是这样不知道具体的字段类型结构是怎样的。怎么能够既方便使用json字段,又能够知道具体的数据类型呢?可以使用GORM的自定义类型。
官方示例 json.RawMessage
自定义的数据类型必须实现 Scanner 和 Valuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库
type JSON json.RawMessage
// 实现 sql.Scanner 接口,Scan 将 value 扫描至 Jsonb
func (j *JSON) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
}
result := json.RawMessage{}
err := json.Unmarshal(bytes, &result)
*j = JSON(result)
return err
}
// 实现 driver.Valuer 接口,Value 返回 json value
func (j JSON) Value() (driver.Value, error) {
if len(j) == 0 {
return nil, nil
}
return json.RawMessage(j).MarshalJSON()
}
这个只是最基础的,还是不能满足我们的需求。
更通用的数据类型
官方还创建了一个 github仓库,GitHub - go-gorm/datatypes: GORM Customized Data Types Collection,收集各种自定义数据类型。
现在go支持泛型,使用起来还是很方便的。
import "gorm.io/datatypes"
type Attribute struct {
Sex int
Age int
Orgs map[string]string
Tags []string
Admin bool
Role string
}
type UserWithJSON struct {
gorm.Model
Name string
Attributes datatypes.JSONType[Attribute]
}
var user = UserWithJSON{
Name: "hello",
Attributes: datatypes.NewJSONType(Attribute{
Age: 18,
Sex: 1,
Orgs: map[string]string{"orga": "orga"},
Tags: []string{"tag1", "tag2", "tag3"},
}),
}
// Create
DB.Create(&user)
// First
var result UserWithJSON
DB.First(&result, user.ID)
// Update
jsonMap = UserWithJSON{
Attributes: datatypes.NewJSONType(Attribute{
Age: 18,
Sex: 1,
Orgs: map[string]string{"orga": "orga"},
Tags: []string{"tag1", "tag2", "tag3"},
}),
}
DB.Model(&user).Updates(jsonMap)
通过使用 datatypes.JSONType 可以实现既使用json字段,又能够清楚的知道json存放的具体数据类型。