在不同的 DAO 中使用相同的数据库事务

问题描述

我在我的一个 Golang 应用程序中有一个用例来更新 API 中的多个表,如果其中一个更新失败,我希望所有以前的更新回滚(Java 中的 @Transactional 所做的事情)。 我尝试按以下方式进行:

func (d *dao) Method1(opt string) error {
    tx,err := d.DB.BeginTx(context.Background(),nil)
    if err != nil {
        return errors.Errorf("Unable to start transaction")
    }
    err := d.dao1.Update1(opt)
        if err != nil {
            log.Infof("Error : %s. Rolling back transaction",err)
            _ = tx.Rollback()
            log.Infof("Transaction rolled back while update 1")
            return err
        }
        err = d.dao2.Update2(opt)
        if err != nil {
            log.Errorf("Error in making update 2 "+
                "Error %v ",err)
            _ = tx.Rollback()
            log.Infof("Transaction rolled back while update 2")
            return err
        }
    }
}


func(d *dao1) Update1 error {
        var query = "update T1 set C1 = ? where id = ?"
        _,err := d.DB.Exec(query,"v1","v2")
        return err
}

func(d *dao2) Update2 error {
        var query = "update T2 set C2 = ? where id = ?"
        _,"v2")
        return err
}

但这不起作用,因为我们在 Update1 和 Update2 方法中创建了一个新事务。 所以我尝试在方法 Update1 和 Update2 的方法参数中传递事务,如下所示:

func Update1(tx sql.Tx) error {
    var query = "update T1 set C1 = ? where id = ?"
    _,err := tx.Exec(query,"v2")
    return err
}

但问题是在某些用例中,我只想调用方法 Update1。那么,Update1 方法中要传递的事务的值应该是多少?

或者有更好的方法来做到这一点。有人可以帮忙吗?

解决方法

如果我理解正确,在某些情况下,您希望调用 Update1 而不是 Update2,而在其他情况下,您希望两个更新都发生。如果是这样,那么我建议这更多是一个设计问题,而不是 Tx 的使用。我认为设计两个函数,每个函数都描述您期望的行为,并将关于调用哪个函数的逻辑委托给调用者,可能会奏效。类似的东西:

func Caller() error {
    if condition {
        return UpdateOne()
    }
    return UpdateBoth()
}

func UpdateOne() error { … }
func UpdateBoth() error { … }