QuerySeter 是 Beego ORM 高级查询的唯一入口,所有复杂条件、关联加载、字段控制均依赖它;它返回链式可构造的查询对象,不执行 SQL,直到调用 All、One、Count 等终结方法才真正发请求。

QuerySeter 是 Beego ORM 高级查询的唯一入口,所有复杂条件、关联加载、字段控制都依赖它。直接用 o.QueryTable("user") 返回的就是一个 QuerySeter 实例,后续链式调用都是在构造查询逻辑,**不执行 SQL**,直到遇到 All、One、Count 等终结方法才真正发请求。
Filter 和 Exclude 的字段路径写法必须带双下划线
外键或反向关联字段不能用点号,比如 profile.age 会报错;必须写成 profile__age。大小写不敏感,Profile__Age 也合法,但建议统一小写加双下划线。
-
qs.Filter("status__in", "active", "pending")→ 生成WHERE status IN (?, ?) -
qs.Exclude("created_at__lt", "2025-01-01")→ 排除早于该时间的记录 - 嵌套两层?支持:
qs.Filter("order__user__city", "shenzhen"),前提是模型里Order有User字段且已声明rel(fk) - 注意:字段名是模型定义里的 Go 字段名(驼峰),不是数据库列名(蛇形),ORM 会自动转换
RelatedSel 预加载必须显式指定关联字段名
RelatedSel 不是“自动加载所有外键”,它只加载你传进去的字段名对应的关系。漏写就还是 N+1 —— 这是最容易被忽略的性能坑。
- 正确:
qs := o.QueryTable("order").RelatedSel("user").RelatedSel("items"),这样查 100 个订单时,只会额外发 2 条 JOIN 查询,而不是 200 次单条查询 - 错误:
qs := o.QueryTable("order").RelatedSel(),这等价于没写,不触发任何预加载 - 如果关联字段是 slice(一对多),
RelatedSel("items")有效;如果是单指针(一对一),同样适用,比如RelatedSel("profile") - 不能混用
Values和RelatedSel:两者互斥,Values只返回扁平字段,不支持结构体嵌套
All / One 里指定字段列表能显著减少内存和网络开销
默认 All(&users) 会 SELECT 所有字段,哪怕你只用 Id 和 Title。尤其当表里有 TEXT 或 BLOB 字段时,浪费更明显。
- 只取必要字段:
qs.Filter("status", "published").All(&articles, "id", "title", "slug") - 字段名必须是模型里的 Go 字段名(如
Title),不是数据库列名(如title) - 不能用别名或表达式,只支持纯字段名;也不支持嵌套字段(如
user__name),那是RelatedSel的事 - 配合
Limit使用时,顺序无关:qs.Limit(10).All(...)和qs.All(...).Limit(10)效果一样,但推荐前者,语义更清晰
Raw 查询和 Values 配合使用才是原生 SQL 的实用姿势
别为了“用原生 SQL”而放弃 ORM 的类型安全。真要写 SQL,优先走 Raw + Values,而不是 Raw + QueryRows 手动解析。
- 查列表(map 切片):
o.Raw("SELECT id, name FROM user WHERE city = ?", "beijing").Values(&list),list类型是[]orm.Params,每个元素是map[string]string - 查单行:
o.Raw("SELECT COUNT(*) as cnt FROM article").Values(&row),然后取row[0]["cnt"] - 避免
QueryRows(&structs):它要求 struct 字段名和 SQL 列名严格一致,且无法处理 NULL 值(会 panic),Values更健壮 - SQL 中的 ? 占位符按顺序绑定,不支持命名参数(如
:id)
RelatedSel 当成银弹、不写字段名就以为能自动加载,或者在 All 里漏掉字段白拿一堆无用数据。这些都不是框架 bug,而是对 QuerySeter 的构造时机和终结行为理解偏差。


















