当前位置: 首页 > news >正文

Gorm(十一)事务

在 Gorm 中,事务(Transaction)用于保证一系列数据库操作的原子性(要么全部成功,要么要么全部失败),避免部分操作成功、部分失败导致的数据不一致。Gorm 支持手动事务自动事务嵌套事务,适用于不同场景。以下是详细说明:

一、手动事务(Manual Transactions)

手动事务需要显式调用 Begin 开启事务,通过 Commit 提交事务,或 Rollback 回滚事务,全程由开发者控制事务生命周期,适合复杂业务逻辑(如多步操作需手动判断是否提交)。

基本流程
  1. 开启事务:调用 db.Begin() 获取事务对象 tx *gorm.DB
  2. 执行操作:通过事务对象 tx 执行数据库操作(如 CreateUpdateDelete)。
  3. 判断结果:若所有操作成功,调用 tx.Commit() 提交;若失败,调用 tx.Rollback() 回滚。
示例:转账业务(扣A向B转账,需保证扣钱和加钱原子性)
// 定义模型
type User struct {gorm.ModelName  stringBalance int // 余额
}// 手动事务实现转账
func transfer(db *gorm.DB, fromID, toID, amount int) error {// 1. 开启事务tx := db.Begin()if tx.Error != nil {return tx.Error // 开启事务失败(如数据库连接问题)}// 2. 执行事务内操作var fromUser, toUser User// 查A的账户if err := tx.First(&fromUser, fromID).Error; err != nil {tx.Rollback() // 查询失败,回滚return err}// 查B的账户if err := tx.First(&toUser, toID).Error; err != nil {tx.Rollback()return err}// A的余额不足,回滚if fromUser.Balance < amount {tx.Rollback()return fmt.Errorf("余额不足")}// A扣钱if err := tx.Model(&fromUser).Update("balance", fromUser.Balance - amount).Error; err != nil {tx.Rollback()return err}// B加钱if err := tx.Model(&toUser).Update("balance", toUser.Balance + amount).Error; err != nil {tx.Rollback()return err}// 3. 所有操作成功,提交事务return tx.Commit().Error
}
关键特性
  • 显式控制:开发者完全掌控事务的开启、提交、回滚,适合需要中间判断(如余额校验)的场景。
  • 事务对象 tx:事务内所有操作必须使用 tx 执行,而非原 db 对象,否则操作会脱离事务控制。
  • 错误处理:任何任何一步操作失败,必须调用 tx.Rollback(),否则事务可能长期占用资源。

二、自动事务(Auto Transactions)

自动事务通过 db.Transaction 方法封装事务逻辑,传入一个函数作为事务内的操作,Gorm 会自动开启事务、执行函数、根据函数返回值决定提交或回滚,简化代码。

基本用法
// 自动事务实现转账
func transferAuto(db *gorm.DB, fromID, toID, amount int) error {// 调用 db.Transaction,传入事务内操作的函数return db.Transaction(func(tx *gorm.DB) error {var fromUser, toUser User// 查A的账户if err := tx.First(&fromUser, fromID).Error; err != nil {return err // 返回错误,Gorm 自动回滚}// 查B的账户if err := tx.First(&toUser, toID).Error; err != nil {return err}// 余额不足,返回错误if fromUser.Balance < amount {return fmt.Errorf("余额不足")}// A扣钱if err := tx.Model(&fromUser).Update("balance", fromUser.Balance - amount).Error; err != nil {return err}// B加钱if err := tx.Model(&toUser).Update("balance", toUser.Balance + amount).Error; err != nil {return err}return nil // 无错误,Gorm 自动提交})
}
关键特性
  • 自动处理:无需手动调用 Begin/Commit/Rollback,函数返回 nil 则提交,返回 error 则回滚。
  • 简化代码:减少模板代码,适合逻辑相对简单、无需复杂中间判断的事务场景。
  • 等价性:功能与手动事务一致,只是将事务控制交给 Gorm 自动处理。

三、嵌套事务(Nested Transactions)

嵌套事务指在一个事务内部开启另一个事务,外层事务称为“父事务”,内层称为“子事务”。Gorm 通过数据库的 SAVEPOINT(保存点)实现嵌套事务,支持内层事务独立回滚而不影响外层。

