Gin + GORM 最小可行集成仅需30行内,关键在正确配置DSN三参数(parseTime=true、loc=Local、charset=utf8mb4)、合理使用AutoMigrate、安全传递DB实例及注意指针传参。

直接上手就能跑通,不需要封装、不搞依赖注入、不加中间件拦截——Gin + GORM 最小可行集成就是 30 行以内搞定。关键不是“怎么搭架子”,而是“怎么避免连不上、查不到、写不进”。
初始化 gorm.DB 时必须处理的三个连接参数
很多人卡在 failed to connect database,其实根本不是驱动没装,而是 DSN 里三个参数没配对:
-
parseTime=true:否则time.Time字段会解析失败或变成零值 -
loc=Local或loc=Asia%2fShanghai:MySQL 默认用系统时区,Go 连接时不显式指定会导致时间字段偏移 -
charset=utf8mb4:不用utf8(它只支持 3 字节 UTF-8),否则 emoji 和部分生僻字存进去是???
正确示例:user:pass@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true&loc=Local
AutoMigrate 不是万能的,但不用它一定出问题
开发阶段靠 DB.AutoMigrate(&User{}) 同步结构最省事,但它有硬限制:
- 只建表、加字段、改非空约束,不会删字段、不会改类型、不会删索引
- 如果已有表字段类型和 struct 不一致(比如 struct 是
int64,DB 里是VARCHAR),AutoMigrate会静默跳过,后续Create直接报错 - 首次运行必须执行一次,否则表不存在,
INSERT报Error 1146: Table 'xxx' doesn't exist
建议开发期加一行:DB.AutoMigrate(&User{});上线后禁用,改用 SQL 迁移脚本。
Gin 路由里用 DB 的两种安全传参方式
别把 *gorm.DB 塞进 gin.Context.Set,也别全局变量裸奔——容易并发冲突或泄露连接:
- 推荐:在
main()初始化好DB,作为参数传给路由注册函数,比如setupRouter(DB) - 次选:用
gin.Engine.Use()注入中间件,把DB绑到c.Keys,但必须确保DB是线程安全的(GORM v2+ 默认是) - 绝对避免:
var DB *gorm.DB全局声明后,在 handler 里直接调用DB.Create(...)—— 没做错误检查,panic 会直接崩掉整个 HTTP server
简单写法示例:
func setupRouter(db *gorm.DB) {
r := gin.Default()
r.GET("/users", func(c *gin.Context) {
var users []User
if err := db.Find(&users).Error; err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, users)
})
}
真正麻烦的从来不是“怎么连上”,而是“连上了但查不到数据”——往往是因为 gorm.Model(&u).Where(...).First() 忘了加 & 取地址,或者 Scan() 时传了值而非指针。这些细节不试一遍,光看文档永远踩坑。


















