GORM Playground Link

https://github.com/go-gorm/playground/pull/575

Description

Gorm v1.24.6 Duplicate saving of sub-table data. v1.24.6 重复保存子表数据

The sub-table structure is associated with the parent table using the gorm:"foreignKey:CDatasetID;references:ID" tag.

子表结构体在父表中使用 gorm:"foreignKey:CDatasetID;references:ID" 关联

Gorm v1.24.6 Duplicate saving of sub-table data. v1.24.6 重复保存子表数据

When executing updateTx := tx.callbacks.Update().Execute(tx.Session(&Session{Initialized: true})), the sub-table data has already been saved once, but tx.Create(value) is still executed, resulting in duplicate saving.

执行 updateTx := tx.callbacks.Update().Execute(tx.Session(&Session{Initialized: true})) 时已经保存了一次,但还是会执行到 tx.Create(value),导致重复保存。

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: iTanken

Gorm v1.24.6 Duplicate saving of sub-table data. v1.24.6 重复保存子表数据

tx.Create(value) is also executed in v1.24.5, but the sub-table data is not saved again.

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: black-06

Would you mind giving an example? It works in my case

type Dataset struct {
    ID     int
    Name   string
    Others []Other `gorm:"foreignKey:OID;references:ID"`
}

type Other struct {
    OID  int
    Name string
}

DB.Save(&Dataset{
    Name: "SA",
    Others: []Other{
        {Name: "name_1"},
        {Name: "name_2"},
    },
})

// INSERT INTO `others` (`o_id`,`name`) VALUES (1,'name_1'),(1,'name_2') ON DUPLICATE KEY UPDATE `o_id`=VALUES(`o_id`)
// INSERT INTO `datasets` (`name`) VALUES ('SA')

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: iTanken

@black-06 Both BeforeSave and BeforeUpdate Hook methods are added. Delete the old sub-data in the BeforeUpdate Hook method. This problem occurs when use Save when adding and modifying.

Once Save is split into Create and Updates, can prevent duplicate saves.

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: black-06

Sorry, I still don't get it. Please show me the code.

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: iTanken

@black-06 https://github.com/go-gorm/playground/pull/575

The issue seems to be related to the data type of the ID, where using the default gorm.Model works fine but using a custom string type ID may lead to this problem.

Comment From: github-actions[bot]

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ Search Before Asking

Comment From: black-06

Save is a combination function. If save value does not contain primary key, it will execute Create, otherwise it will execute Update.

In your case, ExampleRoot.ID is not empty so it exec Update, it should get error "foreign key constraint failed" (other databases). But SQLite will skip foreign_keys check unless PRAGMA foreign_keys = ON is executed. https://github.com/go-gorm/gorm/blob/85eaf9eeda11e4c4c9aa24bf660325e364ca6e6b/finisher_api.go#L104-L108 updateTx.Error == nil then GROM do create, I don't think this is a bug.

You should: 1. Exec PRAGMA foreign_keys = ON In SQLite (to raise the error) 2. Or as you said, use Create and Update instead. 3. Or keep foreignKey empty.

func (root *ExampleRoot) BeforeCreate(tx *gorm.DB) (err error) {
    root.ID = GenIDStr()
    return nil
}

root := ExampleRoot{
    CTitle:          "root-1",
    CPinyinCode:     "ROOT-1",
    ExampleBranches: branches,
}
// don't gen id, do it in hook
// make sure foreignKey is empty (0 for int type, "" for string type)
db.Save(&root)

Comment From: iTanken

@black-06 Wow, thank you very much for your answer. It worked for me to generate the ID in the BeforeCreate hook, and this way requires the least amount of code changes.