# 數據類型
GORM 提供了少量接口,使用戶能夠為 GORM 定義支持的數據類型,這里以 [json](https://github.com/go-gorm/datatypes/blob/master/json.go) 為例
## 實現數據類型
### Scanner / Valuer
自定義的數據類型必須實現 [Scanner](https://pkg.go.dev/database/sql/sql#Scanner) 和 [Valuer](https://pkg.go.dev/database/sql/driver#Valuer) 接口,以便讓 GORM 知道如何將該類型接收、保存到數據庫
例如:
```go
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()
}
```
### GormDataTypeInterface
自定義數據類型在不同的數據庫中可能是不同數據類型,您可以實現 `GormDataTypeInterface` 來設置它們,例如:
```go
type GormDataTypeInterface interface {
GormDBDataType(*gorm.DB, *schema.Field) string
}
func (JSON) GormDBDataType(db *gorm.DB, field *schema.Field) string {
// 使用 field.Tag、field.TagSettings 獲取字段的 tag
// 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 獲取全部的選項
// 根據不同的數據庫驅動返回不同的數據類型
switch db.Dialector.Name() {
case "mysql":
return "JSON"
case "postgres":
return "JSONB"
}
return ""
}
```
### Clause Expression
自定義數據類型可能需要特殊的 SQL,此時 GORM 提供的 API 不適用。這時候您可以定義一個 `Builder` 來實現 `clause.Expression` 接口
```go
type Expression interface {
Build(builder Builder)
}
```
查看 [JSON](https://github.com/go-gorm/datatypes/blob/master/json.go) 獲取詳情
```go
// 根據 Clause Expression 生成 SQL
db.Find(&user, datatypes.JSONQuery("attributes").HasKey("role"))
db.Find(&user, datatypes.JSONQuery("attributes").HasKey("orgs", "orga"))
// MySQL
// SELECT * FROM `users` WHERE JSON_EXTRACT(`attributes`, '$.role') IS NOT NULL
// SELECT * FROM `users` WHERE JSON_EXTRACT(`attributes`, '$.orgs.orga') IS NOT NULL
// PostgreSQL
// SELECT * FROM "user" WHERE "attributes"::jsonb ? 'role'
// SELECT * FROM "user" WHERE "attributes"::jsonb -> 'orgs' ? 'orga'
db.Find(&user, datatypes.JSONQuery("attributes").Equals("jinzhu", "name"))
// MySQL
// SELECT * FROM `user` WHERE JSON_EXTRACT(`attributes`, '$.name') = "jinzhu"
// PostgreSQL
// SELECT * FROM "user" WHERE json_extract_path_text("attributes"::json,'name') = 'jinzhu'
```
## 自定義數據類型集合
我們創建了一個 Github 倉庫,用于收集各種自定義數據類型[https://github.com/go-gorm/datatype](https://github.com/go-gorm/datatypes),非常歡迎同學們的 pull request ;)