GORM Playground Link
https://github.com/go-gorm/playground/pull/575
Description
The sub-table structure is associated with the parent table using the gorm:"foreignKey:CDatasetID;references:ID" tag.
子表结构体在父表中使用
gorm:"foreignKey:CDatasetID;references:ID"关联
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
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.