QueryEngine 支持通过 GraphQL、RESTful、NestQL 接入。本文是 NestQL(Nested Query Language,嵌套查询语言):库方法 get / head / post / put / delete,以及 HTTP POST /get、/head、/post、/put、/delete。/nestql/get 等路径是别名,请求体相同。
请求和响应都是 JSON 对象,键的位置与请求对应。这种形状形似 APIJSON,产品名是 NestQL,不依赖、不包含 APIJSON。库方法成功时不包一层 code / message。失败抛异常,不返回错误码对象。HTTP 成功时在同一层加上 code 与 message。
另外两种入口见 rest.md 和 graphql.md。
一次调用只有一个 method,由 engine.get / head / post / put / delete 或 execute(Method, ...) 决定。
{ "User": { "id": 38710 } }{
"[]": {
"page": 0,
"count": 10,
"User": {
"@column": "id,name:userName",
"@order": "id-,name+"
}
}
}User[] 与匿名 [] 相同,只是响应键用请求里的名字。数组元素是 { "User": { ...列 } }。
page 从 0 开始,偏移是 page * count。没写 count 时用 defaultPageSize(默认 10)。
HEAD 返回总行数,忽略 page、count、@column、@order:
{ "User": { "count": 3 } }数组的 HEAD 只统计主表:{ "[]": { "count": 4 } }。
条件之间是 AND。
| 写法 | SQL |
|---|---|
"id": 1 |
id = ? |
"tag": null |
tag IS NULL |
"sex!": 0 |
sex <> ? |
"sex!": null |
sex IS NOT NULL |
"id>": 1 "id>=" "id<" "id<=" |
对应比较 |
"id{}": [1, 2] 或 "id{}": "1,2" |
id IN (?, ?) |
"id!{}": [1, 2] |
id NOT IN (?, ?) |
"id{}": ">=10,<=20" |
id >= ? AND id <= ? |
"name$": "%Tom%" |
name LIKE ? |
"name$": ["%a%", "%b%"] |
name LIKE ? OR name LIKE ? |
@column 只接受列名和 列:别名。@order 只接受 列、列+(升序)、列-(降序),多项用逗号分隔。
空的 IN 匹配 0 行。空的 NOT IN 匹配全部行。
{
"User": { "id": 38710 },
"Moment[]": {
"Moment": { "userId@": "/User/id" }
}
}| 路径 | 含义 |
|---|---|
/User/id |
从本次响应根开始 |
User/id |
从当前数组元素向外找同级键 |
[]/User/id |
最近一层数组元素 |
[]/[]/User/id |
再向外回退一层 |
同级键即使写在后面,也会按引用先执行。响应里的键顺序仍与请求一致。
数组里第一张不依赖兄弟键的表是主表,按 page / count 查询。其余对象表和嵌套数组按每一行再查,不是 JOIN。
引用不存在时,读操作得到 null(对象)或 [](数组)。写操作失败并回滚。
主键列默认 id,可用 idColumn(...) 修改。
{ "Todo": { "title": "买牛奶", "done": 0 } }POST 把字段写成 INSERT。没写 id 时,尝试用 JDBC 生成键回填 id。
{ "Todo": { "id": 1, "title": "改" } }PUT 把 id(以及带运算符的字段)当作 WHERE,其他字段当作 SET。必须带主键条件,且不会修改主键本身。
{ "Todo": { "id": 1 } }DELETE 的字段都是 WHERE,同样必须有主键。"id{}": [1, 2] 可以一次更新或删除多行。
批量写入:
{ "Todo[]": [ { "title": "一" }, { "title": "二" } ] }POST 还接受两种嵌套插入。查询形态的 表名[]: { 主表, 子表[] } 插入主表一行,再按嵌套插入子表;响应是长度为 1 的数组。父行对象里的 子表[]: [ { 列, 子表[] } ] 按每个元素插入,外键列能唯一确定时自动填父行主键。[]/父表/id 读当前元素里刚插入的父行,/父表/id 读响应根上的父行。PUT / DELETE 不接受这两种嵌套。以前 POST 会拒绝查询形态,现在改为插入,这是不兼容变更。
写操作的响应带 count(影响行数)。GET 的对象或数组不额外加 count。
对象查询(不是 [])若命中 2 行及以上,抛 BadRequestException。要列表就用数组。
只有 page 和 count 有软限制,且可以配置:
count大于maxPageSize(默认 100)时夹到上限,不报错page大于maxPageIndex(默认 0,表示不限制)时夹到上限,不报错maxPageSize(0)关闭 count 夹紧count <= 0或page < 0视为非法请求
没有查询深度、对象个数、数组个数、SQL 条数、更新条数的硬拒绝。12 层以上的 Child[] 可以正常返回。实际边界是数据库超时、连接池和 JVM 栈,不是协议常量。queryTimeoutSeconds 会传给 Statement.setQueryTimeout,0 表示不额外设置。
根上的字符串 tag、version、format,以及数组上的数字 query,会被忽略。它们如果是对象,则仍当作表名。
- 库方法的成功响应不带
code/msg,失败抛异常。HTTP 模块另加顶层code与message(成功为 200 /success),字段名是message而不是msg。不提供登录,因此没有 401。 - 无登录、角色、会话。
registerTable即全部 method。 - 未注册表直接拒绝。
- 不设深度、对象数、数组数、SQL 条数、更新条数硬上限。
page/count只夹紧,不因为“太深”失败。 - 不做 JOIN。同级关联是按引用再查。
- 对象查询命中多行会失败,而不是只取第一行。
@column不允许函数、子查询、表达式。name$不会自动补%。@order不接受DESC原文,只接受+/-。- 不支持
@raw、@group、@having、正则~、远程函数、存储过程、脚本条件。 - 数组上的
query数字被忽略。GET 返回当前页,HEAD 返回总行数,没有 query=0/1/2 组合语义。 - PUT / DELETE 必须带主键条件。
- 一次请求一个本地 JDBC 事务。不支持多数据源事务。
- 引用目标缺失时,读路径得到空结果,写路径回滚。
- 标识符只允许 ASCII 字母、数字、下划线。表名大小写不敏感,只登记一次;MySQL 物理表名折成小写,达梦折成大写。列名大小写保持请求原文。
- 整型列统一成 64 位整数;日期时间转成字符串。
- 达梦默认把物理标识符转成大写;别名保持请求中的大小写。
- 执行顺序按引用拓扑排序,响应键顺序与请求一致。
- 批量写入使用
表名[]: [ {...} ]。POST 也接受查询形态的表名[]: { ... },语义是插入树(以前会拒绝)。PUT / DELETE 仍拒绝查询形态。
不在本版实现、也不要依赖其存在的能力:完整 JOIN 矩阵、@raw、存储过程、远程函数、多数据源事务、驼峰 format 转换。