错误做法是每个 handler 中调用 gorm.open() 创建新 db 实例,正确做法是在 app.run() 前全局初始化并发安全的 *gorm.db 实例并导出为包级变量,显式调优连接池、单独执行 automigrate、使用原生字段标签、事务需基于同一实例、日志监控按实例注册。

Beego 项目里直接用 gorm.Open() 在每个 handler 里建新 DB 实例,是错的——连接池被绕过,高并发下会耗尽数据库连接,且事务、日志、钩子等能力全部失效。
全局初始化一个 *gorm.DB 实例,别在 controller 里重复 open
Beego 的 app.Run() 启动前就该完成 GORM 初始化,并导出为包级变量。GORM 的 *gorm.DB 是并发安全的,内部已封装 *sql.DB 连接池,不需要、也不应该每次请求都重连。
- 在
models/init.go或main.go开头调用一次gorm.Open(),传入驱动(如mysql.Open(dsn))和配置(&gorm.Config{...}) - 用
db.SetMaxOpenConns()、db.SetMaxIdleConns()显式调优,别依赖默认值(默认0表示不限制,极易打爆 MySQL) - 别把
db存在beego.AppConfig或 context 里传递;直接import "yourproject/models"用导出的models.DB - 错误示范:
func UserHandler() { db, _ := gorm.Open(...) }—— 每次请求新建实例,连接池形同虚设
模型注册和迁移必须对每个 *gorm.DB 单独执行
GORM 不像 Beego ORM 那样靠全局 RegisterModel 统一管理模型。模型绑定发生在 AutoMigrate 阶段,且只作用于调用它的那个 DB 实例。
-
DB.AutoMigrate(&User{}, &Order{})必须显式调用,且只能保证当前 DB 实例对应的库表结构同步 - 如果你有 MySQL 和 PostgreSQL 两个实例(
mysqlDB、pgDB),就得分别调用mysqlDB.AutoMigrate(...)和pgDB.AutoMigrate(...) - 字段标签用 GORM 原生语法,例如
gorm:"primaryKey;autoIncrement",不是 Beego ORM 的orm:"auto;pk",混用会静默失败 - 外键、索引、软删除需主动声明:
gorm:"foreignKey:UserID"、gorm:"index"、gorm.DeletedAt
事务控制必须基于同一个 *gorm.DB 实例开启
GORM 的 Transaction 方法返回一个新的 *gorm.DB 实例,它绑定了底层 *sql.Tx。这个实例只能用于当前事务内的操作,不能跨 goroutine 或跨 handler 复用。
- 正确做法:在 handler 或 service 入口处用
DB.Transaction(func(tx *gorm.DB) error { ... })包裹业务逻辑 - 错误做法:先
tx := DB.Begin(),再把tx传给多个函数调用——一旦某个函数 panic 或提前 return,tx.Rollback()很可能漏掉 - 嵌套事务不自动生效:GORM 默认不支持 savepoint,
tx.Transaction(...)在已开启事务的tx上调用,只是普通代码块,不是子事务 - 别在事务里调用其他 DB 实例(比如
pgDB.Create()),GORM 事务严格限定在单个*sql.DB范围内
Beego 中间件与 GORM 日志/监控要单独注入每个 DB 实例
GORM 的 logger、prometheus 等插件通过 Use() 方法注册,它是实例级的,不是全局单例。不同 DB 实例的日志输出、指标采集必须各自配置。
- 初始化 MySQL 实例后立即调用
mysqlDB.Use(logger.New(...)),PostgreSQL 实例同理,别只配一个 - 如果共用同一份 logger 配置,注意
log.WithContext()传入的 context 是否携带了 Beego 的 requestID —— 否则日志无法关联到具体 HTTP 请求 - 连接池指标(如
sql.OpenConnections)需按 DB 实例分别暴露,Prometheus 的Collector要绑定到具体*gorm.DB - 别在
init()里全局gorm.DefaultCallback...修改回调——GORM v2 已移除全局回调,所有扩展必须走Use()
最易忽略的一点:Beego 的 Controller.Data["json"] = obj 直接序列化时,GORM 模型里的 gorm.Model 字段(如 ID、CreatedAt)默认会输出,但若你用了 Select() 或 Joins().Preload(),结果结构可能和原始模型不一致——这时候别依赖结构体反射自动转 JSON,显式构造 map 或用 mapstructure 控制字段白名单。











