gorm.Open必须用对应驱动的Open函数(如mysql.Open、postgres.Open),不可传字符串;DSN解析和连接行为由驱动实现,各数据库差异大,混用会导致nil返回或首次查询卡死。

gorm.Open 传参时必须用对应驱动的 Open 函数
不同数据库的 DSN 解析逻辑和连接行为差异极大,GORM 不做统一抽象,而是把方言适配完全交给驱动层。直接传字符串给 gorm.Open("mysql", dsn) 是旧版写法,新版(v2+)要求显式调用驱动的 Open 函数。
常见错误是漏掉驱动导入下划线或错用函数:
- MySQL:必须
import "gorm.io/driver/mysql",然后gorm.Open(mysql.Open(dsn), &gorm.Config{}) - PostgreSQL:用
postgres.Open(dsn),不是mysql.Open - SQLite:
sqlite.Open("test.db"),路径不带协议前缀 - 达梦数据库:需
dm.Open(dsn),且 DSN 格式为user:pass@tcp(host:port)/db
漏掉 _ 导入或传错 Open 函数会导致 gorm.Open 返回 nil 或首次查询卡死,错误信息常为 "invalid connection",但不会在 Open 阶段报错。
AutoMigrate 在不同数据库上行为不一致的根源
AutoMigrate 不是“建表命令”,它只比对 Go struct 和当前库中表结构的差异,并生成 ALTER 语句。但各数据库对 ALTER 的支持程度、类型映射规则、严格模式开关完全不同。
典型表现:
- MySQL 8+ strict mode 下,已有表中字段类型不匹配(如 Go
int64对应已有TINYINT),AutoMigrate直接跳过整张表,无 error、无日志 - PostgreSQL 对大小写敏感,默认将 struct 字段名转小写,若手动建表用大写字段,迁移会失败或新建冗余列
- SQLite 不支持
ALTER COLUMN,改字段类型只能靠重建表,AutoMigrate可能静默忽略变更
务必在调用前验证连接有效性:db.Migrator().CurrentDatabase() 返回非空字符串,再执行 AutoMigrate。
跨数据库共用模型时 struct tag 必须兼顾方言特性
同一个 struct 用在 MySQL 和 PostgreSQL 上,gorm:"type:json" 在 MySQL 5.7+ 有效,但在 PostgreSQL 需要 gorm:"type:jsonb",否则建表失败或类型被降级为 text。
其他高频冲突点:
- 主键策略:
gorm:"primaryKey;autoIncrement"在 SQLite 中不生效(默认 INTEGER PRIMARY KEY 自增),需额外加gorm:"primaryKey;autoIncrement:false"+ 手动设置 - 时间字段:
gorm:"type:datetime"在 MySQL 可用,在 PostgreSQL 应用gorm:"type:timestamptz"避免时区丢失 - 唯一索引:
gorm:"uniqueIndex"生成的 SQL 在 SQL Server 上可能因索引名超长被截断,需显式指定gorm:"index:idx_name,unique"
不要依赖“一次定义,到处运行”——跨方言时,模型 struct 往往需要条件编译或运行时动态 patch tag。
事务与锁语句无法跨数据库通用
事务本身是标准 SQL 特性,但具体行为严重依赖后端实现。例如:
- MySQL 的
SELECT ... FOR UPDATE默认是当前读,PostgreSQL 需显式加FOR UPDATE才阻塞,否则快照隔离下查不到未提交变更 - SQL Server 要求事务内所有操作必须在同一个连接上,而 GORM 的 session 复用不当(如
db.Session(&gorm.Session{PrepareStmt: true}))可能触发连接切换,导致ROLLBACK失效 - SQLite 不支持真正的并发写事务,
db.Transaction()内部其实是文件锁,高并发下易报"database is locked"
写业务逻辑时,不要假设 Transaction 在所有库上语义一致;涉及行锁、乐观锁、隔离级别控制的场景,必须按目标数据库单独验证。
最易被忽略的是:方言差异不只在建表或查询语法,更藏在连接池行为、错误码含义、空值处理、甚至 time.Time 的序列化方式里。别省那几行适配代码,每个数据库都值得一份独立验证路径。


















