1. Pain Points
It can be seen that there are some issues with managing transactions using conventional transaction methods:
- Redundant code. There are many repetitive
tx.Commit/Rollbackoperations in the code. - High operational risk. It is very easy to forget to execute
tx.Commit/Rollbackoperations, or due to code logic bugs, causing the transaction operation not to close properly. In cases of self-managing transaction operations, most programmers encounter this pitfall. The author updates this description (2023-08-09) because a friend encountered a production incident due to improper handling oftx.Commit/Rollbackin their transaction management. - Complex nested transaction implementation. If there are multiple levels of transaction processing (nested transactions) in the business logic, it requires considering how to pass the
txobject down, making it more cumbersome.
2. Closure Operation
Therefore, to facilitate safe transaction operations, the ORM component also provides a closure operation for transactions, implemented via the Transaction method, which is defined as follows:
func (db DB) Transaction(ctx context.Context, f func(ctx context.Context, tx TX) error) (err error)
When the error returned by the given closure method is nil, the current transaction automatically executes the Commit operation once the closure completes; otherwise, it automatically executes the Rollback operation. The context.Context parameter in the closure is a context variable introduced in goframe v1.16, mainly for link tracing transmission and nested transaction management. As the context variable is an important parameter for nested transaction management, it is defined for explicit parameter passing.
If a panic interruption occurs within the closure operation, the transaction will also automatically rollback to ensure operation safety.
Usage example:
g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
// user
result, err := tx.Ctx(ctx).Insert("user", g.Map{
"passport": "john",
"password": "12345678",
"nickname": "JohnGuo",
})
if err != nil {
return err
}
// user_detail
id, err := result.LastInsertId()
if err != nil {
return err
}
_, err = tx.Ctx(ctx).Insert("user_detail", g.Map{
"uid": id,
"site": "https://johng.cn",
"true_name": "GuoQiang",
})
if err != nil {
return err
}
return nil
})
The closure operation method allows for easy implementation of nested transactions, and it is essentially transparent to upper-level business developers. For more details, you can continue reading the chapter: ORM Transaction - Nested