Revel框架不提供自动事务管理,所有事务必须手动开启并显式提交或回滚。需调用c.DB.Begin()获取*sql.Tx,用defer tx.Rollback()防泄漏,所有SQL操作必须传入该tx,不可混用c.DB;禁止在Filter中统一包裹事务,SQLite需警惕文件锁问题。

Revel 中事务必须手动开启,框架不自动包裹
Revel 本身不提供控制器级事务自动管理(比如像 Rails 的 transaction 块或 Spring 的 @Transactional),所有数据库事务都得由你显式控制。如果你在 Controller 方法里直接调用 db.Insert() 或 db.Update() 而没开事务,每个操作都是独立的自动提交语句——这意味着中间出错时无法回滚,数据一致性完全靠代码逻辑兜底。
使用 DB.Begin() + defer tx.Rollback() 是唯一可靠模式
Go 的 database/sql 驱动要求事务对象(*sql.Tx)必须显式 Commit() 或 Rollback(),且不能跨 goroutine 传递。Revel 的 c.DB 是 *sql.DB 实例,不是事务上下文,所以必须自己调用 c.DB.Begin()。
- 务必在事务开始后立即加
defer tx.Rollback(),否则任何提前 return(包括 panic)都会导致连接泄漏 - 所有模型操作(
tx.Query()、tx.Exec()、自定义 model 方法)必须传入该tx对象,不能继续用c.DB - Revel 的
DB.Insert()等方法只接受*sql.DB,不支持*sql.Tx,所以你要自己写原生 SQL 或封装模型方法适配tx
示例片段:
func (c App) CreateOrder() revel.Result {
tx, err := c.DB.Begin()
if err != nil {
return c.RenderError("DB.Begin failed: " + err.Error())
}
defer tx.Rollback() // 关键:防止漏掉
// 手动执行插入(不能用 c.DB.Insert)
_, err = tx.Exec("INSERT INTO orders (user_id, total) VALUES (?, ?)", c.Params.Get("user_id"), 99.9)
if err != nil {
return c.RenderError("Insert order failed: " + err.Error())
}
_, err = tx.Exec("INSERT INTO items (order_id, name) VALUES (?, ?)", lastInsertID, "book")
if err != nil {
return c.RenderError("Insert item failed: " + err.Error())
}
err = tx.Commit()
if err != nil {
return c.RenderError("Commit failed: " + err.Error())
}
return c.Redirect("/orders")
}
别试图在 Filter 或 Interceptor 里统一套事务
有人想在 BeginFilter 里开事务、FinishFilter 里提交,这是危险的。原因很实在:
- Revel 的 filter 链是全局的,不是 per-request isolation;一个请求中途 panic,filter 可能根本没走到 Finish 阶段
-
c.DB是共享的*sql.DB,你不能把*sql.Tx存进c再让后续 filter 读取——类型不匹配,且 Go 没有上下文注入机制 - 事务必须与具体 SQL 执行严格绑定在同一
*sql.Tx实例上,跨函数传递需显式参数,无法“隐式继承”
SQLite 文件锁和并发事务要特别小心
如果你用的是 SQLite(常见于 Revel 开发期),注意它默认是 DEFERRED 事务,且整个数据库文件级锁。多个并发请求同时进 Begin() 可能阻塞甚至超时,错误信息通常是 database is locked 或 busy timeout expired。
- 避免长事务:不要在事务里做 HTTP 请求、文件读写、sleep 等耗时操作
- 生产环境强烈建议换 PostgreSQL 或 MySQL;SQLite 仅适合单用户开发验证
- 若坚持用 SQLite,可在
sql.Open时加_timeout=5000参数,但治标不治本
真正难处理的从来不是“怎么写事务”,而是“哪些操作必须包进同一事务”以及“失败时怎么向用户解释”。这两点没法靠框架自动解决,得你盯着业务逻辑一条条对齐。


















