fix: circular reference save

This commit is contained in:
chenrui 2022-03-08 17:22:33 +08:00
parent b566ed7913
commit 326862f3f8
4 changed files with 62 additions and 1 deletions

View File

@ -1,6 +1,7 @@
package callbacks package callbacks
import ( import (
"fmt"
"reflect" "reflect"
"strings" "strings"
@ -348,6 +349,13 @@ func saveAssociations(db *gorm.DB, rel *schema.Relationship, values interface{},
refName = rel.Name + "." refName = rel.Name + "."
) )
// stop save association loop
savedRelKey := fmt.Sprintf("gorm:saved_relation_%s", rel.Name)
if _, ok := db.Get(savedRelKey); ok {
return nil
}
db.Set(savedRelKey, true)
for name, ok := range selectColumns { for name, ok := range selectColumns {
columnName := "" columnName := ""
if strings.HasPrefix(name, refName) { if strings.HasPrefix(name, refName) {

View File

@ -220,3 +220,43 @@ func TestFullSaveAssociations(t *testing.T) {
t.Errorf("Failed to preload AppliesToProduct") t.Errorf("Failed to preload AppliesToProduct")
} }
} }
func TestSaveBelongsCircularReference(t *testing.T) {
parent := Parent{}
DB.Create(&parent)
child := Child{ParentID: &parent.ID, Parent: &parent}
DB.Create(&child)
parent.FavChildID = child.ID
parent.FavChild = &child
DB.Save(&parent)
var parent1 Parent
DB.First(&parent1, parent.ID)
AssertObjEqual(t, parent, parent1, "ID", "FavChildID")
DB.Updates(&parent)
DB.First(&parent1, parent.ID)
AssertObjEqual(t, parent, parent1, "ID", "FavChildID")
}
func TestSaveHasManyCircularReference(t *testing.T) {
parent := Parent{}
DB.Create(&parent)
child := Child{ParentID: &parent.ID, Parent: &parent, Name: "HasManyCircularReference"}
child1 := Child{ParentID: &parent.ID, Parent: &parent, Name: "HasManyCircularReference1"}
parent.Children = []*Child{&child, &child1}
DB.Save(&parent)
var children []*Child
DB.Where("parent_id = ?", parent.ID).Find(&children)
if len(children) != len(parent.Children) ||
children[0].ID != parent.Children[0].ID ||
children[1].ID != parent.Children[1].ID {
t.Errorf("circular reference children save not equal children:%v parent.Children:%v",
children, parent.Children)
}
}

View File

@ -95,7 +95,7 @@ func OpenTestConnection() (db *gorm.DB, err error) {
func RunMigrations() { func RunMigrations() {
var err error var err error
allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}} allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}}
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })

View File

@ -80,3 +80,16 @@ type Order struct {
Coupon *Coupon Coupon *Coupon
CouponID string CouponID string
} }
type Parent struct {
gorm.Model
FavChildID uint
FavChild *Child
Children []*Child
}
type Child struct {
gorm.Model
Name string
ParentID *uint
Parent *Parent
}