GORM 提供了少量接口,使用户能够为 GORM 定义支持的数据类型,这里以 json 为例

实现数据类型

Scanner / Valuer

自定义的数据类型必须实现 ScannerValuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库

例如:

  1. type JSON json.RawMessage
  2. // 实现 sql.Scanner 接口,Scan 将 value 扫描至 Jsonb
  3. func (j *JSON) Scan(value interface{}) error {
  4. bytes, ok := value.([]byte)
  5. if !ok {
  6. return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
  7. }
  8. result := json.RawMessage{}
  9. err := json.Unmarshal(bytes, &result)
  10. *j = JSON(result)
  11. return err
  12. }
  13. // 实现 driver.Valuer 接口,Value 返回 json value
  14. func (j JSON) Value() (driver.Value, error) {
  15. if len(j) == 0 {
  16. return nil, nil
  17. }
  18. return json.RawMessage(j).MarshalJSON()
  19. }

GormDataTypeInterface

自定义数据类型在不同的数据库中可能是不同数据类型,您可以实现 GormDataTypeInterface 来设置它们,例如:

  1. type GormDataTypeInterface interface {
  2. GormDBDataType(*gorm.DB, *schema.Field) string
  3. }
  4. func (JSON) GormDBDataType(db *gorm.DB, field *schema.Field) string {
  5. // 使用 field.Tag、field.TagSettings 获取字段的 tag
  6. // 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 获取全部的选项
  7. // 根据不同的数据库驱动返回不同的数据类型
  8. switch db.Dialector.Name() {
  9. case "mysql":
  10. return "JSON"
  11. case "postgres":
  12. return "JSONB"
  13. }
  14. return ""
  15. }

Clause Expression

自定义数据类型可能需要特殊的 SQL,此时 GORM 提供的 API 不适用。这时候您可以定义一个 Builder 来实现 clause.Expression 接口

  1. type Expression interface {
  2. Build(builder Builder)
  3. }

查看 JSON 获取详情

  1. // 根据 Clause Expression 生成 SQL
  2. db.Find(&user, datatypes.JSONQuery("attributes").HasKey("role"))
  3. db.Find(&user, datatypes.JSONQuery("attributes").HasKey("orgs", "orga"))
  4. // MySQL
  5. // SELECT * FROM `users` WHERE JSON_EXTRACT(`attributes`, '$.role') IS NOT NULL
  6. // SELECT * FROM `users` WHERE JSON_EXTRACT(`attributes`, '$.orgs.orga') IS NOT NULL
  7. // PostgreSQL
  8. // SELECT * FROM "user" WHERE "attributes"::jsonb ? 'role'
  9. // SELECT * FROM "user" WHERE "attributes"::jsonb -> 'orgs' ? 'orga'
  10. db.Find(&user, datatypes.JSONQuery("attributes").Equals("jinzhu", "name"))
  11. // MySQL
  12. // SELECT * FROM `user` WHERE JSON_EXTRACT(`attributes`, '$.name') = "jinzhu"
  13. // PostgreSQL
  14. // SELECT * FROM "user" WHERE json_extract_path_text("attributes"::json,'name') = 'jinzhu'

自定义数据类型集合

我们创建了一个 Github 仓库,用于收集各种自定义数据类型https://github.com/go-gorm/datatype,非常欢迎同学们的 pull request ;)