必须全局单例初始化 *gorm.db 并复用,禁止在每个 controller 中调用 gorm.open();需显式配置连接池参数、事务须显式控制并全程使用同一事务句柄,preload 要慎防 n+1,复杂查询优先 raw()。

别在每个 Controller 里调用 gorm.Open() —— 这会导致连接泄漏、性能崩盘,且违背 GORM 并发安全的设计本意。
全局单例初始化 DB 实例,不是可选项
Beego 启动时只应调用一次 gorm.Open(),把返回的 *gorm.DB 赋值给包级变量并导出。这个实例本身是并发安全的,内部已封装 *sql.DB 连接池,复用它即可。
- 在
utils/db.go中初始化,从beego.AppConfig读取mysql.dsn(如root:pass@tcp(127.0.0.1:3306)/myapp?charset=utf8mb4&parseTime=true&loc=Local) - 必须调用
db.DB().SetMaxOpenConns()和db.DB().SetMaxIdleConns()显式配置连接池,否则默认值(0 / 2)极易打满 - 不要在
init()里直接panic或os.Exit();用beego.Error()记录错误,并让应用继续启动(便于容器健康检查失败时快速暴露)
模型定义要匹配已有表结构,别迷信 AutoMigrate
生产环境几乎不会靠代码建表,而是先由 DBA 或 Flyway 管理 DDL。GORM 模型只需“映射”,不是“定义”。
- 结构体名首字母大写,对应表名(
User→ 默认查users,加func(u User) TableName() string { return "user" }强制对齐) - 字段名首字母大写,用
gorm:"column:xxx"显式绑定数据库列名(如AddTime time.Time `gorm:"column:add_time"`) - 主键必须显式声明:
ID uint `gorm:"primaryKey"`(旧版用gorm.Model嵌入会带CreatedAt/UpdatedAt,若不需要得禁用) -
AutoMigrate仅用于开发联调或测试环境;上线前务必关闭,避免误改线上表结构
事务必须显式控制,不能依赖全局 DB
*gorm.DB 本身不带事务上下文。跨方法传递事务句柄,要用 Session(&gorm.Session{NewDB: true}) 或更稳妥的 Begin() + Commit()/Rollback() 链式调用。
- 在 Service 层开启事务:
tx := utils.DB.Begin(),后续所有操作都基于tx(如tx.Create(&u)) - 切勿在事务中混用
utils.DB和tx—— 它们属于不同连接,无法回滚 - Beego Controller 方法结束前必须显式调用
tx.Commit()或tx.Rollback();建议用defer+recover防 panic 导致未回滚 - 复杂业务需嵌套事务时,用
SavePoint,但注意 GORM 的 SavePoint 不跨连接,仍需确保全程使用同一tx实例
预加载(Preload)和原生 SQL 要分清场景
Preload 看似方便,但 N+1 和笛卡尔积问题在关联层级深时立刻显现;而盲目写原生 SQL 又失去 GORM 类型安全优势。
- 一对多(如 User → Posts):用
Preload("Posts"),GORM 会自动发 JOIN 查询(需确认日志输出是否为单条 SQL) - 多对多或复杂条件:改用
Joins("LEFT JOIN posts ON ...").Where(...),避免Preload的隐式 WHERE 覆盖 - 聚合或窗口函数等高级查询:直接
Raw()+Scan(),别硬套 GORM 模型(例如utils.DB.Raw("SELECT COUNT(*), status FROM orders GROUP BY status").Scan(&results)) - MySQL
limit和 MSSQLtop语法不兼容,若需多数据库支持,不要写死SELECT TOP 100,改用Limit(100)让 GORM 自动适配
最容易被忽略的是连接池参数和事务边界 —— 它们不出现在任何教程示例里,却直接决定服务在压测时是稳如泰山还是秒变 503。











