Skip to content

事务处理

VEF Framework 提供了简洁的事务处理 API,确保数据一致性。

基本事务

使用 Transaction 函数

go
import "github.com/ilxqx/vef-framework-go/orm"

err := orm.Transaction(func(tx *gorm.DB) error {
    // Create user
    user := &models.User{
        Username: "john",
        Email:    "john@example.com",
    }
    if err := tx.Create(user).Error; err != nil {
        return err // Rollback on error
    }
    
    // Create user profile
    profile := &models.UserProfile{
        UserId: user.Id,
        Bio:    "Hello, I'm John",
    }
    if err := tx.Create(profile).Error; err != nil {
        return err // Rollback on error
    }
    
    return nil // Commit on success
})

if err != nil {
    // Transaction failed
    log.Error("Transaction failed:", err)
}

手动事务控制

go
tx := orm.DB().Begin()

defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()

// Perform operations
if err := tx.Create(&user).Error; err != nil {
    tx.Rollback()
    return err
}

if err := tx.Create(&profile).Error; err != nil {
    tx.Rollback()
    return err
}

// Commit transaction
return tx.Commit().Error

嵌套事务

使用 SavePoint

go
err := orm.Transaction(func(tx *gorm.DB) error {
    // Main transaction operations
    if err := tx.Create(&order).Error; err != nil {
        return err
    }
    
    // Nested transaction with savepoint
    err := tx.Transaction(func(tx2 *gorm.DB) error {
        // This can be rolled back independently
        if err := tx2.Create(&orderItem).Error; err != nil {
            return err // Only rolls back to savepoint
        }
        return nil
    })
    
    if err != nil {
        // Handle nested transaction failure
        // Main transaction can still continue
    }
    
    return nil
})

事务选项

设置隔离级别

go
import "database/sql"

tx := orm.DB().Begin(&sql.TxOptions{
    Isolation: sql.LevelSerializable,
})

只读事务

go
tx := orm.DB().Begin(&sql.TxOptions{
    ReadOnly: true,
})

在 API 中使用事务

自动事务

预置 API 的写入操作自动在事务中执行:

go
// CreateApi, UpdateApi, DeleteApi automatically use transactions
apis.NewCreateApi[models.User, payloads.UserParams]().
    WithHook(api.BeforeCreate, validateUser).    // In transaction
    WithHook(api.AfterCreate, createUserProfile) // Same transaction

自定义 API 中的事务

go
func (r *UserResource) TransferBalance(ctx fiber.Ctx, params TransferParams) error {
    err := orm.Transaction(func(tx *gorm.DB) error {
        // Deduct from sender
        if err := tx.Model(&models.User{}).
            Where("id = ? AND balance >= ?", params.FromUserId, params.Amount).
            Update("balance", gorm.Expr("balance - ?", params.Amount)).Error; err != nil {
            return err
        }
        
        // Check if deduction was successful
        var fromUser models.User
        if err := tx.First(&fromUser, "id = ?", params.FromUserId).Error; err != nil {
            return errors.New("insufficient balance")
        }
        
        // Add to receiver
        if err := tx.Model(&models.User{}).
            Where("id = ?", params.ToUserId).
            Update("balance", gorm.Expr("balance + ?", params.Amount)).Error; err != nil {
            return err
        }
        
        // Create transfer record
        transfer := &models.Transfer{
            FromUserId: params.FromUserId,
            ToUserId:   params.ToUserId,
            Amount:     params.Amount,
            CreatedAt:  time.Now(),
        }
        if err := tx.Create(transfer).Error; err != nil {
            return err
        }
        
        return nil
    })
    
    if err != nil {
        return api.Error(ctx, err.Error())
    }
    
    return api.Success(ctx, nil)
}

事务回调

注册提交后回调

go
err := orm.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&user).Error; err != nil {
        return err
    }
    
    // Register callback to run after commit
    tx.Callback().Create().After("gorm:after_create").Register("send_email", func(db *gorm.DB) {
        // This runs after successful commit
        go sendWelcomeEmail(user.Email)
    })
    
    return nil
})

错误处理

检查特定错误

go
import "gorm.io/gorm"

err := orm.Transaction(func(tx *gorm.DB) error {
    if err := tx.First(&user, "id = ?", userId).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return errors.New("user not found")
        }
        return err
    }
    return nil
})

自定义回滚

go
err := orm.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&order).Error; err != nil {
        return err
    }
    
    // Check business rule
    if order.Amount > 10000 {
        // Explicit rollback with custom error
        return errors.New("order amount exceeds limit")
    }
    
    return nil
})

最佳实践

  1. 保持事务简短:长事务会锁定资源,影响并发性能
  2. 避免在事务中进行外部调用:如 HTTP 请求、发送邮件等
  3. 使用适当的隔离级别:根据业务需求选择
  4. 处理死锁:实现重试机制
go
func executeWithRetry(fn func() error, maxRetries int) error {
    var err error
    for i := 0; i < maxRetries; i++ {
        err = fn()
        if err == nil {
            return nil
        }
        
        // Check if it's a deadlock error
        if isDeadlockError(err) {
            time.Sleep(time.Millisecond * time.Duration(100*(i+1)))
            continue
        }
        
        return err
    }
    return err
}

下一步

基于 Apache License 2.0 许可发布