2
votes

Comment insérer un nouvel enregistrement dans la base de données à l'aide de plugins / hooks Gorm

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.


0 commentaires

3 Réponses :


0
votes

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
    })
}


2 commentaires

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



-1
votes

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


1 commentaires

Hé merci, mais ce ne sera pas transactionnel, si l'enregistrement du journal échoue, l'enregistrement du modèle ne sera pas annulé



1
votes

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!


0 commentaires