Golang GORM 自定义数据类型

984 阅读2分钟

Golang GORM 自定义数据类型

项目中使用 postgres 数据库,相对于mysql来说,pg的支持的数据类型更多,特别是 json,jsonb的数据类型,可以减少数据表的创建。比如:对于使用1对N的关联关系,可以将N放在json字段中。

但是带来的问题是在 GORM的模型中,不能简单的使用基础类型,int,string来表述,当然也可以使用 json.RawMessage来表示,但是这样不知道具体的字段类型结构是怎样的。怎么能够既方便使用json字段,又能够知道具体的数据类型呢?可以使用GORM的自定义类型。

官方示例 json.RawMessage

自定义的数据类型必须实现 ScannerValuer 接口,以便让 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存放的具体数据类型。