Use JoinTable instead of ForeignKey

This commit is contained in:
Jinzhu 2014-07-30 11:32:18 +08:00
parent d7400c2df4
commit a7aaf151cf
4 changed files with 63 additions and 26 deletions

View File

@ -1,6 +1,9 @@
package gorm package gorm
import "reflect" import (
"fmt"
"reflect"
)
func BeginTransaction(scope *Scope) { func BeginTransaction(scope *Scope) {
scope.Begin() scope.Begin()
@ -28,8 +31,8 @@ func SaveBeforeAssociations(scope *Scope) {
scope.SetColumn(field.Name, value.Interface()) scope.SetColumn(field.Name, value.Interface())
} }
if len(field.ForeignKey) > 0 { if field.JoinTable != nil && field.JoinTable.foreignKey != "" {
scope.SetColumn(field.ForeignKey, newDB.NewScope(value.Interface()).PrimaryKeyValue()) scope.SetColumn(field.JoinTable.foreignKey, newDB.NewScope(value.Interface()).PrimaryKeyValue())
} }
} }
} }
@ -46,16 +49,19 @@ func SaveAfterAssociations(scope *Scope) {
newDB := scope.NewDB() newDB := scope.NewDB()
elem := value.Index(i).Addr().Interface() elem := value.Index(i).Addr().Interface()
if len(field.ForeignKey) > 0 { if field.JoinTable != nil && field.JoinTable.foreignKey != "" {
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue()) newDB.NewScope(elem).SetColumn(field.JoinTable.foreignKey, scope.PrimaryKeyValue())
} }
scope.Err(newDB.Save(elem).Error) scope.Err(newDB.Save(elem).Error)
fmt.Sprintf("INSERT INTO %v (%v, %v) SELECT (%v, %v) FROM %v WHERE NOT EXISTS (SELECT * FROM %v WHERE %v = %v AND %v = %v) limit 1;")
} }
default: default:
newDB := scope.NewDB() newDB := scope.NewDB()
if value.CanAddr() { if value.CanAddr() {
newDB.NewScope(field.Value).SetColumn(field.ForeignKey, scope.PrimaryKeyValue()) if field.JoinTable != nil {
newDB.NewScope(field.Value).SetColumn(field.JoinTable.foreignKey, scope.PrimaryKeyValue())
}
scope.Err(newDB.Save(field.Value).Error) scope.Err(newDB.Save(field.Value).Error)
} else { } else {
destValue := reflect.New(reflect.TypeOf(field.Value)).Elem() destValue := reflect.New(reflect.TypeOf(field.Value)).Elem()
@ -65,7 +71,9 @@ func SaveAfterAssociations(scope *Scope) {
} }
elem := destValue.Addr().Interface() elem := destValue.Addr().Interface()
newDB.NewScope(elem).SetColumn(field.ForeignKey, scope.PrimaryKeyValue()) if field.JoinTable != nil {
newDB.NewScope(elem).SetColumn(field.JoinTable.foreignKey, scope.PrimaryKeyValue())
}
scope.Err(newDB.Save(elem).Error) scope.Err(newDB.Save(elem).Error)
scope.SetColumn(field.Name, destValue.Interface()) scope.SetColumn(field.Name, destValue.Interface())
} }

View File

