GORM链式Where空值不跳过需手动判空;map条件防SQL注入须白名单校验;复杂逻辑用函数式Where+Or;关联动态过滤必须Joins而非Preload。

用 Where 链式调用拼接多个条件时,空值字段会意外生效
很多人直接写 db.Where("status = ?", status).Where("user_id = ?", userID),但当 status 是零值(比如 0 或空字符串)时,条件仍会被加入 SQL,导致查不到预期数据。GORM 不会自动跳过零值参数。
正确做法是按需构建查询对象:
- 声明一个
*gorm.DB变量,初始为db - 每个条件用
if判断是否有效,再链式调用Where、Joins等 - 避免在
Where中硬编码字段名,优先用结构体或 map 映射字段与值
示例:
var query = db
if status != 0 {
query = query.Where("status = ?", status)
}
if len(name) > 0 {
query = query.Where("name LIKE ?", "%"+name+"%")
}
if userID > 0 {
query = query.Where("user_id = ?", userID)
}
var users []User
query.Find(&users)
用 map[string]interface{} 批量加条件时,注意 key 的 SQL 注入风险
虽然 db.Where(map[string]interface{}{"status": 1, "deleted_at": nil}) 写起来快,但 map 的 key 是字段名,如果来自用户输入(比如前端传的 filter[field]),直接拼进 SQL 就是注入漏洞。
安全做法:
立即学习“go语言免费学习笔记(深入)”;
- 只允许白名单字段参与 map 构建,比如预定义
validFields = map[string]bool{"status": true, "category": true} - 对 value 做类型检查,
nil值会被 GORM 转成IS NULL,但"null"字符串不会 - 不要用 map 表达
IN、BETWEEN这类复杂操作,它们不支持
需要 OR、NOT、嵌套括号逻辑时,必须用 Where 的结构体或函数形式
GORM 对 OR 的支持很弱,Or() 只能追加一级 OR 条件,没法控制分组。真正要写 (a = 1 AND b = 2) OR (c = 3 AND d = 4),得靠函数式写法:
db.Where(func(db *gorm.DB) *gorm.DB {
return db.Where("a = ? AND b = ?", 1, 2)
}).Or(func(db *gorm.DB) *gorm.DB {
return db.Where("c = ? AND d = ?", 3, 4)
})
要点:
- 每个匿名函数返回一个
*gorm.DB,代表一组括号内的条件 -
Or和Where在同一层级才有效;嵌套函数里不能用Or拆分子组 - 用
Not包裹函数可实现NOT (x AND y),但别嵌太深,可读性会急剧下降
关联查询 + 动态条件容易漏掉 Joins 或误用 Preload
想查“订单+用户信息”,且用户姓名模糊匹配,但只在需要时才关联——这时候如果只写 Preload("User"),GORM 不会在 JOIN 条件里带上 users.name LIKE ?,因为 Preload 是单独发 SQL 的。
必须用 Joins 并手动写 ON 和 WHERE:
- 先
Joins("JOIN users ON orders.user_id = users.id") - 再用
Where("users.name LIKE ?", "%"+name+"%"),这个条件才会作用于 JOIN 后的结果 - 如果同时要查订单和用户字段,记得用
Select明确字段,否则可能因列名重复报错 -
Preload只适合“无条件加载关联数据”,动态过滤一律走Joins+Where
最麻烦的是多级嵌套关联(比如订单→用户→部门),这时 SQL 手写成本高,建议拆成两步:先查主表 ID 列表,再用 IN 查关联数据。


















