事务本身是一个很有用的东西,合理的利用可以很好的解决问题,但是如果使用不合理,也可能造成其他问题。本文就Go语言的常用姿势来展开讨论。
1、使用Golang语言的ORM举例
grom是支持事务的.
GORM 默认会将单个的 create, update, delete操作封装在事务内进行处理,以确保数据的完整性。
如果你想把多个 create, update, delete 操作作为一个原子操作,Transaction 就是用来完成这个的。
// 开启事务
tx := db.Begin()
// 在事务中执行具体的数据库操作 (事务内的操作使用 'tx' 执行,而不是 'db')
tx.Create(...)
// ...
// 如果发生错误则执行回滚
tx.Rollback()
// 或者(未发生错误时)提交事务
tx.Commit()
特别注意:delete支持事务回滚;truncate不支持回滚
tx.Rollback().Error 可以查看回滚是否报错
delete如果不加where会把所有数据清空,如果此时有另一个mysql实例往数据库新插入数据,当前mysql实例也无法执行回滚
2、展开讨论
做事务的核心逻辑是处理报错和异常,缺一不可。事务进行过程中,如果因为异常或报错程序中断,是不会自动回滚的,所以一定要保证,在接收异常和报错前就回滚。
1、一般的操作
这种写法程序流程也明确,但是事务处理与程序流程嵌入太深,容易遗漏;
func DoSomething() (err error) {
tx, err := db.Begin()
if err != nil {
return
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p) // re-throw panic after Rollback
}
}()
if _, err = tx.Exec(...); err != nil {
tx.Rollback()
return
}
if _, err = tx.Exec(...); err != nil {
tx.Rollback()
return
}
// ...
err = tx.Commit()
return
}
2、高级写法
把数据库操作进一步封装,写法高级一点,也同时处理了异常和报错
func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) {
tx, err := db.Begin()
if err != nil {
return
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p) // re-throw panic after Rollback
} else if err != nil {
tx.Rollback()
} else {
err = tx.Commit()
}
}()
err = txFunc(tx)
return err
}
func DoSomething() error {
return Transact(db, func (tx *sql.Tx) error {
if _, err := tx.Exec(...); err != nil {
return err
}
if _, err := tx.Exec(...); err != nil {
return err
}
})
}