Beego中事务必须调用o.Begin()获取事务对象,不可手拼"BEGIN"SQL;所有ORM操作须基于该事务对象,且每步需检查error,配合committed标记+defer控制Rollback/Commit。

Beego 中事务必须用 Begin 开启,不能手拼 "BEGIN"
很多人在 Beego 里写 db.Exec("BEGIN") 或 orm.Raw("BEGIN").Exec(),以为这就开启了事务 —— 实际上这只是发了一条 SQL,*sql.Tx 对象根本没创建。后续所有 orm.Insert()、orm.Update() 都走默认连接池,完全不在事务里。
正确做法只有两个:
- 用
o.Begin()(orm.Ormer实例)获取事务对象,所有 ORM 操作都基于它 - 或用原生
db.Begin()+tx.Query/Exec,但此时不能再混用orm方法
Beego ORM 的事务对象本身不支持嵌套,也不自动感知 Context 取消;超时或 panic 后,必须靠 defer 显式收尾。
defer tx.Rollback() 必须配状态标记,否则会报 sql.ErrTxDone
直接写 defer tx.Rollback() 是错的:一旦 tx.Commit() 成功,再执行它就会返回 sql: transaction has already been committed or rolled back。这不是 bug,是事务已终结的正常信号。
立即学习“go语言免费学习笔记(深入)”;
必须用布尔变量控制:
- 声明
committed := false在Begin()后立刻 defer func() { if !committed { tx.Rollback() } }()-
tx.Commit()成功后立即设committed = true
别在错误分支里单独写 tx.Rollback() 再 return —— defer 已覆盖,重复调用反而触发错误。
Beego ORM 事务中每步操作都要检查 error,不能跳过
ORM 方法如 o.Insert()、o.Update()、o.Read() 都可能失败。常见错误是只检查最后一步,或用 _ = o.Insert(...) 忽略错误,导致前面已写入的数据无法回滚。
关键点:
- 每个 ORM 调用后必须
if err != nil立即处理,不能“先做完再统一判断” - 查单行用
o.Read(),查不到时返回orm.ErrNoRows;查多行用o.QueryTable().Filter().All(),空结果err == nil - 不要把多个更新塞进一个
o.Raw().Exec()—— 错误定位难,回滚粒度粗
例如转账场景:先扣 A 账户,再加 B 账户。若第二步失败,第一步的 o.Update() 必须能被回滚 —— 这只在同一个 o(即事务对象)下才成立。
Beego 的 DoTransaction 封装只是语法糖,本质仍要手动控制
Beego 提供了 o.DoTransaction(func(o orm.Ormer) error { ... }),看起来像自动回滚,但它内部仍是帮你做了 Begin → defer Rollback → Commit 的模板。你依然要遵守所有规则:
- 闭包内所有 ORM 操作必须用传入的
o,不能用全局orm.NewOrm() - 闭包返回
error才触发回滚;返回nil才提交 - 闭包里发生 panic 会被捕获并转为 error,但日志堆栈会丢失原始位置,调试困难
复杂业务建议不用 DoTransaction,而是手写带状态标记的 Begin/Commit/Rollback 流程 —— 控制更明确,出问题时容易定位到哪一行 SQL 卡住。
最容易被忽略的是:事务对象失效后继续调用任何 ORM 方法都会 panic。比如 tx.Rollback() 返回 error 后,还去 o.Read(),程序立刻崩溃。务必确保所有数据库操作都在 Commit 或 Rollback 之前完成。


















