安全写WHERE字符串条件需用?占位符防注入,IN子句用(?),LIKE通配符放参数中,结构体零值被忽略需用map显式传0,Or/Not需手动加括号,动态条件应派生新查询对象。

Where字符串条件怎么写才安全
直接拼 SQL 字符串最容易出错,但也是最灵活的方式。关键不是“能不能用”,而是“怎么避免注入和括号陷阱”。
-
WHERE后面的字符串必须用?占位符,不能直接插变量,否则有 SQL 注入风险:db.Where("name = ?", name)✅,db.Where("name = '" + name + "'")❌ - 多个条件用
AND连接时,参数顺序必须严格匹配占位符位置:db.Where("age > ? AND status = ?", 18, "active") -
IN子句要加括号且用单个?:db.Where("id IN (?)", []uint{1, 2, 3}),不能写成"id IN (?, ?, ?)" -
LIKE的通配符(%)要写在参数里,不是 SQL 字符串里:db.Where("name LIKE ?", "%"+keyword+"%")
Struct 和 Map 条件的零值陷阱
用结构体或 map 构建条件时,GORM 默认忽略零值字段——这既是便利,也是隐患。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 结构体查询:
db.Where(&User{Name: "alice", Age: 0}).Find(&users)实际只查name = 'alice',Age: 0被跳过 - 想查
age = 0,必须改用 map:db.Where(map[string]interface{}{"name": "alice", "age": 0}).Find(&users) - map 中所有键值对都会参与生成 WHERE 子句,包括
nil值(会转成IS NULL),注意判空逻辑 - 切片主键查询可直接传数组:
db.Where([]int64{1, 5, 9}).Find(&users)→WHERE id IN (1, 5, 9)
Or / Not / And 链式组合的括号问题
GORM 的链式 Or 默认平级拼接,不自动加括号,AND 优先级高于 OR,极易写出非预期 SQL。
- 错误写法:
db.Where("a = ?", 1).Or("b = ?", 2).Where("c = ?", 3)→WHERE a = 1 OR b = 2 AND c = 3(等价于a=1 OR (b=2 AND c=3)) - 正确写法:把 OR 部分包进子查询:
db.Where(db.Where("a = ?", 1).Or("b = ?", 2)).Where("c = ?", 3)→WHERE (a = 1 OR b = 2) AND c = 3 -
Not同理:db.Not(map[string]interface{}{"status": 0, "type": "tmp"})生成NOT (status = 0 AND type = 'tmp'),不是status != 0 AND type != 'tmp' - 复杂逻辑建议提前拆成变量,比如:
base := db.Where("tenant_id = ?", tid); if admin { base = base.Or("is_global = ?", true) }
动态条件构建时的 db 变量复用风险
在 HTTP handler 或循环中动态拼条件时,直接复用全局 db 会导致后续请求污染前序查询状态。
- 错误示范:
if name != "" { db = db.Where("name = ?", name) }—— 修改了全局db实例 - 正确做法:每次从全局
db派生新查询对象:tx := db; if name != "" { tx = tx.Where("name = ?", name) } - 注意
tx.Find(&users)才真正执行,tx是临时 session,不影响db本身 - 调试时可加
.Debug()查看实际生成 SQL,尤其验证括号和参数绑定是否符合预期
Or 或结构体传参,就得手动检查生成的 SQL。

















