直接用gorm.open连postgresql会panic,主因是dsn格式错误或未下划线导入gorm.io/driver/postgres驱动;缺sslmode=disable、host/port/user/password/dbname任一参数,或驱动未注册,均导致unsupported driver或nil panic。

直接用 gorm.Open 连 PostgreSQL 会 panic?先检查 DSN 格式和驱动注册
绝大多数连接失败不是代码逻辑问题,而是 dsn 字符串拼错或驱动没导入。GORM v2 要求显式导入 gorm.io/driver/postgres,且必须带下划线空白导入(_ "gorm.io/driver/postgres"),否则 postgres.Open 会返回 nil,gorm.Open 直接 panic。
常见错误现象:failed to connect database: failed to initialize database, got error unsupported driver: postgres 或空指针 panic。
-
host、port、user、password、dbname缺一不可;sslmode=disable在本地开发时必须显式加上,否则默认尝试 ssl=required,连不上就卡住 - 时区建议统一设为
TimeZone=Asia/Shanghai,避免时间字段写入/查询偏差 - 不要把密码硬编码在代码里,改用环境变量读取,比如
os.Getenv("DB_PASSWORD")
AutoMigrate 不建表?确认模型结构、主键和 GORM 标签
AutoMigrate 不是“自动同步”,它只做“单向创建”:有表就跳过,没表才建;字段少就加,字段多不删,类型不一致也不改。如果你改了 struct 但表已存在,AutoMigrate 不会帮你更新字段类型或加索引。
容易踩的坑:
- Go struct 字段名首字母必须大写(导出),否则 GORM 无法映射
- 主键必须叫
ID(大写)或显式加gorm:"primaryKey"标签,否则 GORM 默认用id小写字段,而 PostgreSQL 表里实际是id列,但 GORM 生成 SQL 时可能用错名 - 想让字段对应 PostgreSQL 的
jsonb类型,得加gorm:"type:jsonb";想加唯一约束,写gorm:"uniqueIndex",不能只靠 struct tag 名字猜
事务手动控制时,db.Begin() 后忘了 Commit() 或 Rollback()
GORM 的事务不是自动提交的。调用 db.Begin() 返回一个新 *gorm.DB 实例,所有后续操作(Create、Save、First 等)都必须在这个实例上调用,最后必须显式 Commit() 或 Rollback()。漏掉任一环节,事务就挂起,连接可能被占住,甚至锁表。
典型错误写法:
tx := db.Begin() tx.Create(&user) // 忘了 tx.Commit() → 连接未释放,下次请求可能卡住
正确做法是用 defer + error 判断:
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
Docker 环境下连不上 pg-test 容器?检查网络和 host 名解析
本地跑通不等于 Docker 里能连。Gin 服务容器要访问 PostgreSQL 容器,host 不能写 localhost 或 127.0.0.1 —— 那是指它自己,不是宿主机或另一个容器。
必须满足两个条件:
- 两个容器在同一个自定义 Docker 网络里(不能用默认 bridge)
-
dsn中的host填的是 PostgreSQL 容器名(如pg-test),Docker DNS 才能解析到对应 IP - PostgreSQL 容器启动时暴露了
5432端口(-p 5432:5432),且POSTGRES_PASSWORD环境变量已设
验证方式:进 Gin 容器执行 nc -zv pg-test 5432,能通才说明网络层没问题;再试 psql -h pg-test -U postgres 看数据库层是否响应。
最常被忽略的点:GORM 的 AutoMigrate 在生产环境不该启用,它不支持字段删除和类型变更;上线前务必用正式迁移工具(如 golang-migrate)管理 SQL 变更。还有,db.Close() 必须在程序退出前调用,否则连接池资源不会释放。











