GORM v2 必须用 dialector 接口(如 mysql.Open(dsn))并显式安装驱动,DSN 需含 parseTime=True 和 charset=utf8mb4(MySQL)或 url.QueryEscape 编码(PostgreSQL),*gorm.DB 必须指针传递且不可复用变量。

gorm.Open() 的参数签名在 v1 和 v2 中完全不同
你写 gorm.Open("mysql", dsn) 能跑通,大概率是在用 v1;而 gorm.Open(mysql.Open(dsn), &gorm.Config{}) 才是 v2 的正确写法。v2 彻底移除了字符串驱动名,改用 dialector 接口——这意味着你必须显式导入并调用对应驱动的 Open 函数。
常见错误:只执行 go get gorm.io/gorm 却漏装驱动,比如没运行 go get gorm.io/driver/mysql,结果编译不报错、运行时 mysql.Open(dsn) panic 报 “undefined: mysql”。
- 确认已执行:
go get gorm.io/driver/mysql(MySQL)、go get gorm.io/driver/postgres(PostgreSQL)等 - v2 中不再支持
import _ "github.com/jinzhu/gorm/dialects/mysql"这类旧导入方式 - DSN 字符串本身不带驱动名,
mysql.Open()已封装解析逻辑,别再手动拼接协议前缀
DSN 里 time.Time 和字符集参数不是可选,而是必填
漏掉 parseTime=True,time.Time 字段会变成零值或 panic;漏掉 charset=utf8mb4,中文插入直接报错或乱码。这两个参数在 MySQL DSN 中几乎从不省略。
PostgreSQL 虽默认支持 time.Time,但若 DSN 中含特殊字符(如用户名带 @ 或密码含 /),不提前用 url.QueryEscape() 编码,就会解析失败,错误提示却显示 “pq: password authentication failed”,完全误导排查方向。
- MySQL DSN 示例:
root:pass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local - PostgreSQL DSN 示例:
user=%s&password=%s&host=localhost&port=5432&dbname=mydb&sslmode=disable,其中%s需由url.QueryEscape()填充 -
loc=Local决定 time.Time 解析时区,生产环境建议统一设为UTC避免夏令时跳变
*gorm.DB 必须作为指针类型保存和传递
结构体字段声明成 DB gorm.DB(值类型)然后赋值 gorm.Open() 返回的 *gorm.DB,Go 编译器直接报错:cannot assign *gorm.DB to dc.DB (type gorm.DB)。这不是 GORM 的 bug,是 Go 类型系统在拦你。
更隐蔽的问题是:即使你绕过编译(比如用 interface{} 中转),运行时调用 db.Create() 可能 panic,因为 gorm.DB 内部含连接池、回调链等不可复制状态,按值传递会破坏其一致性。
- 结构体字段必须定义为
DB *gorm.DB - 初始化后应校验:
sqlDB, _ := db.DB(); sqlDB.Ping(),避免空指针或连接未建好就使用 - 不要在 HTTP handler 里每次请求都
gorm.Open()—— 连接池初始化开销大,且易触发文件描述符耗尽
多个数据库必须用不同变量名隔离,不能复用同一 *gorm.DB 变量
写 var db *gorm.DB; db = gorm.Open(...mysql...); db = gorm.Open(...postgres...),后一次赋值直接覆盖前一次。所有后续 db.Create() 全部发到 PostgreSQL,MySQL 库彻底失联,且没有任何编译或运行时警告。
事务也严格绑定单个 *gorm.DB 实例:db.Transaction(func(tx *gorm.DB) error { db.Create(); otherDB.Create() }) 中,只有 db.Create() 在事务内,otherDB.Create() 是独立连接、独立事务、甚至可能连向另一台物理机器。
- 每个库单独声明变量:
var userDB, logDB, reportDB *gorm.DB - 各自调用
gorm.Open(),传入对应驱动和 DSN - 跨库原子性不存在于 GORM 层,得靠业务层补偿:比如先写主库成功,再发消息队列异步写日志库,失败则告警+人工核对
MaxOpenConns)不显式设置时默认是 10,高并发场景下极易成为瓶颈。这问题不会在启动时报错,而是在压测时突然大量请求超时——它藏得深,但改起来就一行代码。


















