bun是go生态中sql-first的查询构建器与orm混合工具,强调显式sql、结构体映射与事务控制;集成gin时需注意连接池配置、*bun.db传递、errnorows手动判断及事务上下文绑定。

Bun 是 Go 生态中一个轻量、高性能、SQL-first 的 ORM(更准确说是“query builder + ORM hybrid”),和 GORM 完全不同路线:它不隐藏 SQL,鼓励显式编写查询,同时提供结构体映射、事务、预编译、钩子等能力。如果你已习惯手写 SQL、重视可读性与可控性,又不想裸写 database/sql,Bun 是比 GORM 更合适的选择。
但直接套用 Gin + Bun 会踩几个典型坑——比如连接未复用、事务上下文丢失、错误未透出、日志无 SQL 绑定。下面直奔实操。
初始化 Bun 连接池时必须显式配置 maxOpen/maxIdle
很多人用 bun.NewDB 后没调 SetMaxOpenConns 和 SetMaxIdleConns,导致高并发下连接耗尽或频繁重连。
-
maxOpenConns建议设为50~100(MySQL 默认 151,别超) -
maxIdleConns建议设为20~30,避免空闲连接被服务端 kill(如 MySQLwait_timeout=28800) - 务必调用
db.Ping()验证连接,失败时 panic 或返回 error,不能静默忽略
示例:
db := sql.Open("mysql", dsn)
db.SetMaxOpenConns(80)
db.SetMaxIdleConns(25)
if err := db.Ping(); err != nil {
log.Fatal("failed to ping db:", err)
}
bunDB := bun.NewDB(db, dialect)
Gin 中间件里传 Bun DB 实例要用 *bun.DB,不是 sql.DB
常见错误是把 *sql.DB 直接塞进 c.Set("db"),后续用 bun 方法时报 undefined ——因为 bun 的查询方法(如 .NewSelect())只挂在 *bun.DB 上。
- 注册中间件时存
*bun.DB:c.Set("bun", bunDB) - Handler 中取:
bunDB := c.Value("bun").(*bun.DB) - 别在 handler 里重复 new
*bun.DB,那会绕过连接池
否则你会看到 panic: runtime error: invalid memory address 或 nil pointer dereference。
Query 方法不自动处理 ErrNoRows,必须手动判断
Bun 的 .Scan() 和 .Select() 在查不到数据时不 panic,而是返回 sql.ErrNoRows,但 Gin 默认不拦截这个 error,容易漏判。
- 查单条用
.Scan()后必须检查 error:if errors.Is(err, sql.ErrNoRows) { ... } - 查列表用
.Select()返回nil切片 +nilerror,不用特殊处理 - 别依赖
len(res) == 0判断不存在 —— 网络中断、权限不足也会返回空切片但带 error
错误写法:
err := bunDB.NewSelect().Model(&user).Where("id = ?", id).Scan(c.Context(), &user)
// 忘了 err != nil 就直接 return user → 可能返回零值结构体
事务必须绑定 Gin Context,且 commit/rollback 要配对
Bun 的事务对象 *bun.Tx 不自带 context 绑定,Gin 的 c.Request.Context() 无法自动透传到事务操作里。若不做显式传递,超时或 cancel 时事务不会自动 rollback。
- 开启事务:用
bunDB.NewTx(ctx, nil),传入c.Request.Context() - 所有查询/更新都用
tx.NewSelect() / tx.NewUpdate(),不能混用bunDB - 必须用
defer tx.Close()或显式tx.Commit()/tx.Rollback() - 别在 defer 里写
if err != nil { tx.Rollback() }——tx.Close()本身就会 rollback 未 commit 的事务
遗漏 tx.Close() 是最隐蔽的坑:连接不释放、锁不释放、后续请求卡死。
c.AbortWithStatusJSON 得配合 Bun 的 error 类型做细粒度判断,而不是统一 500。











