如何编写可复用的数据库事务包装器?求function currying外的替代方案
Go事务代码复用的几种替代方案
你现在重复编写的事务初始化逻辑,核心是启动事务+根据是否为新事务处理回滚/提交,除了函数柯里化,还有以下几种更贴合Go风格的复用方案:
1. 高阶函数封装(最直接的复用方式)
把事务的初始化、清理逻辑封装成高阶函数,业务代码只需要关注核心逻辑,不用重复处理事务启动、回滚的 boilerplate 代码。
示例代码:
// 定义事务处理高阶函数,接收业务逻辑回调 func WithTransaction(req interface{}, db *DbConnectionManager, fn func(tx Transaction) error) error { isNewTx, rollback, beginErr := db.Begin(req) if beginErr != nil { return beginErr } if isNewTx { defer rollback() } // 执行业务逻辑 if err := fn(tx); err != nil { return err } // 若为新事务,执行提交(需调整原Begin方法,确保能获取到Transaction实例用于提交) if isNewTx { if commitErr := tx.Commit(); commitErr != nil { rollback() // 提交失败则回滚 return commitErr } } return nil }
使用时只需传入业务逻辑:
err := WithTransaction(req, DbConnectionManager, func(tx Transaction) error { // 这里编写具体业务逻辑,比如数据库操作 return userDao.Create(tx, &newUser) }) if err != nil { return nil, err }
2. 事务上下文结构体封装
把事务相关的状态(是否新事务、回滚/提交函数、事务实例)封装成结构体,提供初始化和收尾方法,让业务代码复用这个上下文对象。
示例代码:
type TxContext struct { isNewTx bool rollback func() tx Transaction } // 初始化事务上下文 func NewTxContext(req interface{}, db *DbConnectionManager) (*TxContext, error) { isNewTx, rollback, beginErr := db.Begin(req) if beginErr != nil { return nil, beginErr } // 假设从req或DbConnectionManager中获取当前事务实例 tx := getCurrentTransaction(req) return &TxContext{ isNewTx: isNewTx, rollback: rollback, tx: tx, }, nil } // 完成事务:成功则提交,失败则回滚 func (ctx *TxContext) Finish(success bool) error { if !ctx.isNewTx { return nil } if success { if err := ctx.tx.Commit(); err != nil { ctx.rollback() return err } } else { ctx.rollback() } return nil }
使用示例:
txCtx, err := NewTxContext(req, DbConnectionManager) if err != nil { return nil, err } // 兜底处理:panic时回滚事务 defer func() { if r := recover(); r != nil { txCtx.Finish(false) panic(r) } }() // 业务逻辑执行 if err := orderService.CreateOrder(txCtx.tx, req.Order); err != nil { txCtx.Finish(false) return nil, err } // 业务成功,提交事务 if err := txCtx.Finish(true); err != nil { return nil, err }
3. 简化的闭包封装(最小侵入式)
如果不想大幅改动原有代码结构,可以封装一个初始化函数,返回事务实例和一个收尾函数,让调用方直接通过defer管理事务。
示例代码:
func BeginTransaction(req interface{}, db *DbConnectionManager) (Transaction, func(bool) error, error) { isNewTx, rollback, beginErr := db.Begin(req) if beginErr != nil { return nil, nil, beginErr } tx := getCurrentTransaction(req) // 定义收尾函数,根据业务结果决定提交或回滚 finish := func(success bool) error { if !isNewTx { return nil } if success { if commitErr := tx.Commit(); commitErr != nil { rollback() return commitErr } } else { rollback() } return nil } return tx, finish, nil }
使用示例:
tx, finish, err := BeginTransaction(req, DbConnectionManager) if err != nil { return nil, err } defer finish(false) // 默认失败回滚 // 执行业务逻辑 if err := productDao.UpdateStock(tx, req.ProductID, req.Quantity); err != nil { return nil, err } // 业务成功,调用finish(true)提交事务 if err := finish(true); err != nil { return nil, err }
内容的提问来源于stack exchange,提问作者Uday Reddy
相关产品推荐
相关产品推荐

