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/Rollback operations in the code.
  • High operational risk. It is very easy to forget to execute tx.Commit/Rollback operations, 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 of tx.Commit/Rollback in 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 tx object 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:

  1. 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.

ORM Transaction - Closure - 图1tip

If a panic interruption occurs within the closure operation, the transaction will also automatically rollback to ensure operation safety.

Usage example:

  1. g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
  2. // user
  3. result, err := tx.Ctx(ctx).Insert("user", g.Map{
  4. "passport": "john",
  5. "password": "12345678",
  6. "nickname": "JohnGuo",
  7. })
  8. if err != nil {
  9. return err
  10. }
  11. // user_detail
  12. id, err := result.LastInsertId()
  13. if err != nil {
  14. return err
  15. }
  16. _, err = tx.Ctx(ctx).Insert("user_detail", g.Map{
  17. "uid": id,
  18. "site": "https://johng.cn",
  19. "true_name": "GuoQiang",
  20. })
  21. if err != nil {
  22. return err
  23. }
  24. return nil
  25. })

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