This commit is contained in:
Lunny Xiao 2021-07-22 13:22:35 +08:00
parent dca8a72b64
commit b882579026
3 changed files with 157 additions and 67 deletions

View File

@ -10,7 +10,6 @@ import (
"crypto/sha256" "crypto/sha256"
"database/sql" "database/sql"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"hash/crc32" "hash/crc32"
"io" "io"
@ -631,9 +630,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
} }
fieldValue.Set(reflect.ValueOf(v).Elem().Convert(fieldType)) fieldValue.Set(reflect.ValueOf(v).Elem().Convert(fieldType))
return nil return nil
} } else if fieldType.ConvertibleTo(schemas.TimeType) {
if fieldType.ConvertibleTo(schemas.TimeType) {
dbTZ := session.engine.DatabaseTZ dbTZ := session.engine.DatabaseTZ
if col.TimeZone != nil { if col.TimeZone != nil {
dbTZ = col.TimeZone dbTZ = col.TimeZone
@ -647,42 +644,35 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
fieldValue.Set(reflect.ValueOf(*t).Convert(fieldType)) fieldValue.Set(reflect.ValueOf(*t).Convert(fieldType))
return nil return nil
} else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
err := nulVal.Scan(scanResult) return nulVal.Scan(scanResult)
if err == nil { } else if session.cascadeLevel > 0 && ((col.AssociateType == schemas.AssociateNone &&
return nil session.cascadeMode == cascadeCompitable) ||
} (col.AssociateType == schemas.AssociateBelongsTo &&
session.engine.logger.Errorf("sql.Sanner error: %v", err) session.cascadeMode == cascadeEager)) {
} else if session.statement.UseCascade { var pk = make(schemas.PK, len(col.AssociateTable.PrimaryKeys))
table, err := session.engine.tagParser.ParseWithCache(*fieldValue) var err error
if err != nil {
return err
}
if len(table.PrimaryKeys) != 1 {
return errors.New("unsupported non or composited primary key cascade")
}
var pk = make(schemas.PK, len(table.PrimaryKeys))
pk[0], err = asKind(vv, reflect.TypeOf(scanResult)) pk[0], err = asKind(vv, reflect.TypeOf(scanResult))
if err != nil { if err != nil {
return err return err
} }
if !pk.IsZero() { session.afterProcessors = append(session.afterProcessors, executedProcessor{
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch fun: func(session *Session, bean interface{}) error {
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne fieldValue := bean.(*reflect.Value)
// property to be fetched lazily return session.getStructByPK(pk, fieldValue)
structInter := reflect.New(fieldValue.Type()) },
has, err := session.ID(pk).NoCascade().get(structInter.Interface()) session: session,
if err != nil { bean: fieldValue,
return err })
} session.cascadeLevel--
if has {
fieldValue.Set(structInter.Elem())
} else {
return errors.New("cascade obj is not exist")
}
}
return nil return nil
} else if col.AssociateType == schemas.AssociateBelongsTo {
pkCols := col.AssociateTable.PKColumns()
colV, err := pkCols[0].ValueOfV(fieldValue)
if err != nil {
return err
}
return convertAssignV(*colV, scanResult)
} }
} // switch fieldType.Kind() } // switch fieldType.Kind()

View File

