forked from mirror/gorm
Add `db.Transaction` method for create Transaction block. (#2767)
* Add `db.Transaction` method for create Transaction block. example: ```go func CreateAnimals(db *gorm.DB) error { db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil { // return any error will rollback return err } if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil { return err } // return nil will commit return nil }) } ``` * Ensure rollback when commit has error.
This commit is contained in:
parent
179760d834
commit
59408390c2
25
main.go
25
main.go
|
@ -525,6 +525,31 @@ func (s *DB) Debug() *DB {
|
||||||
return s.clone().LogMode(true)
|
return s.clone().LogMode(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transaction start a transaction as a block,
|
||||||
|
// return error will rollback, otherwise to commit.
|
||||||
|
func (s *DB) Transaction(fc func(tx *DB) error) (err error) {
|
||||||
|
tx := s.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("%s", r)
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = fc(tx)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = tx.Commit().Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makesure rollback when Block error or Commit error
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Begin begins a transaction
|
// Begin begins a transaction
|
||||||
func (s *DB) Begin() *DB {
|
func (s *DB) Begin() *DB {
|
||||||
return s.BeginTx(context.Background(), &sql.TxOptions{})
|
return s.BeginTx(context.Background(), &sql.TxOptions{})
|
||||||
|
|
60
main_test.go
60
main_test.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -469,6 +470,65 @@ func TestTransaction(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransactionWithBlock(t *testing.T) {
|
||||||
|
// rollback
|
||||||
|
err := DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
u := User{Name: "transcation"}
|
||||||
|
if err := tx.Save(&u).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.First(&User{}, "name = ?", "transcation").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("the error message")
|
||||||
|
})
|
||||||
|
|
||||||
|
if err.Error() != "the error message" {
|
||||||
|
t.Errorf("Transaction return error will equal the block returns error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DB.First(&User{}, "name = ?", "transcation").Error; err == nil {
|
||||||
|
t.Errorf("Should not find record after rollback")
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit
|
||||||
|
DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
u2 := User{Name: "transcation-2"}
|
||||||
|
if err := tx.Save(&u2).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.First(&User{}, "name = ?", "transcation-2").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := DB.First(&User{}, "name = ?", "transcation-2").Error; err != nil {
|
||||||
|
t.Errorf("Should be able to find committed record")
|
||||||
|
}
|
||||||
|
|
||||||
|
// panic will rollback
|
||||||
|
DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
u3 := User{Name: "transcation-3"}
|
||||||
|
if err := tx.Save(&u3).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.First(&User{}, "name = ?", "transcation-3").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("force panic")
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := DB.First(&User{}, "name = ?", "transcation").Error; err == nil {
|
||||||
|
t.Errorf("Should not find record after panic rollback")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTransaction_NoErrorOnRollbackAfterCommit(t *testing.T) {
|
func TestTransaction_NoErrorOnRollbackAfterCommit(t *testing.T) {
|
||||||
tx := DB.Begin()
|
tx := DB.Begin()
|
||||||
u := User{Name: "transcation"}
|
u := User{Name: "transcation"}
|
||||||
|
|
Loading…
Reference in New Issue