Skip to content
On this page

事务

数据准备,会自动创建t_dogs

Go
type TDog struct {
	Name string
}

func tAutoMigrate() {
	err := DB.AutoMigrate(&TDog{})
	if err != nil {
		return
	}
}
func main() {
	tAutoMigrate()
}

普通事务

开启普通事务可以使用Transaction方法

WARNING

  • Transaction方法中对数据库的操作要使用回调函数中的(tx *gorm.DB)
Go
func normalTransaction() {
	err := DB.Transaction(func(tx *gorm.DB) error {
		d1 := TDog{Name: "狗哥1号"}
		d2 := TDog{Name: "狗哥2号"}
		dogs := []TDog{d1, d2}
		tx.Model(&TDog{}).Create(&dogs)
		return nil
	})
	if err != nil {
		return
	}
}

正常情况下会成功创建两条记录,t_dogs表结构如下

json
[
  {
    "name": "狗哥1号"
  },
  {
    "name": "狗哥2号"
  }
]

如果Transaction函数中返回错误,那么所有操作都不会被执行

Go
func normalErrorTransaction() {
	err := DB.Transaction(func(tx *gorm.DB) error {
		d3 := TDog{Name: "狗哥3号"}
		d4 := TDog{Name: "狗哥4号"}
		dogs := []TDog{d3, d4}
		tx.Model(&TDog{}).Create(&dogs)
		return errors.New("错误")
	})
	if err != nil {
		return
	}
}

以上操作,由于Transaction函数中返回了error,所以其中的创建d3、d4的操作不会成功执行,上述代码执行后t_dogs表结构如下

json
[
  {
    "name": "狗哥1号"
  },
  {
    "name": "狗哥2号"
  }
]

嵌套事务

嵌套事务中,如果内部事务报错,也不会执行内部的操作

Go
func nestTransaction() {
	err := DB.Transaction(func(tx *gorm.DB) error {
		d5 := TDog{Name: "狗哥5号"}
		tx.Model(&TDog{}).Create(&d5)
		err := tx.Transaction(func(tx *gorm.DB) error {
			d6 := TDog{Name: "狗哥6号"}
			tx.Model(&TDog{}).Create(&d6)
			return errors.New("内部事务出错")
		})
		if err != nil {
			// 暂时不处理这个错误 让外部的事务可以顺利执行
			//return err
			fmt.Printf("内部事务报错 %s\n", err.Error())
		}
		return nil
	})
	if err != nil {
		return
	}
}

以上操作,由于内部的Transaction报错,所以不会创建狗哥6号,而狗哥5号会被成功创建

如果放开下面的return错误,则狗哥6号狗哥5号都不会被创建

Go
if err != nil {
    // 暂时不处理这个错误 让外部的事务可以顺利执行
    //return err
    fmt.Printf("内部事务报错 %s\n", err.Error())
}

手动事务

可以自己开启事务、回滚和提交

手动事务有以下几个方法

Go
// 开启事务
tx := DB.Begin()
// 回滚操作
tx.Rollback()
// 提交,执行全部的操作
tx.Commit()

Commit

示例如下:

Go
func manualTransaction() {
	tx := DB.Begin()
	d7 := TDog{Name: "狗哥7号"}
	d8 := TDog{Name: "狗哥8号"}
	tx.Model(&TDog{}).Create(&d7)
	tx.Model(&TDog{}).Create(&d8)
	tx.Commit()
}

以上代码会创建狗哥7号狗哥8号,上述代码执行后t_dogs表结构如下

json
[
  {
    "name": "狗哥1号"
  },
  {
    "name": "狗哥2号"
  },
  {
    "name": "狗哥5号"
  },
  {
    "name": "狗哥7号"
  },
  {
    "name": "狗哥8号"
  }
]

WARNING

  • 执行过tx.Commit()方法后,再次执行tx.xxx操作时,该操作不会执行

  • 所以,如果是手动事务的情况,所做的操作一定要在tx.Commit方法执行之前

示例如下:

Go
func manualTransaction2() {
	tx := DB.Begin()
	d9 := TDog{Name: "狗哥9号"}
	d10 := TDog{Name: "狗哥10号"}
	tx.Model(&TDog{}).Create(&d9)
	tx.Commit()
	tx.Model(&TDog{}).Create(&d10)
}

以上操作,在Commit之后,再次创建狗哥10号,该操作不会执行,所以只会创建狗哥9号,不会创建狗哥10号,上述代码执行后t_dogs表结构如下

json
[
  {
    "name": "狗哥1号"
  },
  {
    "name": "狗哥2号"
  },
  {
    "name": "狗哥5号"
  },
  {
    "name": "狗哥7号"
  },
  {
    "name": "狗哥8号"
  },
  {
    "name": "狗哥9号"
  }
]

Rollback

Commit方法执行之前,如果想要回滚之前的操作,可以使用Rollback方法

Go
func manualTransaction3() {
	tx := DB.Begin()
	d11 := TDog{Name: "狗哥11号"}
	d12 := TDog{Name: "狗哥12号"}
	dogs := []TDog{d11, d12}
	tx.Create(&dogs)
	tx.Rollback()
	tx.Commit()
}

TIP

  • 以上代码执行后,不会创建任何数据,因为在Commit之前已经回滚(执行Rollback)方法了,此时再执行Commit就没用了

  • 如果在Commit执行之后再去回滚(执行Rollback),是不会成功的

记录回滚点

TIP

  • 可以使用tx.SavePoint方法保存回滚点,然后通过tx.RollbackTo方法回滚到之前的操作

下面的代码会创建狗哥13号,并不会创建狗哥14号

Go
func rollbackPoint() {
	tx := DB.Begin()
	d13 := TDog{Name: "狗哥13号"}
	d14 := TDog{Name: "狗哥14号"}
	tx.Model(&TDog{}).Create(&d13)
	pointName := "f"
	tx.SavePoint(pointName)
	tx.Model(&TDog{}).Create(&d14)
	tx.RollbackTo(pointName)
	tx.Commit()
}

下面的代码会创建狗哥15号狗哥17号,并不会创建狗哥16号

Go
func rollbackPoint2() {
	tx := DB.Begin()
	d15 := TDog{Name: "狗哥15号"}
	d16 := TDog{Name: "狗哥16号"}
	d17 := TDog{Name: "狗哥17号"}
	tx.Model(&TDog{}).Create(&d15)
	pointName := "f"
	tx.SavePoint(pointName)
	tx.Model(&TDog{}).Create(&d16)
	tx.RollbackTo(pointName)
	tx.Model(&TDog{}).Create(&d17)
	tx.Commit()
}