ent 基本使用十九 事务处理

ent 生成的代码中client 提供了比较全的事务处理

启动单个事务进行处理

// GenTx generates group of entities in a transaction.
func GenTx(ctx context.Context, client *ent.Client) error {
    tx, err := client.Tx(ctx)
    if err != nil {
        return fmt.Errorf("starting a transaction: %v", err)
    }
    hub, err := tx.Group.
        Create().
        SetName("Github").
        Save(ctx)
    if err != nil {
        return rollback(tx, fmt.Errorf("failed creating the group: %v", err))
    }
    // Create the admin of the group.
    dan, err := tx.User.
        Create().
        SetAge(29).
        SetName("Dan").
        AddManage(hub).
        Save(ctx)
    if err != nil {
        return rollback(tx, err)
    }
    // Create user "Ariel".
    a8m, err := tx.User.
        Create().
        SetAge(30).
        SetName("Ariel").
        AddGroups(hub).
        AddFriends(dan).
        Save(ctx)
    if err != nil {
        return rollback(tx, err)
    }
    fmt.Println(a8m)
    // Output:
    // User(id=2, age=30, name=Ariel)
    // Commit the transaction.
    return tx.Commit()
}
// rollback calls to tx.Rollback and wraps the given error
// with the rollback error if occurred.
func rollback(tx *ent.Tx, err error) error {
    if rerr := tx.Rollback(); rerr != nil {
        err = fmt.Errorf("%v: %v", err, rerr)
    }
    return err
}
 
 

事务client 封装

有时已经使用了 *ent.Client 但是我们需要支持事务,就可以使用次方法

// WrapGen wraps the existing "Gen" function in a transaction.
func WrapGen(ctx context.Context, client *ent.Client) error {
    tx, err := client.Tx(ctx)
    if err != nil {
        return err
    }
    txClient := tx.Client()
    // Use the "Gen" below, but give it the transactional client; no code changes to "Gen".
    if err := Gen(ctx, txClient); err != nil {
        return rollback(tx, err)
    }
    return tx.Commit()
}
// Gen generates a group of entities.
func Gen(ctx context.Context, client *ent.Client) error {
    // ...
    return nil
}
 
 

最佳实践

  • 一个可重用的事务帮助方法
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
    tx, err := client.Tx(ctx)
    if err != nil {
        return err
    }
    defer func() {
        if v := recover(); v != nil {
            tx.Rollback()
            panic(v)
        }
    }()
    if err := fn(tx); err != nil {
        if rerr := tx.Rollback(); rerr != nil {
            err = errors.Wrapf(err, "rolling back transaction: %v", rerr)
        }
        return err
    }
    if err := tx.Commit(); err != nil {
        return errors.Wrapf(err, "committing transaction: %v", err)
    }
    return nil
}
 
 
  • 使用
func Do(ctx context.Context, client *ent.Client) {
    // WithTx helper.
    if err := WithTx(ctx, client, func(tx *ent.Tx) error {
        return Gen(ctx, tx.Client())
    }); err != nil {
        log.Fatal(err)
    }
}

参考资料

https://entgo.io/docs/transactions/

posted on 2019-10-15 16:00  荣锋亮  阅读(865)  评论(0编辑  收藏  举报

导航