OOro

查询构建器

Use、Table、Raw、Select、Join、Group、分页、锁、流式读取和终结方法参考。

Oro 的查询入口只有三个:

db.Use[Product]()
db.Table("products")
db.Raw("select * from products where id = ?", id)

模型查询使用 Go 字段名;裸表和 Raw 使用数据库列名。

文档分工:

  • 条件写法和多层括号:看 查询条件
  • 字段表达式完整方法:看 条件表达式
  • Join、Select、子查询、终结方法:留在当前查询构建器参考页。

模型查询

product, err := db.Use[Product]().Where("Code", "P001").First(ctx)

常用链式方法:

方法 用途
Where / OrWhere 字段条件或条件对象
WhereGroup / OrWhereGroup 括号分组
WhereWhen 条件成立时追加分组
WhereRaw 原生条件片段
WhereColumn 列与列比较
WhereIn / WhereExists 子查询条件
Select 选择字段、表达式、聚合
With 预加载关系
For 通过关系查询目标侧
As 设置主表别名
Join / LeftJoin / RightJoin / FullJoin Join 查询
CrossJoin / JoinRaw 特殊 Join
OrderBy / OrderByDesc / OrderByRaw 排序
GroupBy / Having / HavingRaw 分组和聚合过滤
HavingColumn / HavingIn / HavingExists 分组后的列比较和子查询过滤
Limit / Offset 限制和偏移
LockForUpdate / LockForShare 行锁
WithDeleted / OnlyDeleted 软删除查询
Shard / AllShards 分片查询
UsePrimary 强制读主库
Cache / CacheKey / CacheTags 查询缓存
Timeout 查询级超时
SkipHooks / SkipEvents 跳过模型 Hook 或全局事件

裸表查询

rows, err := db.Table("products").Where("price", ">=", 100).Get(ctx)

Table 返回 oro.Map。需要结构体时使用 MapTo[T]()

views, err := db.Table("products").
    Select("id", "code", "price").
    MapTo[ProductView]().
    Get(ctx)

Table(...).MapTo[T]() 支持 FirstGetStreamChunkEachPaginateCreateCreateManyResultUpsertMany。聚合统计仍然调用底层 Table 查询的 CountExistsSumAvgMinMax

Raw(...).MapTo[T]() 支持 FirstGetStream;Raw 的执行型 SQL 使用 Raw(...).Exec(ctx)

Raw 查询

rows, err := db.Raw("select * from "+db.TableName("products")+" where code = ?", code).Get(ctx)

Raw 支持:

方法 用途
First(ctx) 返回一行 oro.Mapnil
Get(ctx) 返回 []oro.Map
MapTo[T]() 映射到结构体
Stream(ctx) 流式读取
Exec(ctx) 执行非查询 SQL
Cache / CacheKey / CacheTags 缓存 Raw 读结果
Timeout 设置超时

Raw SQL 不会自动处理表前缀。需要表前缀时使用 db.TableName("products")

Select

Select 可以混合字段名、别名表达式、聚合表达式、Raw 表达式和关系聚合表达式。

rows, err := db.Table("orders").
    Select(
        "status",
        oro.Count("*").As("total"),
        oro.Sum("amount").As("amount_sum"),
        oro.Raw("max(created_at) as last_created_at"),
    ).
    GroupBy("status").
    Get(ctx)

模型查询里字段名使用 Go 字段名:

products, err := db.Use[Product]().Select("ID", "Code", "Price").Get(ctx)

聚合表达式支持 CountSumAvgMinMax,都可以通过 .As(alias) 设置别名。字段别名用 oro.As(field, alias)

聚合表达式也遵循入口字段规则:db.Use[Order]().Select(oro.Sum("Amount")) 使用 Go 字段名;db.Table("orders").Select(oro.Sum("amount")) 使用数据库列名。oro.Raw(...)"o.amount" 这类已限定字段按 SQL 处理。

Group 和 Having

rows, err := db.Table("orders").
    Select("status", oro.Count("*").As("total")).
    GroupBy("status").
    HavingRaw("count(*) > ?", 10).
    Get(ctx)

可用方法:

