gorm事务的使用
gorm事务默认是开启的。为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。
如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
一般不推荐禁用
// 全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})
普通事务
type TMG struct {
ID uint
Name string
}
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&TMG{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&TMG{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
}
执行下面代码,其中执行返回了一个test for err的错误,此时事务自动回滚,数据表中无数据
type TMG struct {
ID uint
Name string
}
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
flag := false
err := db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&TMG{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if !flag {
return errors.New("test for err")
}
if err := tx.Create(&TMG{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
if err != nil {
fmt.Println(err)//test for err
}
}
嵌套事务
GORM 支持嵌套事务,您可以回滚较大事务内执行的一部分操作,例如:
在下面代码中,我们知道包含name2的事务返回了err,是会回滚的,而外层事务返回nil,则会提交
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
db.Transaction(func(tx *gorm.DB) error {
tx.Create(&TMG{Name: "name 1"})
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&TMG{Name: "name 2"})
return errors.New("rollback user2") // Rollback user2
})
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&TMG{Name: "name 3"})
return nil
})
return nil
})
}
手动事务
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(&TMG{Name: "wxf1"})
tx.Create(&TMG{Name: "wxf2"})
tx.Create(&TMG{Name: "wxf3"})
// ...
...
if err!=nil{
// 遇到错误时回滚事务
fmt.Println(tx.Rollback().Error)
}
...
// 否则,提交事务
fmt.Println(tx.Commit().Error)
}
一个特殊的示例
type TMG struct {
ID uint
Name string
}
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
// 事务
CreateTMG(db)
}
func CreateTMG(db *gorm.DB) error {
// 再唠叨一下,事务一旦开始,你就应该使用 tx 处理数据
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return err
}
if err := tx.Create(&TMG{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&TMG{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
SavePoint、RollbackTo
GORM 提供了 SavePoint、Rollbackto 方法,来提供保存点以及回滚至保存点功能
这里rollback到了sp1的位置,也就是说,数据库中只存了wxf666这条数据
func main() {
db, _ := gorm.Open(mysql.New(mysql.Config{DSN: "root:123456@tcp(127.0.0.1:3306)/gormDB?charset=utf8mb4&parseTime=True&loc=Local"}), &gorm.Config{})
db.AutoMigrate(&TMG{})
// 事务
tx := db.Begin()
tx.Create(&TMG{Name: "wxf666"})
tx.SavePoint("sp1")
tx.Create(&TMG{Name: "wxf777"})
tx.Create(&TMG{Name: "wxf888"})
tx.SavePoint("sp2")
tx.RollbackTo("sp1")
tx.Commit()
}