查询
模型有了、视图有了,把它们接起来就是查询。createQuery(model, (q, table) => …) 以回调形式构建查询:
// ① 导入:model/prop 建模型,dto 建视图;sqlClient 是示例提供的客户端
import { model, prop, dto } from '@ts-grm/core'
import { sqlClient } from './infra/sql-client'
// ② 声明模型:Book 表(id/name/edition/price 四列);字段即列,类型即约束
const Book = model("Book", "id", class {
id = prop.i64() // 主键,64 位整数
name = prop.str(50) // 定长字符串(50)
edition = prop.i32() // 32 位整数
price = prop.num(10, 2) // 定点数:总长 10、小数 2 位(金额)
})
// ③ 声明视图:本次查询只取 id/name/price 三列——查询结果就是这个形状
const BookView = dto.view(Book, c => [
c.id,
c.name,
c.price,
])
// ④ 构建并执行查询:价格 > 40 的书,按价格从高到低
const rows = await sqlClient.createQuery(Book, (q, book) => {
q.where(book.price.gt(40)) // 过滤:price > 40
q.orderBy(book.price.desc()) // 排序:价格降序
return q.select(book.fetch(BookView)) // 取形:按 BookView 返回
}).fetchList() // 收尾:执行,返回数组
console.log(rows) // 结果面板会输出数组
运行结果(沙盒种子数据):
[
{ "id": 2, "name": "SQL Mastery", "price": 79 },
{ "id": 1, "name": "TypeScript in Depth", "price": 59.9 },
{ "id": 3, "name": "ORM Patterns", "price": 45.5 }
]
调用形态拆解:
| 一段 | 作用 |
|---|---|
createQuery(Book, (q, book) => …) | 从 Book 发起查询;book 是表对象,属性即列表达式 |
q.where(book.price.gt(40)) | 过滤条件;多个 where 之间默认 AND |
q.orderBy(book.price.desc()) | 排序;可写多列(asc 为默认) |
q.select(book.fetch(BookView)) | 按视图取形 |
.fetchList() | 收尾执行,返回视图类型数组 |
注意与常见 ORM 的差异:条件写在列表达式上(book.price.gt(40)),而不是 where({ price: { gt: 40 } }) 对象字面量——写错属性名编译期就报错。
条件表达式
book.id.eq(3) // 等于
book.price.ne(45.5) // 不等于
book.price.gt(50).lt(80) // 大于 / 小于(可链式)
book.price.between(30, 80) // 区间
book.name.ilike("sql") // 不区分大小写包含匹配(like 区分大小写)
book.id.in(1, 2, 3) // IN
动态条件用 xxxIf 系列:book.price.gteIf(search.min)——参数为 null/undefined 时该条件自动省略(「查询 API → 条件表达式」展开)。
收尾方法
查询对象(createQuery 的返回值)按需求选一个收尾方法执行:
| 方法 | 语义 | 返回 |
|---|---|---|
fetchList() | 多行 | T[] |
fetchRequired() | 恰好一行:0 行抛 NoDataError、多行抛 TooManyDataError | T |
fetchOptional() | 至多一行:无行返回 null({ nullAsUndefined: true } 时返回 undefined) | T | null |
fetchPage({ pageNo?, pageSize }) | 分页:总数 + 当前页(见下) | Page<T> |
fetchRange({ limit, offset? }) | 区间:跳过 offset 行取 limit 行 | T[] |
fetchCount() | 行数 | number |
没有 .fetchOne()——"必须有且仅有一行"用 fetchRequired(),"至多一行"用 fetchOptional(),不要把行数例外留给自己处理。
分页示例——一次拿回总数与分页信息:
// fetchPage:按页取数。pageNo 从 1 开始,缺省 1;pageSize 必填
// (片段:完整可运行版见工作区)
const page = await sqlClient.createQuery(Book, (q, book) => {
q.orderBy(book.id) // 分页必须配合稳定排序,否则页间数据会漂移
return q.select(book.fetch(BookView))
}).fetchPage({ pageNo: 1, pageSize: 2 })
console.log(page.totalRowCount) // 3(种子数据共 3 本)
console.log(page.pageNo) // 1
console.log(page.isFirstPage) // true
console.log(page.isLastPage) // false
console.log(page.rows) // 第 1 页的 2 行
Page<T> 结构:totalRowCount / totalPageCount / pageNo / isFirstPage / isLastPage / rows。
下一步
「安装 → 模型 → 视图 → 查询」闭环已完成,你已经能在沙盒里跑 ts-grm 了(环境与数据库抽屉的使用见「沙盒环境」页)。接下来进入核心概念:模型的另一半——关联、继承与多态、复合值,以及整个框架的理念基石「DTO 哲学」。