聊聊gorm的Transaction

3,375 阅读1分钟

本文主要研究一下gorm的Transaction

Transaction

gorm.io/gorm@v1.20.10/finisher_api.go

// Transaction start a transaction as a block, return error will rollback, otherwise to commit.
func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) {
	panicked := true

	if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {
		// nested transaction
		if !db.DisableNestedTransaction {
			err = db.SavePoint(fmt.Sprintf("sp%p", fc)).Error
			defer func() {
				// Make sure to rollback when panic, Block error or Commit error
				if panicked || err != nil {
					db.RollbackTo(fmt.Sprintf("sp%p", fc))
				}
			}()
		}

		if err == nil {
			err = fc(db.Session(&Session{}))
		}
	} else {
		tx := db.Begin(opts...)

		defer func() {
			// Make sure to rollback when panic, Block error or Commit error
			if panicked || err != nil {
				tx.Rollback()
			}
		}()

		if err = tx.Error; err == nil {
			err = fc(tx)
		}

		if err == nil {
			err = tx.Commit().Error
		}
	}

	panicked = false
	return
}

DB的Transaction方法针对非TxCommitter类型的db.Statement.ConnPool执行db.Begin,之后注册defer针对panicked或者err的执行tx.Rollback(),执行fc之后,判断err为nil的情况下执行tx.Commit(),并将Error赋值为err

实例

func TestInsertInTx(t *testing.T) {
	err := db.Transaction(func(tx *gorm.DB) error {
		classes := []model.Class{
			{
				Name: "abc2",
			},
			{
				Name: "abc2",
			},
		}
		err := db.Create(&classes).Error
		if err != nil {
			return err
		}

		uc := []model.UserClass{
			{
				UserId: 1,
				ClassId: classes[0].ID,
			},
			{
				UserId: 1,
				ClassId: classes[1].ID,
			},
		}
		err2 := db.Create(&uc).Error
		if err2 != nil {
			return err2
		}

		return nil
	})

	if err != nil {
		t.Error(err)
	}
}

小结

gorm的DB提供了Transaction方法,相当于一个事务模板,自动commit或者rollback,它提供了func(tx *gorm.DB) error参数用于自定义事务内的数据库操作。

doc