En essayant d'insérer des journaux pour détecter les changements de valeurs de mes modèles en utilisant gorm dans golang, je le fais en utilisant des plugins:
func ChangelogCreatePlugin(db *gorm.DB) { log := &Log{NewValue: "the new value", OldValue: "the old value", CreatedAt: "current time"} // Here is the problem db.Save(log) <- this is not acceptable }
Ma définition de plugin est quelque chose comme ceci:
type MyModel struct { Id Name } type Log struct { Id NewValue OldValue CreatedAt }
L'insertion d'un modèle de données différent à l'aide de l'argument db *gorm.DB
dans le plugin n'est pas acceptable car cet argument est initialisé pour accepter les données du même modèle qui a déclenché le plugin.
Mon exigence est de stocker mon journal dans la même transaction db, donc si l'un d'eux échoue, ils doivent tous les deux échouer. Comment faire une telle chose dans gorm?
Mise à jour:
Je connais les crochets. Les hooks ne sont pas utiles dans mon cas car je veux que mes logs suivent différents modèles, donc je recherche une solution plus "réutilisable", plutôt que de copier / coller des implémentations de hooks dans mes modèles.
3 Réponses :
Essayez de créer un decorator
transactionnel en utilisant func(d *gorm.DB)
ou f ...func(d *gorm.DB)
comme argument. Cela devrait ressembler à
repo , _ := gorm.Open(...) tdb := &TransactionalDecorator{DB: repo} saveModel := func(d *gorm.DB) error { // save model } saveLog := func(d *gorm.DB) error { // save log } err := tdb.Transaction(saveModel, saveLog)
Toutes les fonctions d'interaction de base de données seront exécutées dans la même transaction
type TransactionalDecorator struct { DB *gorm.DB } func (t *TransactionalDecorator) Transaction(f ...func(d *gorm.DB) error) error { return t.DB.Transaction(func(tx *gorm.DB) error { for _, fn := range f { if err := fn(tx); err != nil { return err } } return nil }) }
Hé, merci pour ta réponse. Cependant, je ne veux pas que ma solution me force à faire des changements dans ma base de code existante, car cette bibliothèque devrait fonctionner avec différents dépôts en n'implémentant que des modifications mineures. C'est pourquoi j'ai choisi des plugins, ou j'ai pensé aux hooks depuis le début.
@MAZux - Ajoutez quelques exemples d'utilisation du plugin
Que diriez-vous:
func ChangelogCreatePlugin(db *gorm.DB) { log := &Log{NewValue: "the new value", OldValue: "the old value", CreatedAt: "current time"} // Here is the problem db.Session(&gorm.Session{}).Save(log) }
Cela devrait vous donner une base de données / tx "fraîche" avec laquelle travailler
Hé merci, mais ce ne sera pas transactionnel, si l'enregistrement du journal échoue, l'enregistrement du modèle ne sera pas annulé
Après de nombreuses recherches et débogages, j'ai trouvé cette solution:
Vous devez d'abord enregistrer l'exécution de votre plugin dans le bon ordre, dans mon cas, cela devrait être quelque chose comme ceci:
func ChangelogCreatePlugin(db *gorm.DB) { // first make a check that the model insert transaction doesn't have any error if db.Error != nil { return } log := &Log{NewValue: "the new value", OldValue: "the old value", CreatedAt: "current time"} // get a new db session for the new model to work logDb := db.Session(&gorm.Session{}) // if an error ocurred while saving the log // push it into original model db instance errors, so it will be rolledback eventually logErr := logDb.Save(log) if logErr != nil { db.AddError(logErr) } }
Cette commande garantira que votre plugin sera activé ou déclenché avant la validation de transaction de toute clause d'insertion de vos modèles.
Pour avoir plus d'informations sur les endroits où vous pouvez enregistrer vos plugins, jetez un œil aux rappels par défaut enregistrés dans gorm
Après cela, j'ai dû ajuster le code du plugin:
gormDb.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("changelog_create", ChangelogCreatePlugin)
J'espère que cela aidera quelqu'un un jour!