GORM 与 Gin 配合需确保模型结构、DB 实例、参数安全三者一致;DSN 必含 parseTime=True 和 loc=Local;模型字段须用 gorm tag 显式映射;Where 查询必须传指针;Save 是全量更新,Update 才是局部更新;错误必须显式检查。

直接用 GORM 做增删改查没问题,但 Gin 本身不处理数据库——它只管 HTTP 请求分发。真正关键的是:模型定义是否匹配表结构、DB 实例是否全局可访问、查询时传参方式是否安全。错一个环节,Find 返回空、Save 不生效、Delete 删错行,都是高频问题。
gorm.Open 连接 MySQL 时必须注意 dsn 参数格式
dsn 字符串里 parseTime=True 和 loc=Local 缺一不可,否则 time.Time 字段会解析失败或时区错乱;charset=utf8mb4 比 utf8 更稳妥,避免 emoji 存储异常。
常见错误现象:invalid time format、ERROR 1292 (22007): Incorrect datetime value
-
root:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local✅ -
root:password@tcp(127.0.0.1:3306)/dbname?charset=utf8❌(无 parseTime,time 字段为零值) -
root:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True❌(HTML 实体 & 在 Go 字符串里不会被识别)
模型 struct tag 中的 column 和 primary_key 必须显式声明
GORM 默认按字段名映射列名(如 Name → name),但一旦表字段是下划线命名(如 create_time),就必须用 gorm:"column:create_time" 显式绑定,否则查询时字段为空。
容易踩的坑:auto_increment 必须配合 primary_key 使用,否则 Create 后 ID 不会回填;gorm:"default:CURRENT_TIMESTAMP" 对 MySQL 无效,得靠建表语句或 DEFAULT 约束。
示例:
type TbTest struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement"`
Name string `gorm:"column:name;type:varchar(255)"`
CreateAt time.Time `gorm:"column:create_time;not null"`
UpdateAt time.Time `gorm:"column:update_time;not null"`
}
Where 查询必须传指针或结构体,不能只传值
db.Where(&user).Find(&results) 是按主键查,db.Where("id = ?", 5).Find(&results) 是条件查——二者语义完全不同。如果误写成 db.Where(user).Find(&results)(没取地址),GORM 会把整个 struct 当作 map 解析,导致 SQL 错误或漏条件。
典型错误:
-
user := User{ID: 5}; db.Where(user).Find(&u)→ 生成WHERE "user"."id" = 0(因为没地址,字段未导出或零值) -
db.Where("id = ?", id).First(&u)✅(推荐,显式、可控) -
db.First(&u, id)✅(简写,等价于WHERE id = ?)
Save 和 Update 的行为差异极易混淆
Save 是“全量更新”:它会把 struct 所有非零字段都写入数据库,哪怕你只想改一个字段;Update 或 Updates 才是“局部更新”,只动指定字段。
比如:
u := User{ID: 5}
db.First(&u) // 查出完整记录
u.Name = "new name"
u.Save(&u) // ❌ 把 Age、Email 等所有零值字段也设为 NULL(如果 struct 定义里它们是零值类型)
正确做法:
-
db.Model(&u).Update("name", "new name")✅(只更新 name) -
db.Model(&u).Updates(map[string]interface{}{"name": "new", "age": 25})✅ -
db.Select("name").Model(&u).Updates(User{Name: "new"})✅(Select 控制字段白名单)
另外,Delete 传 struct 时若没设主键值,会删整张表——GORM v2 默认开启软删除,真删要加 Unscoped()。
最常被忽略的是:GORM 的 Find、Create、Update 等方法返回 *gorm.DB,错误必须显式检查 err := db.Create(&u).Error,而不是只看变量是否为空。静默失败比报错更难排查。


















