gorm v2 增删改查需选对方法并控制行为边界:create 用于插入新记录,save 自动判断 insert/update 但易出错;where 不忽略零值;gorm.open 需先 sql.open 再传入,连接池配置须在 gorm.open 前设置。

GORM v2 的增删改查不是“写对语法就行”,而是“选对方法 + 控制行为边界”——用错 Create 和 Save、忽略 Where 对零值的处理、在 gorm.Open 后才配连接池,这三件事占了 80% 的线上问题。
gorm.Open 报 unsupported driver panic 怎么办
这不是 DSN 写错了,是用了 GORM v1 写法却装了 v2。v2 彻底移除了 gorm.Open("mysql", dsn) 这种签名。
- 必须先用
sql.Open("mysql", dsn)拿到*sql.DB - 再传给
gorm.Open(mysql.New(mysql.Config{Conn: sqlDB}), &gorm.Config{}),mysql.New()不可省 -
sqlDB.SetMaxOpenConns(20)和sqlDB.SetMaxIdleConns(10)必须在gorm.Open前调,否则无效 - MySQL 8.0+ 若迁移报外键错误,加
&gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}
Create 和 Save 到底该用哪个
Create 是插入新记录,Save 是保存当前状态(自动判断 INSERT/UPDATE),但这个“自动”非常危险。
- 没主键或主键为 0 时,
Save(&u)可能静默重复插入,也可能覆盖不该动的字段 - 新增一律用
Create:它不看主键,只管插;失败时res.Error明确,res.RowsAffected可信 - 想只插部分字段?用
Select("name", "price").Create(&good) - 想跳过时间戳等默认字段?用
Omit("created_at", "updated_at").Create(&good) - 批量插入直接传切片:
db.Create(&goods),比循环快且事务安全
Where(map[string]interface{}) 查不到数据的真相
Where(map[string]interface{}{"name": "", "age": 0}) 不会忽略空值,而是生成 WHERE name = '' AND age = 0。
- 零值(
""、0、false)照常参与条件拼接 - 要过滤空值,得自己做判断:
if name != "" { db = db.Where("name = ?", name) } - 或者用结构体 +
map[string]interface{}混合方式,但别依赖 GORM 自动跳过 - 查单条推荐
Find(&post, id)而非First(),它不修改变量地址,也更明确
事务里 Exec 后不检查 RowsAffected 会出大事
尤其做幂等更新或软删除时,RowsAffected == 0 往往比 err 更关键。
-
Exec返回sql.Result,必须显式调res.RowsAffected()确认是否真改了数据 - MySQL 插入后要 ID,用
res.LastInsertId();PostgreSQL 不支持,得换RETURNING或Scan - 事务中多个
Exec,每个都要检查自己的Result,不能只看最后一个 -
QueryRow().Scan()别连写,中间没机会判断row.Err();sql.ErrNoRows要单独处理,不是所有“查无结果”都该 panic
最易被忽略的是:sql.Open 不真连数据库,上线前必须加 db.Ping();time.Time 字段没配 parseTime=true 会扫成字符串;未初始化的 struct 指针传给 First() 会直接 panic。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











