diff --git a/blongs_to_test.go b/blongs_to_test.go index 9ab2faa0..8a0cbfcb 100644 --- a/blongs_to_test.go +++ b/blongs_to_test.go @@ -1,3 +1,7 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package xorm import ( diff --git a/engine.go b/engine.go index c3e0d8d0..1c0efc51 100644 --- a/engine.go +++ b/engine.go @@ -931,6 +931,7 @@ func (engine *Engine) mapType(parsingTables map[reflect.Type]*core.Table, v refl if ormTagStr != "" { col = &core.Column{ FieldName: t.Field(i).Name, + FieldType: t.Field(i).Type, Nullable: true, IsPrimaryKey: false, IsAutoIncrement: false, @@ -1044,6 +1045,7 @@ func (engine *Engine) mapType(parsingTables map[reflect.Type]*core.Table, v refl col = core.NewColumn( engine.ColumnMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, + sqlType, sqlType.DefaultLength, sqlType.DefaultLength2, @@ -1054,6 +1056,7 @@ func (engine *Engine) mapType(parsingTables map[reflect.Type]*core.Table, v refl idFieldColName = col.Name } + col.FieldType = t.Field(i).Type ctx.col = col } if col.IsAutoIncrement { diff --git a/session.go b/session.go index d7677163..830011bb 100644 --- a/session.go +++ b/session.go @@ -149,7 +149,7 @@ func (session *Session) Alias(alias string) *Session { // NoCascade indicate that no cascade load child object func (session *Session) NoCascade() *Session { - session.statement.cascadeMode = cascadeManuallyLoad + session.statement.cascadeMode = cascadeManually return session } @@ -204,12 +204,12 @@ func (session *Session) Charset(charset string) *Session { // Cascade indicates if loading sub Struct func (session *Session) Cascade(trueOrFalse ...bool) *Session { - var mode = cascadeAutoLoad + var mode = cascadeAuto if len(trueOrFalse) >= 1 { if trueOrFalse[0] { - mode = cascadeAutoLoad + mode = cascadeAuto } else { - mode = cascadeManuallyLoad + mode = cascadeManually } } @@ -447,8 +447,8 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b continue } - rawValueType := reflect.TypeOf(rawValue.Interface()) vv := reflect.ValueOf(rawValue.Interface()) + rawValueType := vv.Type() col := table.GetColumnIdx(key, idx) if col.IsPrimaryKey { pk = append(pk, rawValue.Interface()) @@ -639,35 +639,25 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } else if (col.AssociateType == core.AssociateNone && session.statement.cascadeMode == cascadeCompitable) || (col.AssociateType == core.AssociateBelongsTo && - session.statement.cascadeMode == cascadeAutoLoad) { - table := col.AssociateTable - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - return nil, errors.New("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) + session.statement.cascadeMode == cascadeAuto) { + var pk = make(core.PK, len(col.AssociateTable.PrimaryKeys)) var err error - pk[0], err = asKind(vv, rawValueType) + rawValueType := col.AssociateTable.PKColumns()[0].FieldType + if rawValueType.Kind() == reflect.Ptr { + pk[0] = reflect.New(rawValueType.Elem()).Interface() + } else { + pk[0] = reflect.New(rawValueType).Interface() + } + err = convertAssign(pk[0], vv.Interface()) if err != nil { return nil, err } - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - fieldValue.Set(structInter.Elem()) - } else { - return nil, errors.New("cascade obj is not exist") - } + pk[0] = reflect.ValueOf(pk[0]).Elem().Interface() + if err = session.getByPK(pk, fieldValue); err != nil { + return nil, err } + hasAssigned = true } else if col.AssociateType == core.AssociateBelongsTo { hasAssigned = true err := convertAssign(fieldValue.FieldByName(table.PKColumns()[0].FieldName).Addr().Interface(), @@ -681,47 +671,25 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b if (col.AssociateType == core.AssociateNone && session.statement.cascadeMode == cascadeCompitable) || (col.AssociateType == core.AssociateBelongsTo && - session.statement.cascadeMode == cascadeAutoLoad) { - table := col.AssociateTable - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - panic("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) + session.statement.cascadeMode == cascadeAuto) { + var pk = make(core.PK, len(col.AssociateTable.PrimaryKeys)) var err error - pk[0], err = asKind(vv, rawValueType) + rawValueType := col.AssociateTable.ColumnType(col.AssociateTable.PKColumns()[0].FieldName) + if rawValueType.Kind() == reflect.Ptr { + pk[0] = reflect.New(rawValueType.Elem()).Interface() + } else { + pk[0] = reflect.New(rawValueType).Interface() + } + err = convertAssign(pk[0], vv.Interface()) if err != nil { return nil, err } - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - 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).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { - fieldValue.Set(structInter) - } - } else { - return nil, errors.New("cascade obj is not exist") - } + pk[0] = reflect.ValueOf(pk[0]).Elem().Interface() + if err = session.getByPK(pk, fieldValue); err != nil { + return nil, err } + hasAssigned = true } else if col.AssociateType == core.AssociateBelongsTo { hasAssigned = true if fieldValue.IsNil() { @@ -873,6 +841,34 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b return pk, nil } +func (session *Session) getByPK(pk core.PK, fieldValue *reflect.Value) error { + if !isPKZero(pk) { + 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).NoCascade().get(structInter.Interface()) + if err != nil { + return err + } + if has { + if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + fieldValue.Set(structInter) + } + } else { + return errors.New("cascade obj is not exist") + } + } + return nil +} + // saveLastSQL stores executed query information func (session *Session) saveLastSQL(sql string, args ...interface{}) { session.lastSQL = sql diff --git a/session_convert.go b/session_convert.go index 1e6355c3..e69788b8 100644 --- a/session_convert.go +++ b/session_convert.go @@ -8,7 +8,6 @@ import ( "database/sql" "database/sql/driver" "encoding/json" - "errors" "fmt" "reflect" "strconv" @@ -206,37 +205,18 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } else if (col.AssociateType == core.AssociateNone && session.statement.cascadeMode == cascadeCompitable) || (col.AssociateType == core.AssociateBelongsTo && - session.statement.cascadeMode == cascadeAutoLoad) { - table := col.AssociateTable - - // TODO: current only support 1 primary key - if len(table.PrimaryKeys) > 1 { - return errors.New("unsupported composited primary key cascade") - } - - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + session.statement.cascadeMode == cascadeAuto) { + var pk = make(core.PK, len(col.AssociateTable.PrimaryKeys)) + // only 1 PK checked on tag parsing + rawValueType := col.AssociateTable.ColumnType(col.AssociateTable.PKColumns()[0].FieldName) var err error pk[0], err = str2PK(string(data), rawValueType) if err != nil { return err } - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Elem().Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist") - } + if err = session.getByPK(pk, fieldValue); err != nil { + return err } } } @@ -489,35 +469,18 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, if (col.AssociateType == core.AssociateNone && session.statement.cascadeMode == cascadeCompitable) || (col.AssociateType == core.AssociateBelongsTo && - session.statement.cascadeMode == cascadeAutoLoad) { - table := col.AssociateTable - if len(table.PrimaryKeys) > 1 { - return errors.New("unsupported composited primary key cascade") - } - - var pk = make(core.PK, len(table.PrimaryKeys)) + session.statement.cascadeMode == cascadeAuto) { + var pk = make(core.PK, len(col.AssociateTable.PrimaryKeys)) var err error - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + // only 1 PK checked on tag parsing + rawValueType := col.AssociateTable.ColumnType(col.AssociateTable.PKColumns()[0].FieldName) pk[0], err = str2PK(string(data), rawValueType) if err != nil { return err } - if !isPKZero(pk) { - structInter := reflect.New(fieldType.Elem()) - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist") - } + if err = session.getByPK(pk, fieldValue); err != nil { + return err } } } diff --git a/session_find.go b/session_find.go index f95dcfef..ff39e87b 100644 --- a/session_find.go +++ b/session_find.go @@ -172,9 +172,8 @@ func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Va var newElemFunc func(fields []string) reflect.Value elemType := containerValue.Type().Elem() - var isPointer bool - if elemType.Kind() == reflect.Ptr { - isPointer = true + var isPointer = elemType.Kind() == reflect.Ptr + if isPointer { elemType = elemType.Elem() } if elemType.Kind() == reflect.Ptr { diff --git a/statement.go b/statement.go index 4f4631ea..de8a071c 100644 --- a/statement.go +++ b/statement.go @@ -37,8 +37,8 @@ type cascadeMode int const ( cascadeCompitable cascadeMode = iota - cascadeAutoLoad - cascadeManuallyLoad + cascadeAuto + cascadeManually ) // Statement save all the sql info for executing SQL