Add Serializer Scan support

This commit is contained in:
Jinzhu 2022-02-15 20:14:57 +08:00
parent 6393ad5222
commit bf85076f0e
4 changed files with 138 additions and 76 deletions

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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
View 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)
}
)