@ -19,11 +19,13 @@ func (session *Session) Load(beanOrSlices interface{}, cols ...string) error {
v = v.Elem() v = v.Elem()
} }
if v.Kind() == reflect.Slice { if v.Kind() == reflect.Slice {
return session.loadFind(beanOrSlices, cols...) return session.loadFindSlice(v, cols...)
} else if v.Kind() == reflect.Map {
return session.loadFindMap(v, cols...)
} else if v.Kind() == reflect.Struct { } else if v.Kind() == reflect.Struct {
return session.loadGet(beanOrSlices, cols...) return session.loadGet(v, cols...)
} }
return errors.New("unsupported load type, must struct or slice") return errors.New("unsupported load type, must struct, slice or map")
} }
func isStringInSlice(s string, slice []string) bool { func isStringInSlice(s string, slice []string) bool {
@ -36,11 +38,7 @@ func isStringInSlice(s string, slice []string) bool {
} }
// loadFind load 's belongs to tag field immedicatlly // loadFind load 's belongs to tag field immedicatlly
func (session *Session) loadFind(slices interface{}, cols ...string) error { func (session *Session) loadFindSlice(v reflect.Value, cols ...string) error {
v := reflect.ValueOf(slices)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Slice { if v.Kind() != reflect.Slice {
return errors.New("only slice is supported") return errors.New("only slice is supported")
} }
@ -100,13 +98,73 @@ func (session *Session) loadFind(slices interface{}, cols ...string) error {
return nil return nil
} }
// loadFindMap load 's belongs to tag field immedicatlly
func (session *Session) loadFindMap(v reflect.Value, cols ...string) error {
if v.Kind() != reflect.Map {
return errors.New("only map is supported")
}
if v.Len() <= 0 {
return nil
}
vv := v.Index(0)
if vv.Kind() == reflect.Ptr {
vv = vv.Elem()
}
tb, err := session.engine.tagParser.ParseWithCache(vv)
if err != nil {
return err
}
var pks = make(map[*schemas.Column][]interface{})
for i := 0; i < v.Len(); i++ {
ev := v.Index(i)
for _, col := range tb.Columns() {
if len(cols) > 0 && !isStringInSlice(col.Name, cols) {
continue
}
if col.AssociateTable != nil {
if col.AssociateType == schemas.AssociateBelongsTo {
colV, err := col.ValueOfV(&ev)
if err != nil {
return err
}
vv := colV.Interface()
/*var colPtr reflect.Value
if colV.Kind() == reflect.Ptr {
colPtr = *colV
} else {
colPtr = colV.Addr()
}*/
if !utils.IsZero(vv) {
pks[col] = append(pks[col], vv)
}
}
}
}
}
for col, pk := range pks {
slice := reflect.MakeSlice(col.FieldType, 0, len(pk))
err = session.In(col.Name, pk...).find(slice.Addr().Interface())
if err != nil {
return err
}
}
return nil
}
// loadGet load bean's belongs to tag field immedicatlly // loadGet load bean's belongs to tag field immedicatlly
func (session *Session) loadGet(bean interface{}, cols ...string) error { func (session *Session) loadGet(v reflect.Value, cols ...string) error {
if session.isAutoClose { if session.isAutoClose {
defer session.Close() defer session.Close()
} }
v := reflect.Indirect(reflect.ValueOf(bean))
tb, err := session.engine.tagParser.ParseWithCache(v) tb, err := session.engine.tagParser.ParseWithCache(v)
if err != nil { if err != nil {
return err return err
@ -117,32 +175,38 @@ func (session *Session) loadGet(bean interface{}, cols ...string) error {
continue continue
} }
if col.AssociateTable != nil { if col.AssociateTable == nil || col.AssociateType != schemas.AssociateBelongsTo {
if col.AssociateType == schemas.AssociateBelongsTo { continue
colV, err := col.ValueOfV(&v) }
if err != nil {
return err
}
vv := colV.Interface() colV, err := col.ValueOfV(&v)
var colPtr reflect.Value if err != nil {
if colV.Kind() == reflect.Ptr { return err
colPtr = *colV }
} else {
colPtr = colV.Addr()
}
if !utils.IsZero(vv) && session.cascadeLevel > 0 { var colPtr reflect.Value
has, err := session.ID(vv).NoAutoCondition().get(colPtr.Interface()) if colV.Kind() == reflect.Ptr {
if err != nil { colPtr = *colV
return err } else {
} colPtr = colV.Addr()
if !has { }
return errors.New("load bean does not exist")
} pks := col.AssociateTable.PKColumns()
session.cascadeLevel-- pkV, err := pks[0].ValueOfV(colV)
} if err != nil {
return err
}
vv := pkV.Interface()
if !utils.IsZero(vv) && session.cascadeLevel > 0 {
has, err := session.ID(vv).NoAutoCondition().get(colPtr.Interface())
if err != nil {
return err
} }
if !has {
return errors.New("load bean does not exist")
}
session.cascadeLevel--
} }
} }
return nil return nil

View File

@ -280,6 +280,42 @@ func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields
} }
} }
func (session *Session) getStructByPK(pk schemas.PK, fieldValue *reflect.Value) error {
if pk.IsZero() {
return errors.New("getStructByPK: primary key is zero")
}
var structInter reflect.Value
if fieldValue.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
structInter = reflect.New(fieldValue.Type().Elem())
} else {
structInter = *fieldValue
}
} else {
structInter = fieldValue.Addr()
}
has, err := session.ID(pk).NoAutoCondition().get(structInter.Interface())
if err != nil {
return err
}
if !has {
return errors.New("cascade obj is not exist")
}
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
fieldValue.Set(structInter)
fmt.Println("getByPK value ptr:", fieldValue.Interface())
return nil
} else if fieldValue.Kind() == reflect.Struct {
fieldValue.Set(structInter.Elem())
fmt.Println("getByPK value:", fieldValue.Interface())
return nil
}
return errors.New("set value failed")
}
func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
// if has no reftable, then don't use cache currently // if has no reftable, then don't use cache currently
if !session.canCache() { if !session.canCache() {