结论:Fiber + GORM 能跑通,但默认配置下易卡在连接、迁移、时间字段解析和事务透传四点;AutoMigrate没建表主因是仅比对结构而不校验连接,需确保*gorm.DB非nil、调用db.Migrator().CurrentDatabase()验证连通性、ID字段显式加gorm:"primaryKey"、MySQL严格模式下类型精确匹配。

直接说结论:Fiber + GORM 能跑通,但默认配置下容易卡在连接、迁移、时间字段解析和事务透传四个地方,不是框架不兼容,而是初始化顺序和实例复用没对上。
为什么 db.AutoMigrate(&User{}) 执行了却没建表
常见现象是日志里没报错、也没输出 SQL,但进数据库查不到表。根本原因不是 GORM 不干活,而是它只做「结构比对」,不校验底层是否真连上了。
- 确保
gorm.Open返回的*gorm.DB非 nil,且调用过db.Migrator().CurrentDatabase()得到非空字符串(否则说明连接失败但被静默吞掉) - 检查 struct tag:主键字段必须带
gorm:"primaryKey",比如ID uint `gorm:"primaryKey"`;否则 GORM 会跳过该模型 - MySQL 8+ 默认 strict mode 下,若已有同名表但字段类型不匹配(如 Go 的
int64对应数据库INT,但表里是BIGINT),AutoMigrate直接跳过,不提示也不报错 - Fiber 启动前必须完成迁移,不能放在某个路由 handler 里异步执行——那会用到另一个 goroutine 的上下文,
db实例可能已失效
time.Time 字段查出来全是零值
这是 MySQL 驱动层解析失败的典型表现,和 Fiber 无关,但常在 Fiber 项目里集中暴露。
- 连接字符串里必须加
parseTime=True和loc=Local(或loc=Asia/Shanghai),例如:"user:pass@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local" - struct 中字段类型必须是
time.Time,不能是*time.Time或自定义类型(除非你实现了Scanner/Valuer) - 如果用了 GORM v2(
gorm.io/gorm),确认驱动是gorm.io/driver/mysql,而不是旧版github.com/go-sql-driver/mysql—— 后者不参与 GORM 的 time 解析流程
事务里调用 service 函数,Rollback 无效
Fiber 路由函数里开了事务,但 service 层用的还是全局 db 变量,导致事务对象没传下去。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 绝对不要在 service 函数里用包级变量
DB *gorm.DB,它和当前事务完全无关 - 把事务 db 实例显式传进去:
func CreateUser(tx *gorm.DB, u *User) error { return tx.Create(u).Error } - 注意
db.Session(...)会生成新实例,得基于事务 db 创建:tx.Session(&gorm.Session{PrepareStmt: true}),而不是DB.Session(...) - 调试时打印
tx.Statement.ConnPool == nil,为false才说明事务有效;为true就是空连接池,rollback 必然失效
Fiber handler 中怎么安全用 GORM 查询单条记录
别无脑用 First,它隐式加 ORDER BY id ASC,在分页或非主键查询场景下反而拖慢性能。
- 查「存在性」或「任意一条」用
Take:db.Where("email = ?", email).Take(&u)→ 不排序,最快 - 查「最早创建」或依赖主键顺序时才用
First:db.Where("status = ?", "active").First(&u)→ 等价于ORDER BY id LIMIT 1 - 两者都返回
gorm.ErrRecordNotFound,但First在有 WHERE 条件时仍强制排序,Take完全不加ORDER BY - 如果需要按其他字段排序(比如
created_at DESC),必须显式写:db.Order("created_at DESC").Limit(1).Find(&u)
最常被忽略的是:Fiber 的中间件和路由 handler 是并发执行的,所有共享的 *gorm.DB 实例必须是线程安全的——GORM 本身是,但你自己封装的 DB 工厂、连接池配置、日志钩子如果用了非原子操作,就可能在高并发下出问题。

















