Add Serializer Scan support
This commit is contained in:
parent
6393ad5222
commit
bf85076f0e
@ -40,14 +40,17 @@ type SavePointerDialectorInterface interface {
|
|||||||
RollbackTo(tx *DB, name string) error
|
RollbackTo(tx *DB, name string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TxBeginner tx beginner
|
||||||
type TxBeginner interface {
|
type TxBeginner interface {
|
||||||
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConnPoolBeginner conn pool beginner
|
||||||
type ConnPoolBeginner interface {
|
type ConnPoolBeginner interface {
|
||||||
BeginTx(ctx context.Context, opts *sql.TxOptions) (ConnPool, error)
|
BeginTx(ctx context.Context, opts *sql.TxOptions) (ConnPool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TxCommitter tx commiter
|
||||||
type TxCommitter interface {
|
type TxCommitter interface {
|
||||||
Commit() error
|
Commit() error
|
||||||
Rollback() error
|
Rollback() error
|
||||||
@ -58,6 +61,7 @@ type Valuer interface {
|
|||||||
GormValue(context.Context, *DB) clause.Expr
|
GormValue(context.Context, *DB) clause.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDBConnector SQL db connector
|
||||||
type GetDBConnector interface {
|
type GetDBConnector interface {
|
||||||
GetDBConn() (*sql.DB, error)
|
GetDBConn() (*sql.DB, error)
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ type Field struct {
|
|||||||
ReflectValueOf func(context.Context, reflect.Value) reflect.Value
|
ReflectValueOf func(context.Context, reflect.Value) reflect.Value
|
||||||
ValueOf func(context.Context, reflect.Value) (value interface{}, zero bool)
|
ValueOf func(context.Context, reflect.Value) (value interface{}, zero bool)
|
||||||
Set func(context.Context, reflect.Value, interface{}) error
|
Set func(context.Context, reflect.Value, interface{}) error
|
||||||
|
Serializer SerializerInterface
|
||||||
NewValuePool FieldNewValuePool
|
NewValuePool FieldNewValuePool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +169,16 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
||||||
field.DataType = String
|
field.DataType = String
|
||||||
} else if _, ok := field.TagSettings["SERIALIZER"]; ok {
|
field.Serializer = v
|
||||||
|
} else if name, ok := field.TagSettings["SERIALIZER"]; ok {
|
||||||
field.DataType = String
|
field.DataType = String
|
||||||
|
if strings.ToLower(name) == "json" {
|
||||||
|
field.Serializer = &JSONSerializer{}
|
||||||
|
} else {
|
||||||
|
schema.err = fmt.Errorf("invalid serializer type %v", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
||||||
@ -413,69 +420,21 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
|||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync pools
|
|
||||||
var (
|
|
||||||
normalPool sync.Map
|
|
||||||
stringPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v string
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
intPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v int64
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
uintPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v uint64
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
floatPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v float64
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
boolPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v bool
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
timePool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
var v time.Time
|
|
||||||
ptrV := &v
|
|
||||||
return &ptrV
|
|
||||||
},
|
|
||||||
}
|
|
||||||
poolInitializer = func(reflectType reflect.Type) FieldNewValuePool {
|
|
||||||
if v, ok := normalPool.Load(reflectType); ok {
|
|
||||||
return v.(FieldNewValuePool)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return reflect.New(reflectType).Interface()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return v.(FieldNewValuePool)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// create valuer, setter when parse struct
|
// create valuer, setter when parse struct
|
||||||
func (field *Field) setupValuerAndSetter() {
|
func (field *Field) setupValuerAndSetter() {
|
||||||
// Setup NewValuePool
|
// Setup NewValuePool
|
||||||
if _, ok := reflect.New(field.IndirectFieldType).Interface().(sql.Scanner); !ok {
|
var fieldValue = reflect.New(field.FieldType).Interface()
|
||||||
|
if field.Serializer != nil {
|
||||||
|
field.NewValuePool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &Serializer{
|
||||||
|
Field: field,
|
||||||
|
Interface: reflect.New(reflect.ValueOf(field.Serializer).Type()).Interface().(SerializerInterface),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if _, ok := fieldValue.(sql.Scanner); !ok {
|
||||||
|
// set default NewValuePool
|
||||||
switch field.IndirectFieldType.Kind() {
|
switch field.IndirectFieldType.Kind() {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
field.NewValuePool = stringPool
|
field.NewValuePool = stringPool
|
||||||
@ -595,6 +554,19 @@ func (field *Field) setupValuerAndSetter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set
|
// Set
|
||||||
|
if field.Serializer != nil {
|
||||||
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||||
|
if serializer, ok := v.(*Serializer); ok {
|
||||||
|
serializer.Interface.Scan(ctx, field, value, serializer.value)
|
||||||
|
fallbackSetter(ctx, value, serializer.Interface, field.Set)
|
||||||
|
serializer.Interface = reflect.New(reflect.ValueOf(field.Serializer).Type()).Interface().(SerializerInterface)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch field.FieldType.Kind() {
|
switch field.FieldType.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||||
|
@ -3,6 +3,9 @@ package schema
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
@ -19,30 +22,19 @@ type FieldNewValuePool interface {
|
|||||||
Put(interface{})
|
Put(interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type fieldNewValuePool struct {
|
|
||||||
getter func() interface{}
|
|
||||||
putter func(interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fp fieldNewValuePool) Get() interface{} {
|
|
||||||
return fp.getter()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fp fieldNewValuePool) Put(v interface{}) {
|
|
||||||
fp.putter(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serializer field value serializer
|
// Serializer field value serializer
|
||||||
type Serializer struct {
|
type Serializer struct {
|
||||||
Field *Field
|
Field *Field
|
||||||
Interface SerializerInterface
|
Interface SerializerInterface
|
||||||
Destination reflect.Value
|
Destination reflect.Value
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan implements sql.Scanner interface
|
// Scan implements sql.Scanner interface
|
||||||
func (s *Serializer) Scan(value interface{}) error {
|
func (s *Serializer) Scan(value interface{}) error {
|
||||||
return s.Interface.Scan(s.Context, s.Field, s.Destination, value)
|
s.value = value
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value implements driver.Valuer interface
|
// Value implements driver.Valuer interface
|
||||||
@ -56,6 +48,38 @@ type SerializerInterface interface {
|
|||||||
Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error)
|
Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSONSerializer json serializer
|
||||||
|
type JSONSerializer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements serializer interface
|
||||||
|
func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||||
|
fieldValue := reflect.New(field.FieldType)
|
||||||
|
|
||||||
|
if dbValue != nil {
|
||||||
|
var bytes []byte
|
||||||
|
switch v := dbValue.(type) {
|
||||||
|
case []byte:
|
||||||
|
bytes = v
|
||||||
|
case string:
|
||||||
|
bytes = []byte(v)
|
||||||
|
default:
|
||||||
|
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", dbValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(bytes, fieldValue.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements serializer interface
|
||||||
|
func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error) {
|
||||||
|
fv, _ := field.ValueOf(ctx, dst)
|
||||||
|
return fv, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateClausesInterface create clauses interface
|
// CreateClausesInterface create clauses interface
|
||||||
type CreateClausesInterface interface {
|
type CreateClausesInterface interface {
|
||||||
CreateClauses(*Field) []clause.Interface
|
CreateClauses(*Field) []clause.Interface
|
||||||
|
62
schema/pool.go
Normal file
62
schema/pool.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sync pools
|
||||||
|
var (
|
||||||
|
normalPool sync.Map
|
||||||
|
stringPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v string
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
intPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v int64
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
uintPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v uint64
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
floatPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v float64
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
boolPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v bool
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
timePool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
var v time.Time
|
||||||
|
ptrV := &v
|
||||||
|
return &ptrV
|
||||||
|
},
|
||||||
|
}
|
||||||
|
poolInitializer = func(reflectType reflect.Type) FieldNewValuePool {
|
||||||
|
v, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return reflect.New(reflectType).Interface()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return v.(FieldNewValuePool)
|
||||||
|
}
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user