gorm v2 常见问题有三:连接失败因误用 v1 的 gorm.open("mysql", dsn),须改用 sql.open + mysql.new;建表失败因主键非 id 或字段未加 tag;查不到数据因 where 传 struct 时零值参与条件,应链式调用或用 map 过滤。

直接上手 GORM v2,最常卡在连接失败、查不到数据、建不出表这三件事上。只要避开初始化和模型定义的几个硬性规则,后续 CRUD 就很顺。
gorm.Open 为什么一运行就 panic
不是 DSN 写错了,而是用了 GORM v1 的写法却装了 v2 ——gorm.Open("mysql", dsn) 在 v2 中已彻底移除,直接报 unsupported driver。
- 必须先用
sql.Open拿到*sql.DB,再传给gorm.Open - 驱动要包一层:
mysql.New(mysql.Config{Conn: sqlDB}),不能直接传mysql.Open(dsn) -
sqlDB.SetMaxOpenConns()和SetMaxIdleConns()必须在gorm.Open前调,否则无效 - DSN 中的
&是普通字符(Go 字符串里写&即可),不是 HTML 实体&
AutoMigrate 为什么没建表 or 字段全是 id/created_at
结构体字段根本没被 GORM 识别出来,它退回到了默认字段集。主键和字段命名是硬门槛。
- 主键字段名必须是
ID(首字母大写),或显式加标签:ID uint `gorm:"primaryKey"`;id、Id、UserId都不认 - 非主键字段默认转蛇形小写:
UserName→user_name;想保留原名得加gorm:"column:UserName" - 如果不想自动复数化表名(
users→user),初始化时加配置:&gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}} -
AutoMigrate不删字段、不改类型,只新增字段和索引;想重试请先db.Migrator().DropTable(&User{})
Where 查询传 struct 为什么查不到数据
Where(&User{Name: "Alice", Age: 0}) 这种写法会把所有字段(包括零值)塞进 WHERE 条件,最终生成 WHERE name = 'Alice' AND age = 0,而你本意可能是“只按非空字段过滤”。
- GORM 对 struct 的零值字段(
0、""、false、time.Time{})**不忽略**,而是照常参与条件拼接 - 安全做法是链式调用:
Where("name = ?", name).Where("age > ?", 0) - 动态条件建议手动构建
map[string]interface{},只放非零/非空的 key-value - 字段名大小写敏感:数据库列是
user_name,struct tag 就得写gorm:"column:user_name"
CreateInBatches 批量插入要注意什么
这是 GORM v2 官方推荐的批量插入方式,但默认行为容易引发数据不一致。
- 别用
db.Create(&users)传切片——v2 默认仍是一条一条插,性能差且易耗尽连接池 -
CreateInBatches默认不开启事务,某一批失败,前面批次已提交;如需强一致性,必须显式包裹db.Transaction() - MySQL 不支持
RETURNING,所以即使加了Clauses(clause.Returning{}),ID 也不会回填到 struct 切片中 - 批次大小建议设为 100–500;太小失去批量意义,太大可能触发 MySQL
max_allowed_packet限制
最常被忽略的是:GORM v2 的钩子(如 BeforeCreate)默认不被 Save() 触发,必须用 Create() 或 Update();还有读写分离场景下,事务内所有操作强制走主库——哪怕你只写了 Find(),也得注意时序一致性。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











