type JSON json.RawMessage // Scan scan value into Jsonb, implements sql.Scanner interface 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 } // Value return json value, implement driver.Valuer interface func(j JSON) Value() (driver.Value, error) { iflen(j) == 0 { returnnil, nil } return json.RawMessage(j).MarshalJSON() }
There are many third party packages implement the
Scanner
/
Valuer
interface, which can be used with GORM together, for example:
import ( "github.com/google/uuid" "github.com/lib/pq" ) type Post struct { ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"` Title string Tags pq.StringArray `gorm:"type:text[]"` }
GORM will read column’s database type from
tag
type
, if not found, will check if the struct implemented interface
GormDBDataTypeInterface
or
GormDataTypeInterface
and will use its result as data type
type GormDataTypeInterface interface { GormDataType() string } type GormDBDataTypeInterface interface { GormDBDataType(*gorm.DB, *schema.Field) string }
The result of
GormDataType
will be used as the general data type and can be obtained from
schema.Field
‘s field
DataType
, which might be helpful when
writing plugins
or
hooks
for example:
func(JSON) GormDataType() string { return"json" } type User struct { Attrs JSON } func(user User) BeforeCreate(tx *gorm.DB) { field := tx.Statement.Schema.LookUpField("Attrs") if field.DataType == "json" { // do something } }
GormDBDataType
usually returns the right data type for current driver when migrating, for example:
func(JSON) GormDBDataType(db *gorm.DB, field *schema.Field) string { // use field.Tag, field.TagSettings gets field's tags // checkout https://github.com/go-gorm/gorm/blob/master/schema/field.go for all options // returns different database type based on driver name switch db.Dialector.Name() { case"mysql", "sqlite": return"JSON" case"postgres": return"JSONB" } return"" }
If the struct hasn’t implemented the
GormDBDataTypeInterface
or
GormDataTypeInterface
interface, GORM will guess its data type from the struct’s first field, for example, will use
string
for
NullString
type NullString struct { String string// use the first field's data type Valid bool } type User struct { Name NullString // data type will be string }
// Generates SQL with clause Expression 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'