添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

GORM provides few interfaces that allow users to define well-supported customized data types for GORM, takes json as an example

Implements Customized Data Type

Scanner / Valuer

Scanner and Valuer interfaces, so GORM knowns to how to receive/save it into the database

For example:

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) {
if len(j) == 0 {
return nil, 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[]"`
}

GormDataTypeInterface

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
}

GormValuerInterface

Create/Update from SQL Expr

type Location struct {
X, Y int
}

func (loc Location) GormDataType() string {
return "geometry"
}

func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
return clause.Expr{
SQL: "ST_PointFromText(?)",
Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
}
}

// Scan implements the sql.Scanner interface
func (loc *Location) Scan(v interface{}) error {
// Scan a value into struct from database driver
}

type User struct {
ID int
Name string
Location Location
}

db.Create(&User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`point`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))

db.Model(&User{ID: 1}).Updates(User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// UPDATE `user_with_points` SET `name`="jinzhu",`location`=ST_PointFromText("POINT(100 100)") WHERE `id` = 1

Create From SQL Expr and Update with SQL Expression for details

Value based on Context

Clause Expression

JSON and SQL Builder for details, the following is an example of usage:

// 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'

Customized Data Types Collections

https://github.com/go-gorm/datatypes , pull request welcome ;)

Getting Started Overview Declaring Models Connecting to Database CRUD Interface Create Query Advanced Query Update Delete Raw SQL & SQL Builder Associations Belongs To Has One Has Many Many To Many Association Mode Preloading (Eager Loading) Tutorials Context Error Handling Method Chaining Session Hooks Transactions Migration Logger Generic Database Interface Performance Customize Data Types Scopes Conventions Settings Advanced Topics Database Resolver Sharding Serializer Prometheus Hints Indexes Constraints Composite Primary Key Security GORM Config Write Plugins Write Driver ChangeLog Community Contribute Translate current site Getting Started Overview Declaring Models Connecting to Database CRUD Interface Create Query Advanced Query Update Delete Raw SQL & SQL Builder Associations Belongs To Has One Has Many Many To Many Association Mode Preloading (Eager Loading) Tutorials Context Error Handling Method Chaining Session Hooks Transactions Migration Logger Generic Database Interface Performance Customize Data Types Scopes Conventions Settings Advanced Topics Database Resolver Sharding Serializer Prometheus Hints Indexes Constraints Composite Primary Key Security GORM Config Write Plugins Write Driver ChangeLog Community Contribute Translate current site English