Query<T>
查询构建器。通过链式 API 构造 SQL 查询,最终以 all() / one() / count() / exists() 执行。
创建 Query
// 手动创建
let q = Query<User>()
// 通过宏生成(推荐)
let q = User.query()链式方法
所有方法返回 this,支持链式调用。
select()
q.select([Expr.Column("id"), Expr.Column("name")])
select()接受Array<Expr>。Col<T>不是Expr,所以需用Expr.Column("name")构造。宏生成的query()已自动设置 select,通常无需手动调用。
from()
q.from("users")默认由宏生成的 query() 自动设置。
filter()
支持单个条件或条件数组。
// 单个
q.filter(User.col().age >= 18)
// 多个(AND 连接)
q.filter([
User.col().age >= 18,
User.col().status == "active"
])order()
q.order([User.col().id.desc()])
q.order([User.col().status.asc(), User.col().id.desc()])limit() / offset()
q.limit(10).offset(20)groupBy() / having()
groupBy() / having() 接受 Array<Expr>,需用 Expr.Column() 构造:
q.groupBy([Expr.Column("dept")])
q.having([Expr.Binary(Expr.Column("count"), BinOp.Gt, Expr.Value(5))])include()
// 预加载关联
q.include(UserRel.posts)
q.include(UserRel.profile)
// 指定关联返回字段
q.include(PostRel.author, [Col<Any>("name")])
// 嵌套 include:withInclude 链式声明两级预加载
q.include(PostRel.author.withInclude(UserRel.profile))
// 字符串点号路径,与 withInclude 链等价
q.includeAll(["author.profile", "tags"])include() 采用分步批量查询(batch include):主查询后按目标表执行 WHERE 主键/fk IN (...) 批量子查询回填,无 JOIN、无笛卡尔积。嵌套关联(withInclude / includeAll)递归批量装配。限制:目标实体须有单列 id 主键;复合主键主实体的集合 include 不支持;includeAll 路径不支持字段子集。
using()
设置执行上下文。支持 Refine / Session / Tx。
q.using(rf)
q.using(rf.session())
q.using(tx) // 在事务中使用注意:
q.using(DB)已在 0.6.0 移除——DB是纯连接层,不含方言与参数偏移。需 ORM 查询请使用Refine。
终止方法
all()
let users: Array<User> = User.query().using(rf).all()需已设置 mapper。宏生成的 query() 已自动设置。
one()
let user: Option<User> = User.query().using(rf)
.filter(User.col().id == 1)
.one()count()
忽略 LIMIT/OFFSET,使用 COUNT(*):
let count: Int64 = User.query().using(rf)
.filter(User.col().age >= 18)
.count()exists()
等价于 count() > 0:
let exists: Bool = User.query().using(rf)
.filter(User.col().email == "test@example.com")
.exists()sum() / avg() / min() / max()
聚合查询,忽略 LIMIT/OFFSET,保留 filter/groupBy/having 条件:
let total: Int64 = User.query().using(rf).sum(User.col().age)
let totalF: Float64 = User.query().using(rf).sum(User.col().score) // Col<Float64>
let avg: Float64 = User.query().using(rf).avg(User.col().age)
let min: Int64 = User.query().using(rf).min(User.col().age)
let max: Int64 = User.query().using(rf).max(User.col().age)sum对Col<Int64>返回Int64、对Col<Float64>返回Float64;avg恒返回Float64- 空结果集时
sum为 0,min/max为 0(SQL NULL 归一化)
page()
一步分页,自动执行 COUNT + LIMIT/OFFSET,返回 Page<T>:
let pg: Page<User> = User.query().using(rf)
.order([User.col().id.desc()])
.page(2, 20) // 第 2 页,每页 20 条Page<T> 字段与方法:items(当前页)、total(总数)、page(页码)、size(每页条数)、totalPages()、hasNext()。页码从 1 开始,page/size 小于 1 抛 QueryException。
forUpdate()
生成 SELECT ... FOR UPDATE 悲观锁,必须在事务中与 using(tx) 搭配:
rf.transaction { tx: Tx =>
let rows = User.query().using(tx)
.filter(User.col().id == 1)
.forUpdate()
.all()
}批量写操作
以下方法执行后立即返回受影响行数,不返回实体:
updateWhere()
按查询条件批量更新,返回 Int64 受影响行数:
let updated: Int64 = Post.query().using(rf)
.filter(Post.col().user_id == author.id)
.updateWhere(["title"], ["UPDATED"])setCols(列名)与setVals(值)等长且一一对应;两者长度不一致或列数为 0 抛QueryException- 条件复用链式
filter();不触发实体钩子
deleteWhere()
按查询条件批量删除,返回 Int64 受影响行数:
let deleted: Int64 = Post.query().using(rf)
.filter(Post.col().user_id == author.id)
.deleteWhere()- 条件复用链式
filter();不加条件即删除全表,需自行保证WHERE已设置 - 不触发实体钩子
高级用法
自定义 Mapper
手动构造 Query<T> 时需设置 mapper。columnMap 以列名为 key,索引为 value:
let q = Query<User>()
q.select([Expr.Column("id"), Expr.Column("name")])
q.from("users")
q.setMapper({ result: QueryResult, columnMap: HashMap<String, Int64> =>
let u = User()
u.id = result.get<Int64>(columnMap.get("id").getOrThrow())
u.name = result.get<String>(columnMap.get("name").getOrThrow())
u
})
let users = q.using(rf).all()