I have a use case in one of my Golang applications to update multiple tables in an API and in case one of the updates fails, I would want all the previous updates to roll back (Something that @Transactional in Java does). I tried doing it in the below way:
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 = ?"
_, err := d.DB.Exec(query, "v1", "v2")
return err
}
But this doesn't work as we are creating a new transaction in the Update1 & Update2 methods. So I tried to pass transactions in the method parameters of method Update1 & Update2 like below:
func Update1(tx sql.Tx) error {
var query = "update T1 set C1 = ? where id = ?"
_, err := tx.Exec(query, "v1", "v2")
return err
}
But the problem is in some use cases I want just method Update1 to be called. In that case, what should be the value of the transaction to be passed in the Update1 method?
Or is there a better way to do it. Can someone please help?
If I understand correctly, in some cases you want
Update1
to be called but notUpdate2
, while in other cases you want both updates to happen. If that’s the case, then I would suggest that this is more of a design problem, rather than with the usage ofTx
. I think designing two functions that each describes the behavior you expect, and delegating the logic about which one to call to the caller, might work. Something like: