不能直接拼接sql字符串,因易导致sql注入;应使用gorm的参数化查询方法(如where、or)或db.raw配?占位符;多条件需动态追加,空值用指针判断跳过;复杂逻辑用scopes封装and/or组合。

为什么不能直接拼接 SQL 字符串
因为 fmt.Sprintf 或字符串拼接生成的 SQL 容易触发 SQL 注入,尤其当条件来自 c.Query 或 c.PostForm 时。比如用户传入 name=xxx%27%20OR%201=1--,拼成 "WHERE name = '" + name + "'" 就直接崩了。
GORM 提供的 Where、Or、Not 等方法默认使用参数化查询,底层走的是 ? / $1 占位符,安全且可缓存执行计划。
- 永远避免用
fmt.Sprintf("SELECT * FROM user WHERE name = '%s'", name) - 不要手动加引号或转义——GORM 会处理
- 若必须用原生 SQL,只用
DB.Raw("...").Scan(&v)并严格配?占位符
多条件组合时怎么写 Where 链式调用
核心是「条件存在才追加」,不是「全写死再过滤」。GORM 的 Where 和 Or 支持链式构建,且空条件会被自动跳过。
例如:按用户名模糊查、年龄范围筛选、状态是否启用,三者都可选:
var users []models.User
query := DB.Model(&models.User{})
if name := c.Query("name"); name != "" {
query = query.Where("username LIKE ?", "%"+name+"%")
}
if minAge := c.Query("min_age"); minAge != "" {
if age, err := strconv.Atoi(minAge); err == nil {
query = query.Where("age >= ?", age)
}
}
if status := c.Query("status"); status != "" {
query = query.Where("status = ?", status)
}
err := query.Find(&users).Error
- 每个
query = query.Where(...)返回新 Query 对象,不污染原始DB - 注意
LIKE中的%必须在 Go 层拼,不能写进 SQL 模板(否则无法参数化) - 数值类参数要先类型转换,避免传字符串进
WHERE age > '25'导致隐式转换或报错
空值和零值字段怎么跳过条件
GORM 默认把 0、""、false 当作有效值参与查询,这会导致意外结果。比如 age=0 的用户被查出来,但业务上可能想忽略它。
解决方式不是靠字段默认值,而是靠显式判断:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 对整型:检查是否为 0 且非业务合法值(如年龄 0 不合理)
- 对字符串:用
strings.TrimSpace()判空,避免只传空格 - 对布尔:用指针类型
*bool,nil表示未传,&true才生效
结构体接收参数时建议这样定义:
type UserFilter struct {
Username *string `form:"username"`
Age *int `form:"age"`
Status *bool `form:"status"`
}
然后在 handler 里:
var filter UserFilter
if err := c.ShouldBindQuery(&filter); err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
return
}
query := DB.Model(&models.User{})
if filter.Username != nil && *filter.Username != "" {
query = query.Where("username LIKE ?", "%"+*filter.Username+"%")
}
if filter.Age != nil {
query = query.Where("age = ?", *filter.Age)
}
复杂 OR/AND 混合逻辑怎么组织
当出现「(A AND B) OR (C AND D)」这类结构,单纯链式 Where 会失效——它默认全是 AND 连接。
必须用 Scopes 或匿名函数封装子条件:
func ageAndStatusScope(minAge int, status string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("age >= ? AND status = ?", minAge, status)
}
}
// 使用
query := DB.Scopes(
ageAndStatusScope(18, "active"),
ageAndStatusScope(60, "retired"),
).Find(&users)
- 不要试图用
Or去强行拼,容易漏括号或优先级错乱 - 多个
Scopes是 OR 关系;单个Scope内部用Where是 AND - 如果子条件还要动态,就在 Scope 函数里加判断逻辑
真正难的不是语法,是理清业务逻辑是否真需要 OR —— 很多场景其实能拆成多次查询合并结果,更可控。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










