一对多查询必须用RelatedSel(),否则外键字段为空;RelatedSel()生成LEFT JOIN而非N+1,仅对已注册模型有效且字段名需首字母大写;跨表过滤用双下划线如"Post__Title__contains";反向查询需reverse(many)标签;高并发宜分步查+手动组装。

一对多查询必须用 RelatedSel(),否则外键字段为空
Beego ORM 不会自动加载关联数据,哪怕结构体里定义了 Post []*Post `orm:"rel(fk)"` 这样的字段,All() 或 One() 默认只查主表。不加 RelatedSel(),user.Post 就是 nil 或空切片,且无任何错误提示。
常见错误现象:o.QueryTable("user").Filter("id", 1).All(&u) 后打印 len(u.Post) 是 0,但数据库里明明有 5 条关联文章。
-
RelatedSel()本质是生成 LEFT JOIN SQL,不是 N+1 查询 - 它只对已注册的模型生效(
RegisterModel(new(User), new(Post))) - 字段名必须首字母大写,否则反射失败 ——
Post []*Post可以,post []*Post不行
Filter() 跨关联表条件要写双下划线 __,不是点号
想查 “作者 ID 为 1 且文章标题包含 ‘Go’ 的用户”,不能写 Filter("Post.Title__contains", "Go") —— 点号会被忽略,字段名直接变成 Post.Title 导致 SQL 报错或查不到。
正确写法是用双下划线连接关系链:Filter("Post__Title__contains", "Go"),对应 SQL 中的 T1.title LIKE '%Go%'(T1 是 JOIN 出来的 posts 表别名)。
- 层级支持多级:如
User__Profile__Age__gt(User → Profile 是 one-to-one,Profile → Age 是字段) - 大小写敏感:结构体字段名是
Post,就不能写成post__title - 只对已注册模型中定义的
rel(fk)或reverse(many)字段有效
一对多反向查询(从子表查父表)用 reverse(many) + Filter()
比如从 Post 查所属 User,结构体里得这么定义:
type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"`
}
type User struct {
Id int
Name string
Post []*Post `orm:"reverse(many)"`
}
然后才能用 o.QueryTable("post").Filter("User__Name", "Alice").All(&posts)。漏掉 reverse(many),User__Name 就无法解析。
-
reverse(many)不创建外键,纯 ORM 映射用;真正外键得靠数据库建表时加 - 如果只查子表、不涉及父表字段,根本不需要
reverse标签 -
reverse(one)用于一对一反查(如从Profile查User),用法一致
高并发下 RelatedSel() 容易触发连接池耗尽
RelatedSel() 会强制走 JOIN,当主表结果集很大(比如查 1000 个用户)且每个用户关联几十篇文章时,单条 SQL 返回的数据量可能暴涨,同时占用更多连接和内存。
生产环境更稳妥的做法是分两步:
- 先查主表 ID 列表:
o.QueryTable("user").Filter("status", 1).ValuesList("id", &ids) - 再用 IN 批量查子表:
o.QueryTable("post").Filter("user_id__in", ids).OrderBy("user_id").All(&posts) - 最后在 Go 层手动组装映射(用 map[int][]*Post)
这比 RelatedSel() 多一次 DB 往返,但可控、可缓存、不卡死连接池 —— 尤其当子表数据量远大于主表时,JOIN 反而更慢。











