|
|
|
|
package gormExample
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
"log"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TXDemo() {
|
|
|
|
|
// 初始化测试数据
|
|
|
|
|
if err := DB.AutoMigrate(&Author{}); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
var a1, a2 Author
|
|
|
|
|
a1.Name = "库里"
|
|
|
|
|
a2.Name = "莫兰特"
|
|
|
|
|
a1.Points = 11600
|
|
|
|
|
a2.Points = 200
|
|
|
|
|
if err := DB.Create([]*Author{&a1, &a2}).Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 事务操作
|
|
|
|
|
// a1 赠送 a2 2000 积分
|
|
|
|
|
p := 2000
|
|
|
|
|
// 开始事务
|
|
|
|
|
tx := DB.Begin()
|
|
|
|
|
// 有时需要考虑数据库是否支持事务的情景
|
|
|
|
|
if tx.Error != nil {
|
|
|
|
|
log.Fatalln(tx.Error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行赠送操作
|
|
|
|
|
a1.Points -= p
|
|
|
|
|
a2.Points += p
|
|
|
|
|
|
|
|
|
|
// 1执行SQL,可能导致的错误
|
|
|
|
|
if err := tx.Save(&a1).Error; err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := tx.Save(&a2).Error; err != nil {
|
|
|
|
|
// 回滚事务
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2业务逻辑可能导致的错误
|
|
|
|
|
// 要求author的积分不能为负数
|
|
|
|
|
if a1.Points < 0 || a2.Points < 0 {
|
|
|
|
|
log.Println("a1.Points < 0 || a2.Points < 0")
|
|
|
|
|
// 回滚事务
|
|
|
|
|
if err := tx.Rollback().Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提交事务
|
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 决定回滚还是提交,集中的处理错误风格
|
|
|
|
|
//if err1 != nil || err2 != nil {
|
|
|
|
|
// tx.Rollback()
|
|
|
|
|
//} else {
|
|
|
|
|
// tx.Commit()
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TXCallback() {
|
|
|
|
|
// 初始化测试数据
|
|
|
|
|
if err := DB.AutoMigrate(&Author{}); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
var a1, a2 Author
|
|
|
|
|
a1.Name = "库里"
|
|
|
|
|
a2.Name = "莫兰特"
|
|
|
|
|
a1.Points = 1600
|
|
|
|
|
a2.Points = 200
|
|
|
|
|
if err := DB.Create([]*Author{&a1, &a2}).Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
log.Println(a1.ID, a2.ID)
|
|
|
|
|
|
|
|
|
|
// 实现事务
|
|
|
|
|
if err := DB.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
// a1 赠送 a2 2000 积分
|
|
|
|
|
p := 200
|
|
|
|
|
// 执行赠送操作
|
|
|
|
|
a1.Points -= p
|
|
|
|
|
a2.Points += p
|
|
|
|
|
|
|
|
|
|
// 1执行SQL,可能导致的错误
|
|
|
|
|
if err := tx.Save(&a1).Error; err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := tx.Save(&a2).Error; err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2业务逻辑可能导致的错误
|
|
|
|
|
// 要求author的积分不能为负数
|
|
|
|
|
if a1.Points < 0 || a2.Points < 0 {
|
|
|
|
|
return errors.New("a1.Points < 0 || a2.Points < 0")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nil 的返回,会导致事务提交
|
|
|
|
|
return nil
|
|
|
|
|
}); err != nil {
|
|
|
|
|
// 返回错误,为了后续的业务逻辑处理
|
|
|
|
|
// 为了通知我们,事务成功还是失败
|
|
|
|
|
// 返回错误,不影响事务的提交和回滚
|
|
|
|
|
log.Println(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TXNested() {
|
|
|
|
|
// 初始化测试数据
|
|
|
|
|
if err := DB.AutoMigrate(&Author{}); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
var a1, a2, a3 Author
|
|
|
|
|
a1.Name = "库里"
|
|
|
|
|
a2.Name = "莫兰特"
|
|
|
|
|
a3.Name = "欧文"
|
|
|
|
|
a1.Points = 1600
|
|
|
|
|
a2.Points = 200
|
|
|
|
|
a3.Points = 4000
|
|
|
|
|
if err := DB.Create([]*Author{&a1, &a2, &a3}).Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
log.Println(a1.ID, a2.ID, a3.ID)
|
|
|
|
|
|
|
|
|
|
// 实现事务
|
|
|
|
|
if err := DB.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
// a1 赠送 a2 2000 积分
|
|
|
|
|
p := 20000
|
|
|
|
|
|
|
|
|
|
// 执行赠送操作
|
|
|
|
|
|
|
|
|
|
// a2 多了积分
|
|
|
|
|
a2.Points += p
|
|
|
|
|
if err := tx.Save(&a2).Error; err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// a1 赠送,使用嵌套事务完成
|
|
|
|
|
errA1 := tx.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
a1.Points -= p
|
|
|
|
|
// 1执行SQL,可能导致的错误
|
|
|
|
|
if err := tx.Save(&a1).Error; err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if a1.Points < 0 {
|
|
|
|
|
return errors.New("a1.Points < 0")
|
|
|
|
|
}
|
|
|
|
|
// 没有错误成功
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// a1 发送失败,才需要a3
|
|
|
|
|
if errA1 != nil {
|
|
|
|
|
// a3 赠送,使用嵌套事务完成
|
|
|
|
|
errA3 := DB.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
a3.Points -= p
|
|
|
|
|
if err := tx.Save(&a3).Error; err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if a3.Points < 0 {
|
|
|
|
|
return errors.New("a3.Points < 0")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
// a3 同样失败
|
|
|
|
|
if errA3 != nil {
|
|
|
|
|
return errors.New("a1 and a3 all send points failed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nil 的返回,会导致事务提交
|
|
|
|
|
return nil
|
|
|
|
|
}); err != nil {
|
|
|
|
|
// 返回错误,为了后续的业务逻辑处理
|
|
|
|
|
// 为了通知我们,事务成功还是失败
|
|
|
|
|
// 返回错误,不影响事务的提交和回滚
|
|
|
|
|
log.Println(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TXSavePoint() {
|
|
|
|
|
// 初始化测试数据
|
|
|
|
|
if err := DB.AutoMigrate(&Author{}); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
var a1, a2, a3 Author
|
|
|
|
|
a1.Name = "库里"
|
|
|
|
|
a2.Name = "莫兰特"
|
|
|
|
|
a3.Name = "欧文"
|
|
|
|
|
a1.Points = 1600
|
|
|
|
|
a2.Points = 200
|
|
|
|
|
a3.Points = 4000
|
|
|
|
|
if err := DB.Create([]*Author{&a1, &a2, &a3}).Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
log.Println(a1.ID, a2.ID, a3.ID)
|
|
|
|
|
|
|
|
|
|
// 事务操作
|
|
|
|
|
// a1 赠送 a2 2000 积分
|
|
|
|
|
p := 20000
|
|
|
|
|
// 开始事务
|
|
|
|
|
tx := DB.Begin()
|
|
|
|
|
// 有时需要考虑数据库是否支持事务的情景
|
|
|
|
|
if tx.Error != nil {
|
|
|
|
|
log.Fatalln(tx.Error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行赠送操作
|
|
|
|
|
// a2 得到积分
|
|
|
|
|
a2.Points += p
|
|
|
|
|
// 1执行SQL,可能导致的错误
|
|
|
|
|
if err := tx.Save(&a2).Error; err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 逻辑记录发送points是否成功
|
|
|
|
|
var flagSend bool
|
|
|
|
|
|
|
|
|
|
// a1 先给 a2 send
|
|
|
|
|
// 设置一个 savepoint
|
|
|
|
|
tx.SavePoint("beforeA1")
|
|
|
|
|
a1.Points -= p
|
|
|
|
|
if err := tx.Save(&a1).Error; err != nil || a1.Points < 0 {
|
|
|
|
|
// 回滚到 beforeA1
|
|
|
|
|
tx.RollbackTo("beforeA1")
|
|
|
|
|
|
|
|
|
|
// a3 to a2
|
|
|
|
|
tx.SavePoint("beforeA3")
|
|
|
|
|
a3.Points -= p
|
|
|
|
|
if err := tx.Save(&a3).Error; err != nil || a3.Points < 0 {
|
|
|
|
|
// 回滚到 beforeA3
|
|
|
|
|
tx.RollbackTo("beforeA3")
|
|
|
|
|
} else {
|
|
|
|
|
flagSend = true
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
flagSend = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 判定发送是否成功
|
|
|
|
|
if flagSend {
|
|
|
|
|
// 提交事务
|
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 回滚事务
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
}
|
|
|
|
|
}
|