事务
事务
事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。
很形象的一个例子,张三给李四转账100元,在程序里面,张三的余额就要-100,李四的余额就要+100 整个事件是一个整体,哪一步错了,整个事件都是失败的
gorm事务默认是开启的。为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。
如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
一般不推荐禁用
// 全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})
本节课表结构
type User struct {
ID uint `json:"id"`
Name string `json:"name"`
Money int `json:"money"`
}
// InnoDB引擎才支持事务,MyISAM不支持事务
// DB.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
普通事务
以张三给李四转账为例,不使用事务的后果
var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")
// 张三给李四转账100元
// 先给张三-100
zhangsan.Money -= 100
DB.Model(&zhangsan).Update("money", zhangsan.Money)
// 模拟失败的情况
// 再给李四+100
lisi.Money += 100
DB.Model(&lisi).Update("money", lisi.Money)
在失败的情况下,要么张三白白损失了100,要么李四凭空拿到100元
这显然是不合逻辑的,并且不合法的
那么,使用事务是怎样的
var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")
// 张三给李四转账100元
DB.Transaction(func(tx *gorm.DB) error {
// 先给张三-100
zhangsan.Money -= 100
err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Error
if err != nil {
fmt.Println(err)
return err
}
// 再给李四+100
lisi.Money += 100
err = tx.Model(&lisi).Update("money", lisi.Money).Error
if err != nil {
fmt.Println(err)
return err
}
// 提交事务
return nil
})
使用事务之后,他们就是一体,一起成功,一起失败
手动事务
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)
// ...
// 遇到错误时回滚事务
tx.Rollback()
// 否则,提交事务
tx.Commit()
刚才的代码也可以这样实现
var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")
// 张三给李四转账100元
tx := DB.Begin()
// 先给张三-100
zhangsan.Money -= 100
err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Error
if err != nil {
tx.Rollback()
}
// 再给李四+100
lisi.Money += 100
err = tx.Model(&lisi).Update("money", lisi.Money).Error
if err != nil {
tx.Rollback()
}
// 提交事务
tx.Commit()