禁用默认事务
GORM 事务默认是开启的。为了确保数据一致性,GORM 会在事务里执行写入操作(增删改)。如果对数据一致性要求不高的话,可以在初始化时禁用它,性能将提升大约 30%。一般不推荐禁用。
// 全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})
自动事务
通过 db.Transaction
函数实现事务,如果闭包函数返回错误,则回滚事务。
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&User{Name: "Lili"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&User{Name: "xiaoming"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
手动事务
在开发中经常需要数据库事务来保证多个数据库写操作的原子性。例如电商系统中的扣减库存和保存订单。
GORM 事务用法:
// 开启事务
tx := db.Begin()
// 在事务中执行数据库操作,使用的是 tx 变量,不是 db。
// 库存减一
// 等价于: UPDATE `goods` SET `stock` = stock - 1 WHERE `goods`.`id` = '2' and stock > 0
// RowsAffected 用于返回 sql 执行后影响的行数
rowsAffected := tx.Model(&goods).Where("stock > 0").Update("stock", gorm.Expr("stock - 1")).RowsAffected
if rowsAffected == 0 {
// 如果更新库存操作,返回影响行数为 0,说明没有库存了,结束下单流程
// 这里回滚作用不大,因为前面没成功执行什么数据库更新操作,也没什么数据需要回滚。
// 这里就是举个例子,事务中可以执行多个 sql 语句,错误了可以回滚事务
tx.Rollback()
return
}
err := tx.Create(保存订单).Error
// 保存订单失败,则回滚事务
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
嵌套事务
GORM 支持嵌套事务,可以回滚较大事务内执行的一部分操作,例如:
db.Transaction(func(tx *gorm.DB) error {
tx.Create(&user1)
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&user2)
return errors.New("rollback user2") // Rollback user2
})
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&user3)
return nil
})
return nil
})
// Commit user1, user3
SavePoint、RollbackTo
GORM 提供了 SavePoint
、RollbackTo
来提供保存点以及回滚至保存点,例如:
tx := db.Begin()
tx.Create(&user1)
tx.SavePoint("sp1")
tx.Create(&user2)
tx.RollbackTo("sp1") // Rollback user2
tx.Commit() // Commit user1
这里 rollback 到了 sp1 的位置,也就是说,数据库中只存了 user1 这条数据。
小结
总的来说 GORM 事务的实现非常简单,Go 语言操作 MySQL 事务主要用到了三个函数:
Db.Begin()
开始事务Db.Commit()
提交事务Db.Rollback()
回滚事务