基本用法
// 嵌套事务示例
func nestedTransaction(db *gorm.DB) error {return db.Transaction(func(parentTx *gorm.DB) error {// 父事务:创建用户AuserA := User{Name: "Alice", Balance: 1000}if err := parentTx.Create(&userA).Error; err != nil {return err // 父事务回滚}// 开启子事务(嵌套事务)if err := parentTx.Transaction(func(childTx *gorm.DB) error {// 子事务:创建用户BuserB := User{Name: "Bob", Balance: 500}if err := childTx.Create(&userB).Error; err != nil {return err // 子事务回滚(仅回滚自身操作,不影响父事务的userA)}// 子事务内主动回滚(例如业务判断失败)return fmt.Errorf("子事务主动回滚") // 仅子事务回滚,userB 不会被创建}); err != nil {fmt.Printf("子事务失败:%v,但父事务可继续\n", err)}// 父事务继续执行:更新用户A的余额return parentTx.Model(&userA).Update("balance", 2000).Error})
}
实现原理
  • 保存点(SAVEPOINT):子事务开启时,Gorm 会在父事务中创建一个保存点(如 SAVEPOINT sp1)。
  • 子事务回滚:子事务失败时,仅回滚到保存点(ROLLBACK TO sp1),不影响父事务之前的操作。
  • 父事务提交:只有当父事务最终提交时,所有未回滚的子事务操作才会生效。
关键特性
  • 局部回滚:子事务回滚仅影响自身操作,父事务可继续执行或独立回滚,适合复杂业务中“部分步骤允许失败”的场景。
  • 数据库支持:依赖数据库对 SAVEPOINT 的支持(如 MySQL、PostgreSQL 支持,SQLite 部分支持)。
  • 嵌套层级:理论上支持多层嵌套,但层级过深可能影响性能,建议控制在 2-3 层以内。

四、事务的注意事项

  1. 操作必须使用事务对象:事务内的所有数据库操作(Create/Update/Find 等)必须通过 tx 执行,否则会绕过事务,导致数据不一致。
  2. 锁冲突与超时:长事务可能导致锁冲突(如并发修改同一行),建议事务执行时间尽可能短。
  3. 索引与性能:事务内的查询应确保走索引,避免全表扫描导致事务阻塞。
  4. 嵌套事务的限制:子事务无法单独提交,必须依赖父事务最终提交;若父事务回滚,所有子事务操作都会被回滚。

总结

  • 手动事务:显式控制 Begin/Commit/Rollback,适合复杂逻辑和中间判断。
  • 自动事务:通过 db.Transaction 自动管理事务,简化代码,适合简单场景。
  • 嵌套事务:基于保存点实现,支持子事务局部回滚,适合复杂业务中部分步骤可失败的场景。

根据业务复杂度和灵活性需求选择合适的事务方式,核心目标是保证数据操作的原子性。

http://www.dtcms.com/a/532882.html

相关文章:

  • Ubuntu24.04安装好Mysql8后,检查mysql占用的内存和磁盘
  • 阿里云申请域名后网站海外网络加速器
  • Orleans ILifecycleParticipant 生命周期管理详细分析
  • 企业门户网站建设方案后台管理wordpress多级tree分类目录
  • Spring XML AOP配置实战指南
  • 什么人需要网站建设柳州网站开发公司
  • 做纯净系统的网站产品做国外网站有哪些
  • 商贸公司网站建设兴城泳装电子商务网站建设
  • 张祥前统一场论中的洛伦兹变换:多层次的应用与全新内涵
  • 网安面试题收集(4)
  • 高端上海网站设计公司价格wordpress 打赏
  • Yolo_lableimg_env
  • 【09】C语言中的格式输入函数scanf()详解
  • 鼠键共享工具
  • 个人网站备案 拍照装修网店
  • 投资,如何获得估值回归的收益?
  • 专业上海网站建设公司排名住房和城乡建设部网站杂志
  • 边界扫描测试原理 4 -- 保持状态
  • 国外服务器网站打开慢重庆公司起名
  • 个人怎样做旅游网站清新太和做网站
  • 电商系统设计:运费
  • Ceph分布式存储
  • 网站建设销售职责网站开发与运维收费明细
  • 破解空间网站十堰网站建设怎么做
  • 网站价值评估 php东莞住房和建设局网站
  • 11-14强制类型转换
  • redis中的数据类型
  • 2025年10月25日(星期六)骑行哈马者游记
  • 数据结构 —— 堆
  • 基于阿里云SDK的DDNS系统:架构设计与性能优化实战