gorm where字符串写法须用?占位符防sql注入;结构体忽略零值而map不忽略;or/not需嵌套where避免括号错误;全局条件应优先用scope而非硬编码或callback。

Where 字符串写法最常用,但要注意参数绑定和 SQL 注入防护
GORM 的 Where 接收字符串时,必须用 ? 占位符,GORM 会自动做参数化处理——这是防 SQL 注入的关键,别手拼字符串。比如 db.Where("name = '" + input + "'") 是危险操作,绝对要避免。
- 多个条件直接连写:
db.Where("age > ? AND status = ?", 18, 1) - IN 查询要传切片,不是展开的多个参数:
db.Where("id IN ?", []uint{1,2,3})(不是?, ?, ?) - LIKE 模糊匹配记得自己加通配符:
db.Where("name LIKE ?", "%"+keyword+"%") - 时间范围用
BETWEEN更清晰:db.Where("created_at BETWEEN ? AND ?", start, end)
Struct 和 Map 传参行为完全不同,零值处理是最大坑点
用结构体传条件(db.Where(&User{Name: "Tom", Age: 0}))时,Age: 0、Status: false、Name: "" 全部被忽略——GORM 只认「非零值」。而 Map 不吃这套:db.Where(map[string]interface{}{"age": 0, "status": false}) 会如实生成 age = 0 AND status = false。
- 想查
deleted_at IS NULL?Struct 写不出,得用字符串:db.Where("deleted_at IS NULL") - Map 是稳妥选择,尤其涉及状态码、开关字段(如
is_active: 0)时 - 主键 IN 查询可直接传切片:
db.Where([]uint{1,2,3}),等价于id IN (1,2,3),简洁但仅限主键字段
Or / Not / And 组合条件容易括号错乱,推荐用嵌套 Where 显式分组
GORM 的链式 Or 默认平级拼接,不自动加括号。比如 db.Where("a = ?", 1).Or("b = ?", 2).Where("c = ?", 3) 生成的是 WHERE a = 1 OR b = 2 AND c = 3(AND 优先级高),不是你想要的 (a = 1 OR b = 2) AND c = 3。
- 正确做法是把 OR 部分包进一个子
Where:db.Where(db.Where("a = ?", 1).Or("b = ?", 2)).Where("c = ?", 3) -
Not同理,db.Not("status = ?", 0)是单条件否定;db.Not(map[string]interface{}{"status": 0, "type": "tmp"})会生成NOT (status = 0 AND type = 'tmp') - 复杂逻辑建议拆成变量,提升可读性:
base := db.Where("tenant_id = ?", tid); if admin { base = base.Or("is_global = ?", true) }
全局默认条件不能靠 Where 硬塞,要用 Scope 或 Callback 控制作用域
想让所有查询自动带 status = 1?千万别在每个 db.Where(...) 前手动加——漏一处就可能查出脏数据。GORM 提供两种正统方式:
-
Scope最轻量,适合业务层复用:func Active(db *gorm.DB) *gorm.DB { return db.Where("status = ?", 1) },然后db.Scopes(Active).Find(&users) - Callback 更底层,适合框架级约束(如租户隔离),但要注意它会影响所有查询,包括
Count、Update等,且无法被Unscoped之外的方式关闭 - 慎用
Unscoped:它绕过所有全局条件,但也会跳过软删除逻辑,该查不到的数据反而查到了
真正难的不是写条件,而是判断「这个条件该不该对所有调用者生效」——Scope 可控,Callback 隐蔽,选错一种,后期 debug 能绕晕人。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