方法 用途
GroupBy(fields...) 分组字段
Having(field, value) field = value
Having(field, op, value) 分组后的字段条件
HavingColumn(left, args...) 分组后的列比较
HavingIn(field, oro.Query(query)) HAVING field IN (subquery)
HavingExists(oro.Query(query)) HAVING EXISTS (subquery)
HavingRaw(sql, args...) 原生 having 条件

Having 不接收回调。结构化 Having 的字段名仍遵循入口规则;如果要按聚合别名或聚合表达式过滤,使用 HavingRaw,或把聚合放到子查询后在外层 Where 过滤。

Join

Join 使用回调统一表达 ON 条件。查询过滤条件仍然写在外层 Where

rows, err := db.Table("orders").As("o").
    LeftJoin("users", func(j *oro.Join) {
        j.As("u").OnColumn("u.id", "o.user_id")
    }).
    Where("o.status", "paid").
    Get(ctx)

Join 条件方法:

方法 用途
As(alias) 设置 join 表别名
OnColumn(left, right) 列等值比较
OnColumn(left, op, right) 列操作符比较
OrOnColumn(left, args...) OR 列比较
Where(field, args...) Join 条件值比较
OrWhere(field, args...) OR Join 条件值比较
WhereGroup(fn) Join 条件分组,追加为 AND (...)

支持的 Join 类型:

方法 SQL 语义
Join(source, fn) inner join
LeftJoin(source, fn) left join
RightJoin(source, fn) right join
FullJoin(source, fn) full join,取决于驱动能力
CrossJoin(table) cross join
JoinRaw(sql, args...) 原生 join 片段

source 可以是表名,也可以是 oro.Query(...).As(alias) 子查询。

latestOrders := db.Table("orders").
    Select("user_id", oro.Raw("max(id) as last_order_id")).
    GroupBy("user_id")

rows, err := db.Table("users").As("u").
    LeftJoin(oro.Query(latestOrders).As("lo"), func(j *oro.Join) {
        j.OnColumn("lo.user_id", "u.id")
    }).
    Get(ctx)

Join 回调里可以写分组:

rows, err := db.Table("orders").As("o").
    Join("users", func(j *oro.Join) {
        j.As("u").
            OnColumn("u.id", "o.user_id").
            WhereGroup(func(g *oro.Join) {
                g.Where("u.status", "active").
                    OrWhere("u.vip", true)
            })
    }).
    Get(ctx)

当前 Join 只提供 WhereGroup,没有单独的 OrWhereGroup。需要 OR 条件时,在 group 内用 OrWhere 表达。

字段别名使用 oro.As(field, alias),主表别名使用查询的 As(alias),Join 表别名使用 j.As(alias)

rows, err := db.Table("orders").As("o").
    Select(oro.As("o.status", "status"), oro.Count("*").As("total")).
    Join("users", func(j *oro.Join) {
        j.As("u").OnColumn("u.id", "o.user_id")
    }).
    GroupBy("o.status").
    Get(ctx)

子查询

oro.Query(query)Use / Table 查询作为子查询源。

paidOrders := db.Table("orders").Select("user_id").Where("status", "paid")

users, err := db.Table("users").
    WhereIn("id", oro.Query(paidOrders)).
    Get(ctx)

作为 FromJoin 源时可以设置别名:

rows, err := db.From(oro.Query(paidOrders).As("po")).Get(ctx)

终结方法

方法 返回
First(ctx) 一条或 nil
Find(ctx, id) 主键查询一条或 nil,模型查询可用
Get(ctx) 切片
Count(ctx) int64
Exists(ctx) bool
Sum(ctx, field) / Avg(ctx, field) oro.Decimal
Min[T](ctx, field) / Max[T](ctx, field) oro.Null[T]
Stream(ctx) 流式迭代器
Chunk(ctx, size, fn) 分块读取
Each(ctx, fn) 逐行读取
Paginate(size) 分页器
Create / CreateMany / CreateManyResult 写入
Upsert / UpsertMany 冲突写入
Update / Delete 更新或删除

查询返回规则

  • 单条查不到返回 nil, nil
  • 多条查不到返回空切片;
  • Exists 查不到返回 false, nil
  • 只有连接、SQL、扫描、约束、事务等问题才返回 error。
编辑此页