事务的封装和管理
在平时开发中, 有些业务是需要保证ACID的, 因此事务就派上用场了.
大多是否在应用中是使用orm来操作数据库的, 下面的所有例子都是使用orm为例子.
用事务, 大多数人一般是这样用的:在需要用到事务的地方, 都来一堆以下的代码:
事务业务1:
var err error
//开始事务
if err = o.Begin(); err != nil {
return nil, err
}
//操作1
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//操作2
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//操作3
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//提交事务
_ = o.Commit()
事务业务2:
var err error
//开始事务
if err = o.Begin(); err != nil {
return nil, err
}
//操作1
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//操作2
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//操作3
if err = db.Exec(); err !=nil {
_ = o.Rollback()
}
//提交事务
_ = o.Commit()
事务业务n…
这样使用, 正确性是没有问题的. 但是有一些缺点:
- 代码臃肿
- 不好管理事务
- 事务没有统一入口, 记录各处都记录事务, 造成混乱
- 不够傻瓜式
下面引出一种正确的, 优秀的处理方案
事务优化封装管理
- 只提供一个事务接口, 封装好事务的处理, 业务开发者只需关心操作逻辑, 不需要关心事务, 事务的处理对业务开发者是透明的
- 接口入参自定的业务逻辑处理函数, 业务开发只需要传入具体的操作就ok
- 在事务接口里统一做统计,记录相关工作
源码如下:
//业务逻辑的函数, 开发者在使用事务时, 定义这样的函数传入Transaction就行了
type TxFunc func(db orm.Ormer) (interface{}, error)
//真正的事务处理函数
func Transaction(f TxFunc) (interface{}, error) {
var(
var err error
o = orm.NewOrm()
)
//开启事务
if err = o.Begin(); err != nil {
return nil, err
}
defer func() {
//捕捉panic
if p := recover(); p != nil {
_ = o.Rollback()
panic(p) // re-throw panic after Rollback
} else if err != nil {
//回滚不覆盖err, 是因为回滚就是因为有业务的报错, 所以不应该被这条语句覆盖掉业务的err
_ = o.Rollback() // err is non-nil; don't change it
} else {
//这里返回err, 是因为业务的err肯定是nil才走到这里, 如果在最后的commit的err非nil,是需要返回的
err = o.Commit() // err is nil; if Commit returns error update err
}
}()
//调用业务逻辑
return f(o)
}
使用例子:
//业务逻辑处理函数
txF := func(db orm.Ormer) (interface{}, error) {
var err error
err = db.Exec()
err = db.Exec()
err = db.Exec()
return nil, err
}
//事务统一接口
res, err = models.Transaction(txF)
if err != nil {
logs.Error("a Transaction err:%s", err.Error())
return
}
也可以这样使用:
//事务统一接口
res, err = models.Transaction(func(db orm.Ormer) (interface{}, error) {
var err error
err = db.Exec()
err = db.Exec()
err = db.Exec()
return nil, err
})
if err != nil {
logs.Error("a Transaction err:%s", err.Error())
return
}
经过这样的优化, 事务的处理应该是简洁简单, 比较”傻瓜式”了.
总结
这样来封装事务接口, 可以做到事务的回滚和提交处理对开发者是透明的, 能够更好的统一管理事务, 不会造成事务sql随处可见的情况