@ -6,6 +6,12 @@ import (
"time" "time"
) )
type joinTable struct {
joinTable string
foreignKey string
associationForeignKey string
}
type Field struct { type Field struct {
Name string Name string
DBName string DBName string
@ -14,10 +20,10 @@ type Field struct {
IsIgnored bool IsIgnored bool
Tag reflect.StructTag Tag reflect.StructTag
SqlTag string SqlTag string
ForeignKey string
BeforeAssociation bool BeforeAssociation bool
AfterAssociation bool AfterAssociation bool
isPrimaryKey bool isPrimaryKey bool
JoinTable *joinTable
} }
func (f *Field) IsScanner() bool { func (f *Field) IsScanner() bool {

View File

@ -115,8 +115,8 @@ func TestRelated(t *testing.T) {
var creditcard CreditCard var creditcard CreditCard
var user3 User var user3 User
db.First(&creditcard, "number = ?", "1234567890") db.Debug().First(&creditcard, "number = ?", "1234567890")
db.Model(&creditcard).Related(&user3) db.Debug().Model(&creditcard).Related(&user3)
if user3.Id != user.Id || user3.Name != user.Name { if user3.Id != user.Id || user3.Name != user.Name {
t.Errorf("Should get user from credit card correctly") t.Errorf("Should get user from credit card correctly")
} }
@ -131,17 +131,17 @@ func TestQueryManyToManyWithRelated(t *testing.T) {
user := User{Name: "Many2Many", Languages: languages} user := User{Name: "Many2Many", Languages: languages}
db.Save(&user) db.Save(&user)
// var newLanguages []Language var newLanguages []Language
// db.Model(&user).Related(&newLanguages, "Languages") // db.Model(&user).Related(&newLanguages, "Languages")
// if len(newLanguages) != 3 { // if len(newLanguages) != 3 {
// t.Errorf("Query many to many relations") // t.Errorf("Query many to many relations")
// } // }
// newLanguages = []Language{} newLanguages = []Language{}
// db.Model(&user).Many2Many("Languages").Find(&newLanguages) db.Model(&user).Many2Many("Languages").Find(&newLanguages)
// if len(newLanguages) != 3 { if len(newLanguages) != 3 {
// t.Errorf("Query many to many relations") t.Errorf("Query many to many relations")
// } }
// db.Model(&User{}).Many2Many("Languages").Add(&Language{}) // db.Model(&User{}).Many2Many("Languages").Add(&Language{})
// db.Model(&User{}).Many2Many("Languages").Remove(&Language{}) // db.Model(&User{}).Many2Many("Languages").Remove(&Language{})

View File

@ -263,33 +263,56 @@ func (scope *Scope) Fields() []*Field {
} }
if scope.db != nil { if scope.db != nil {
indirectValue := reflect.Indirect(value)
field.Tag = fieldStruct.Tag field.Tag = fieldStruct.Tag
field.SqlTag = scope.sqlTagForField(&field) field.SqlTag = scope.sqlTagForField(&field)
// parse association // parse association
elem := reflect.Indirect(value) typ := indirectValue.Type()
typ := elem.Type() foreignKey := settings["FOREIGNKEY"]
associationForeignKey := settings["ASSOCIATIONFOREIGNKEY"]
many2many := settings["MANY2MANY"]
switch elem.Kind() { switch indirectValue.Kind() {
case reflect.Slice: case reflect.Slice:
typ = typ.Elem() typ = typ.Elem()
if typ.Kind() == reflect.Struct { if typ.Kind() == reflect.Struct {
foreignKey := scopeTyp.Name() + "Id" if foreignKey == "" {
if reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() { foreignKey = scopeTyp.Name() + "Id"
field.ForeignKey = foreignKey
} }
if associationForeignKey == "" {
associationForeignKey = typ.Name() + "Id"
}
// if not many to many, foreign key could be null
if many2many == "" {
if !reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() {
foreignKey = ""
}
}
field.AfterAssociation = true field.AfterAssociation = true
field.JoinTable = &joinTable{
joinTable: many2many,
foreignKey: foreignKey,
associationForeignKey: associationForeignKey,
}
} }
case reflect.Struct: case reflect.Struct:
if !field.IsTime() && !field.IsScanner() { if !field.IsTime() && !field.IsScanner() {
if scope.HasColumn(field.Name + "Id") { if foreignKey == "" && scope.HasColumn(field.Name+"Id") {
field.ForeignKey = field.Name + "Id" field.JoinTable = &joinTable{foreignKey: field.Name + "Id"}
field.BeforeAssociation = true
} else if scope.HasColumn(foreignKey) {
field.JoinTable = &joinTable{foreignKey: foreignKey}
field.BeforeAssociation = true field.BeforeAssociation = true
} else { } else {
foreignKey := scopeTyp.Name() + "Id" if foreignKey == "" {
foreignKey = scopeTyp.Name() + "Id"
}
if reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() { if reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() {
field.ForeignKey = foreignKey field.JoinTable = &joinTable{foreignKey: foreignKey}
} }
field.AfterAssociation = true field.AfterAssociation = true
} }