Gin+GORM整合需显式处理字段校验、时间填充、ID解析、软删除和缓存控制,否则易出现静默失败;Create不报错但未写入因未检查RowsAffected,时间字段需用gorm tag禁JSON覆盖,Delete默认软删且需禁缓存。

Create 不报错但没写入?因为 GORM 默认不校验字段合法性,也不强制抛错
ShouldBindJSON 绑定后字段为空或时间错乱?因为没配 validator,也没控制 JSON 解析对时间/ID 字段的覆盖
Delete 后刷新页面还看到数据?因为软删除生效了,但前端没处理 DeletedAt,响应头也没禁缓存
整合不是连上数据库就完事——Gin + GORM 的坑全在默认行为里。每个“看起来正常”的操作,背后都藏着静默失败、字段覆盖、缓存污染的风险。
为什么 Create 返回 err == nil 却没写入
常见现象:前端传 {"name": "a", "age": 200},结构体定义 Age uint8,Create 不报错,但数据库没新增记录,RowsAffected == 0。
原因在于 GORM 的 Create 方法只检查底层 SQL 执行是否出错,不校验 Go 结构体字段值是否合法(比如溢出、重复主键、违反唯一索引等)。
- 必须显式检查
result.Error和result.RowsAffected,不能只判err != nil - 对关键字段(如邮箱、手机号、ID)做业务校验,建议用
go-playground/validator配合Validate.Struct - 主键冲突时,GORM 可能静默跳过;若结构体含
ID字段且非零,Create会转为UPDATE,导致语义错乱
如何让 CreatedAt/UpdatedAt 自动填充又不被 JSON 覆盖
错误做法:只靠数据库设置 DEFAULT CURRENT_TIMESTAMP —— GORM 不读取也不信任它。
正确做法全靠结构体 tag 控制:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 字段类型必须是
*time.Time或嵌入gorm.Model(含CreatedAt/UpdatedAt) - 禁用 JSON 解析写入:
CreatedAt time.Time `json:"-" gorm:"autoCreateTime"` - 若用
sql.NullTime,Scan后必须判断.Valid,否则前端收到null无法区分“未设置”和“空时间” - 避免在结构体里同时定义
json:"created_at"和gorm:"column:created_at",容易引发覆盖冲突
c.Param("id") 怎么安全解析成整型
直接 uint(c.Param("id")) 会 panic;用 strconv.Atoi 不够,因为 uint 范围更窄,且没做语义校验。
- 统一用
strconv.ParseUint(c.Param("id"), 10, 64),捕获strconv.ErrSyntax并返回400 Bad Request - 查库前必须判断解析结果是否为
0:if id == 0 { c.AbortWithStatusJSON(400, gin.H{"error": "invalid id"}) },否则First(&u, id)会跳过 WHERE 条件,变成全表扫描 - 查不到时,要明确判断
result.Error == gorm.ErrRecordNotFound,对应返回404,别返回空对象加200
Delete 后数据还在?两个隐藏开关没关
现象:调用 db.Delete(&user),响应 200,但刷新页面仍显示该用户。
- 第一开关:GORM 默认启用软删除,
Delete实际执行的是UPDATE SET deleted_at = NOW()。要物理删除,得写db.Unscoped().Delete(&user) - 第二开关:浏览器缓存了上一个
200 OK响应。所有写操作(POST/PUT/DELETE)返回前必须加c.Header("Cache-Control", "no-cache") - 前端也要配合:读列表时,过滤掉
DeletedAt IS NOT NULL的记录,否则用户永远看不到“已删除”状态
gin.Default() 和 gorm.Open() 连起来,而是把每个“它应该这样”的假设,都换成一行显式的判断或配置。时间字段、ID 解析、软删除、缓存头——这些点一旦漏掉一个,API 就会在生产环境里安静地出错。

















