Iris MVC 不提供事务管理,Controller 中直接调用 db.Exec() 会因每次使用独立连接和自动提交导致数据不一致;必须通过 iris.Context 传递 *sql.Tx 并显式 Commit/Rollback。

Iris 框架的 MVC 层本身不提供事务管理能力,事务必须由底层数据访问层(如 sql.Tx 或 ORM 的事务对象)显式控制;控制器方法里不能靠框架自动开启/提交/回滚,必须手动传递并管理 *sql.Tx 或等效事务句柄。
为什么 Controller 方法里直接用 db.Exec() 无法保证事务?
Iris MVC 不拦截或包装控制器方法的执行上下文,所有数据库操作都走原生 Go SQL 接口。如果你在 Get() 或 Create() 中多次调用 db.Exec(),每次都是独立连接、独立事务(默认 auto-commit),中间出错时前面的写入已不可逆。
- 常见错误现象:
user inserted but order not created,日志里没报错但数据不一致 - 根本原因:没复用同一个
*sql.Tx实例,也没显式调用tx.Commit()或tx.Rollback() - 别指望
app.Register()注入的*sql.DB能自动切换到事务模式——它只是连接池,不是事务上下文
如何把 *sql.Tx 安全传入 Controller 并正确使用?
不能把 *sql.Tx 直接注册为全局依赖(会并发冲突),也不能在 Controller 字段里存它(MVC 控制器是单例或请求作用域,Tx 是一次性的)。正确做法是:在路由处理链中开启事务,通过 iris.Context 临时携带,并在 Controller 方法签名中接收。
- 在中间件或 BeforeActivation 阶段开启事务:
tx, err := db.Begin(),失败则ctx.StopWithStatus(500) - 用
ctx.Set("tx", tx)把事务句柄挂到当前请求上下文 - Controller 方法签名加参数:
func (c *OrderController) Create(ctx iris.Context, tx *sql.Tx, order Order) string—— Iris 会自动从ctx中提取"tx"值注入 - 方法末尾必须显式
tx.Commit()或tx.Rollback();推荐用defer+recover保底回滚
事务失败时怎么统一回滚且返回标准错误?
单纯 panic *BizError 不会触发 tx.Rollback(),必须把事务清理逻辑和错误响应耦合起来。最稳的方式是用 ctx.StopWithStatus() 组合手动回滚。
- 不要只写
panic(&BizError{...})—— recover 中拿不到tx,无法回滚 - 正确流程:
if err != nil { tx.Rollback(); ctx.StatusCode(400); ctx.JSON(BizError{...}); ctx.StopWithStatus(400); return } - Commit 后也要检查错误:
if err := tx.Commit(); err != nil { ctx.StatusCode(500); ...; return },因为网络中断、锁超时等可能发生在 commit 阶段 - 避免在多个 Controller 方法间传递
tx—— 一个 HTTP 请求只应对应一个事务,跨方法意味着你设计错了分层边界
事务不是加个注解就生效的魔法,它要求你清晰知道哪几行 SQL 必须原子执行、谁负责开启、谁负责收尾。Iris MVC 只帮你把请求路由到方法,剩下的——包括 Begin()、Rollback()、Commit() 的时机和顺序——全得你自己钉死。漏掉任意一环,数据就可能半截写入。











