Date: Sun, 19 May 2024 17:38:44 +0800
Subject: [PATCH 040/184] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E4=B8=BA=207.0.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 4 ++--
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index a3ecc3338..45f4522ca 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -4,8 +4,8 @@
4.0.0
com.github.Tencent
- APIJSON-spring-boot3
- 6.4.3
+ APIJSON
+ 7.0.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index a02013311..3bdad0525 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "6.4.3";
+ public static final String VERSION = "7.0.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From aab39ff28dc65982ae5a5bec35af86cc40270b78 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 19 May 2024 23:28:06 +0800
Subject: [PATCH 041/184] =?UTF-8?q?=E6=95=B0=E5=AD=97=E8=BD=AC=E5=AD=97?=
=?UTF-8?q?=E7=AC=A6=E4=B8=B2=E8=8C=83=E5=9B=B4=E4=BC=98=E5=8C=96=E4=B8=BA?=
=?UTF-8?q?=20JavaScript=20=E7=9A=84=20Number.MAX=5FSAFE=5FINTEGER=20~=20N?=
=?UTF-8?q?umber.MIN=5FSAFE=5FINTEGER?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLExecutor.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index bb476783a..03cd3506d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1077,10 +1077,14 @@ public Object getNumVal(Number value) {
}
double v = value.doubleValue();
- if (v > Integer.MAX_VALUE || v < Integer.MIN_VALUE) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
+ // if (v > Integer.MAX_VALUE || v < Integer.MIN_VALUE) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
+ // return value.toString();
+ // }
+ // JavaScript: Number.MAX_SAFE_INTEGER ~ Number.MIN_SAFE_INTEGER
+ if (v > 9007199254740991L || v < -9007199254740991) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
return value.toString();
}
-
+
return value;
}
From 75902360ef1ba27661259a91d2eccb99817e1bbb Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 19 May 2024 23:29:02 +0800
Subject: [PATCH 042/184] JavaScript: Number.MAX_SAFE_INTEGER ~
Number.MIN_SAFE_INTEGER
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 03cd3506d..426a53ee5 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1081,7 +1081,7 @@ public Object getNumVal(Number value) {
// return value.toString();
// }
// JavaScript: Number.MAX_SAFE_INTEGER ~ Number.MIN_SAFE_INTEGER
- if (v > 9007199254740991L || v < -9007199254740991) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
+ if (v > 9007199254740991L || v < -9007199254740991L) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
return value.toString();
}
From 482368c6ef8b20cb5363477f5d754a3deeff2692 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 19 May 2024 23:30:03 +0800
Subject: [PATCH 043/184] Update README-English.md
---
README-English.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README-English.md b/README-English.md
index 3cd37214e..4d4aacb19 100644
--- a/README-English.md
+++ b/README-English.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-🏆 Tencent Top 8 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
+🏆 Tencent Top 7 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
中文版
From b70ee9074e3e6ff30b17f52340408f9f829f4067 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 19 May 2024 23:32:54 +0800
Subject: [PATCH 044/184] =?UTF-8?q?=E8=85=BE=E8=AE=AF=E5=86=85=E5=A4=96=20?=
=?UTF-8?q?5=20=E4=B8=AA=E5=A5=96=E9=A1=B9=E3=80=81=E8=85=BE=E8=AE=AF?=
=?UTF-8?q?=E5=BC=80=E6=BA=90=E5=89=8D=E4=B8=83=E3=80=81GitHub=20Java=20To?=
=?UTF-8?q?p=20100=E3=80=81Trending=20=E6=97=A5=E5=91=A8=E6=9C=88=E6=A6=9C?=
=?UTF-8?q?=E5=A4=A7=E6=BB=A1=E8=B4=AF=20=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/tree/master?tab=readme-ov-file#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 86399e402..9f7dd24ac 100644
--- a/README.md
+++ b/README.md
@@ -180,7 +180,7 @@ https://github.com/Tencent/APIJSON/wiki
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
* **社区影响力大** (GitHub 16K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
-* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前八、腾讯后端 Star 第一、GitHub Java 日周月榜大满贯 等)
+* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前七、GitHub Java Top 100、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
From 4d199a1591aec3ce6e2901b64720cd21fcf959dc Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 25 May 2024 22:52:03 +0800
Subject: [PATCH 045/184] =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A8=E8=8D=90?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E3=80=8AAPIJSON=E8=AF=AD=E6=B3=95?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8=EF=BC=8C=E8=B6=85=E8=AF=A6=E7=BB=86=E3=80=8B?=
=?UTF-8?q?=EF=BC=8C=E6=84=9F=E8=B0=A2=E4=BD=9C=E8=80=85=E7=9A=84=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
点赞、收藏、转发支持热心的作者吧 ^_^
https://juejin.cn/post/7370950331599306806
---
README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README.md b/README.md
index 9f7dd24ac..0f2290e2b 100644
--- a/README.md
+++ b/README.md
@@ -602,6 +602,9 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[APIJSON使用介绍](http://api.flyrise.cn:9099/docs/open-docs//1459)
[MassCMS With APIJSON最佳实践](https://zhuanlan.zhihu.com/p/655826966)
+
+[APIJSON语法使用,超详细](https://juejin.cn/post/7370950331599306806)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
From 72996e95507e0de4201294e34d01bbbdc97136f0 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 27 May 2024 00:45:20 +0800
Subject: [PATCH 046/184] =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A8=E8=8D=90?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E3=80=8AAPIJSON=E8=AF=AD=E6=B3=95?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8=EF=BC=8C=E8=B6=85=E8=AF=A6=E7=BB=86=E3=80=8B?=
=?UTF-8?q?=EF=BC=8C=E6=84=9F=E8=B0=A2=E4=BD=9C=E8=80=85=E7=9A=84=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
点赞、收藏、转发支持热心的作者吧 ^_^
https://blog.csdn.net/qq_36565607/article/details/139167040
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0f2290e2b..ad60c57fb 100644
--- a/README.md
+++ b/README.md
@@ -603,7 +603,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[MassCMS With APIJSON最佳实践](https://zhuanlan.zhihu.com/p/655826966)
-[APIJSON语法使用,超详细](https://juejin.cn/post/7370950331599306806)
+[APIJSON语法使用,超详细](https://blog.csdn.net/qq_36565607/article/details/139167040)
### 生态项目
From 5e4e848dde5984cd89fff27955a15862e4ae4d21 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 2 Jun 2024 15:49:25 +0800
Subject: [PATCH 047/184] =?UTF-8?q?Java=20=E7=89=88=E5=B7=B2=E7=BB=8F?=
=?UTF-8?q?=E6=98=AF=E5=9B=BD=E5=86=85=E9=A1=B6=E7=BA=A7=E3=80=81=E5=9B=BD?=
=?UTF-8?q?=E9=99=85=E4=B8=80=E6=B5=81=E7=9A=84=20Java=20=E5=BC=80?=
=?UTF-8?q?=E6=BA=90=E9=A1=B9=E7=9B=AE=E4=BA=86=20-=20=E6=A0=B9=E6=8D=AE?=
=?UTF-8?q?=E5=BC=80=E6=BA=90=E6=8C=87=E5=8D=97=E9=92=88=E6=8A=A5=E5=91=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#%E7%BB%9F%E8%AE%A1%E5%88%86%E6%9E%90
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index ad60c57fb..28ac01782 100644
--- a/README.md
+++ b/README.md
@@ -488,6 +488,8 @@ https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
+根据开源指南针报告,APIJSON Java 版已经是国内顶级、国际一流的 Java 开源项目了 [#518](https://github.com/Tencent/APIJSON/issues/518)
+
### 规划及路线图
新增功能、强化安全、提高性能、增强稳定、完善文档、丰富周边、推广使用
From 201c078083a4c8aa59e0aa93f24af6ab18728ec8 Mon Sep 17 00:00:00 2001
From: Bill <1594805355@qq.com>
Date: Sun, 2 Jun 2024 17:02:21 +0800
Subject: [PATCH 048/184] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=92=8C?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=20TABLE=5FSCHEMA=5FMAP?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 21 ++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index c39fd9d43..8c2339dcf 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -130,6 +130,11 @@ public abstract class AbstractSQLConfig implements SQLConfig TABLE_SCHEMA_MAP;
+
/**
* 表名映射,隐藏真实表名,对安全要求很高的表可以这么做
*/
@@ -157,6 +162,19 @@ public abstract class AbstractSQLConfig implements SQLConfig\\|\\[\\]\\{\\} /\\.\\+\\-\\*\\^\\?\\(\\)\\$]+$");
+ TABLE_SCHEMA_MAP = new HashMap<>();
+ TABLE_SCHEMA_MAP.put(Table.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(Column.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(PgClass.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(PgAttribute.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(SysTable.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(SysColumn.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(ExtendedProperty.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(AllTable.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(AllColumn.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(AllTableComment.class.getSimpleName(), DEFAULT_SCHEMA);
+ TABLE_SCHEMA_MAP.put(AllColumnComment.class.getSimpleName(), DEFAULT_SCHEMA);
+
TABLE_KEY_MAP = new HashMap<>();
TABLE_KEY_MAP.put(Table.class.getSimpleName(), Table.TABLE_NAME);
TABLE_KEY_MAP.put(Column.class.getSimpleName(), Column.TABLE_NAME);
@@ -1320,7 +1338,8 @@ public String getSQLSchema() {
return ""; //Oracle, Dameng 的 all_tables, dba_tables 和 all_tab_columns, dba_columns 表好像不属于任何 Schema
}
- String sch = getSchema();
+ //String sch = getSchema();
+ String sch = TABLE_SCHEMA_MAP.get(table);
return sch == null ? DEFAULT_SCHEMA : sch;
}
@Override
From c3a3399224b1fed1fb2f7751ae7a62d75086691e Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 2 Jun 2024 17:13:13 +0800
Subject: [PATCH 049/184] =?UTF-8?q?getSQLSchema=20=E6=A0=B9=E6=8D=AE?=
=?UTF-8?q?=E4=BC=98=E5=85=88=E7=BA=A7=E5=A4=84=E7=90=86=E4=B8=8D=E5=90=8C?=
=?UTF-8?q?=E6=83=85=E5=86=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 8c2339dcf..6b714e587 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1323,7 +1323,7 @@ public String getSchema() {
@Override
public String getSQLSchema() {
String table = getTable();
- //强制,避免因为全局默认的 @schema 自动填充进来,导致这几个类的 schema 为 sys 等其它值
+ // FIXME 全部默认填充判断是 系统表 则不填充 // 强制,避免因为全局默认的 @schema 自动填充进来,导致这几个类的 schema 为 sys 等其它值
if (Table.TAG.equals(table) || Column.TAG.equals(table)) {
return SCHEMA_INFORMATION; //MySQL, PostgreSQL, SQL Server 都有的
}
@@ -1338,9 +1338,11 @@ public String getSQLSchema() {
return ""; //Oracle, Dameng 的 all_tables, dba_tables 和 all_tab_columns, dba_columns 表好像不属于任何 Schema
}
- //String sch = getSchema();
- String sch = TABLE_SCHEMA_MAP.get(table);
- return sch == null ? DEFAULT_SCHEMA : sch;
+ String sch = getSchema(); // 前端传参 @schema 优先
+ if (sch == null) {
+ sch = TABLE_SCHEMA_MAP.get(table); // 其次 Access 表 alias 和 schema 配置
+ }
+ return sch == null ? DEFAULT_SCHEMA : sch; // 最后代码默认兜底配置
}
@Override
public AbstractSQLConfig setSchema(String schema) {
From f05835378a37ca3f30e14bebe6e4463950622ea7 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Jun 2024 19:22:15 +0800
Subject: [PATCH 050/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?key[]:{=20query:2=20=E6=88=96=20query:"All"=20}=20=E9=BB=98?=
=?UTF-8?q?=E8=AE=A4=E8=BF=94=E5=9B=9E=E5=88=97=E8=A1=A8=E5=88=86=E9=A1=B5?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractObjectParser.java | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index e64420138..6b1692357 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -27,6 +27,7 @@
import static apijson.JSONObject.KEY_COMBINE;
import static apijson.JSONObject.KEY_DROP;
import static apijson.JSONObject.KEY_TRY;
+import static apijson.JSONRequest.KEY_QUERY;
import static apijson.RequestMethod.POST;
import static apijson.RequestMethod.PUT;
import static apijson.orm.SQLConfig.TYPE_ITEM;
@@ -555,8 +556,22 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
}
}
+ String query = value.getString(KEY_QUERY);
child = parser.onArrayParse(value, path, key, isSubquery);
isEmpty = child == null || ((JSONArray) child).isEmpty();
+
+ if (isEmpty == false && ("2".equals(query) || "ALL".equals(query))) {
+ String infoKey = JSONResponse.formatArrayKey(key) + "Info";
+ if (request.containsKey("total@") == false && request.containsKey(infoKey + "@") == false) {
+ // onParse("total@", "/" + key + "/total");
+ // onParse(infoKey + "@", "/" + key + "/info");
+ // 替换为以下性能更好、对流程干扰最小的方式:
+ String totalPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/total");
+ String infoPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/info");
+ response.put("total", onReferenceParse(totalPath));
+ response.put(infoKey, onReferenceParse(infoPath));
+ }
+ }
}
else { //APIJSON Object
boolean isTableKey = JSONRequest.isTableKey(Pair.parseEntry(key, true).getKey());
From 8715e1298d1303b6ead6abd2f5d87cf91c529959 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Jun 2024 19:24:00 +0800
Subject: [PATCH 051/184] =?UTF-8?q?key[]:{=20query:2=20=E6=88=96=20query:"?=
=?UTF-8?q?All"=20}=20=E5=9C=A8=E5=B7=B2=E6=9C=89=20total=20=E5=92=8C=20ke?=
=?UTF-8?q?yListInfo=20=E5=AD=97=E6=AE=B5=E6=97=B6=E4=B8=8D=E8=BF=94?=
=?UTF-8?q?=E5=9B=9E=E9=BB=98=E8=AE=A4=E7=9A=84=E5=88=97=E8=A1=A8=E5=88=86?=
=?UTF-8?q?=E9=A1=B5=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 6b1692357..23b9b271a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -562,7 +562,8 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
if (isEmpty == false && ("2".equals(query) || "ALL".equals(query))) {
String infoKey = JSONResponse.formatArrayKey(key) + "Info";
- if (request.containsKey("total@") == false && request.containsKey(infoKey + "@") == false) {
+ if (request.containsKey("total") == false && request.containsKey(infoKey) == false
+ && request.containsKey("total@") == false && request.containsKey(infoKey + "@") == false) {
// onParse("total@", "/" + key + "/total");
// onParse(infoKey + "@", "/" + key + "/info");
// 替换为以下性能更好、对流程干扰最小的方式:
From 09a60dc89191c5ab2c6fa6ff21c663e7a69cfced Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Jun 2024 19:31:27 +0800
Subject: [PATCH 052/184] =?UTF-8?q?key[]:{=20query:2=20=E6=88=96=20query:"?=
=?UTF-8?q?All"=20}=20=E9=BB=98=E8=AE=A4=E8=BF=94=E5=9B=9E=E5=88=97?=
=?UTF-8?q?=E8=A1=A8=E5=88=86=E9=A1=B5=E4=BF=A1=E6=81=AF=20=E8=B0=83?=
=?UTF-8?q?=E6=95=B4=20total=20=E7=9A=84=20key=20=E5=90=8D=EF=BC=8C?=
=?UTF-8?q?=E9=81=BF=E5=85=8D=E5=90=8C=E7=BA=A7=E5=A4=9A=E5=88=97=E8=A1=A8?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E9=87=8D=E5=90=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractObjectParser.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 23b9b271a..03b419978 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -561,15 +561,16 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
isEmpty = child == null || ((JSONArray) child).isEmpty();
if (isEmpty == false && ("2".equals(query) || "ALL".equals(query))) {
+ String totalKey = JSONResponse.formatArrayKey(key) + "Total";
String infoKey = JSONResponse.formatArrayKey(key) + "Info";
- if (request.containsKey("total") == false && request.containsKey(infoKey) == false
- && request.containsKey("total@") == false && request.containsKey(infoKey + "@") == false) {
+ if ((request.containsKey(totalKey) || request.containsKey(infoKey)
+ || request.containsKey(totalKey + "@") || request.containsKey(infoKey + "@")) == false) {
// onParse("total@", "/" + key + "/total");
// onParse(infoKey + "@", "/" + key + "/info");
// 替换为以下性能更好、对流程干扰最小的方式:
String totalPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/total");
String infoPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/info");
- response.put("total", onReferenceParse(totalPath));
+ response.put(totalKey, onReferenceParse(totalPath));
response.put(infoKey, onReferenceParse(infoPath));
}
}
From 0093589068d7962719798e665c4bc49c7074a3cb Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Jun 2024 23:02:39 +0800
Subject: [PATCH 053/184] =?UTF-8?q?key[]:{=20query:2=20=E6=88=96=20query:"?=
=?UTF-8?q?All"=20}=20=E8=A7=A3=E5=86=B3=E5=88=97=E8=A1=A8=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E4=B8=BA=E7=A9=BA=E4=B8=8D=E8=BF=94=E5=9B=9E=E5=88=97?=
=?UTF-8?q?=E8=A1=A8=E5=88=86=E9=A1=B5=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractObjectParser.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 03b419978..94e2020d0 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -560,7 +560,7 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
child = parser.onArrayParse(value, path, key, isSubquery);
isEmpty = child == null || ((JSONArray) child).isEmpty();
- if (isEmpty == false && ("2".equals(query) || "ALL".equals(query))) {
+ if ("2".equals(query) || "ALL".equals(query)) { // 不判断 isEmpty,因为分页数据可能只是某页没有
String totalKey = JSONResponse.formatArrayKey(key) + "Total";
String infoKey = JSONResponse.formatArrayKey(key) + "Info";
if ((request.containsKey(totalKey) || request.containsKey(infoKey)
@@ -568,8 +568,10 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
// onParse("total@", "/" + key + "/total");
// onParse(infoKey + "@", "/" + key + "/info");
// 替换为以下性能更好、对流程干扰最小的方式:
- String totalPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/total");
- String infoPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key + "/info");
+
+ String keyPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, "/" + key);
+ String totalPath = keyPath + "/total";
+ String infoPath = keyPath + "/info";
response.put(totalKey, onReferenceParse(totalPath));
response.put(infoKey, onReferenceParse(infoPath));
}
From abe10a270ac7f4b19d8a46bf739cbfc9211e3d90 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 16 Jun 2024 23:04:33 +0800
Subject: [PATCH 054/184] =?UTF-8?q?=E8=85=BE=E8=AE=AF=E5=86=85=E5=A4=96=20?=
=?UTF-8?q?5=20=E4=B8=AA=E5=A5=96=E9=A1=B9=E3=80=81=E8=85=BE=E8=AE=AF?=
=?UTF-8?q?=E5=BC=80=E6=BA=90=E5=89=8D=E4=B8=83=E3=80=81=E8=85=BE=E8=AE=AF?=
=?UTF-8?q?=E5=90=8E=E7=AB=AF=20Star=20=E7=AC=AC=E4=B8=80=E3=80=81Trending?=
=?UTF-8?q?=20=E6=97=A5=E5=91=A8=E6=9C=88=E6=A6=9C=E5=A4=A7=E6=BB=A1?=
=?UTF-8?q?=E8=B4=AF=20=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 28ac01782..8d6afdad7 100644
--- a/README.md
+++ b/README.md
@@ -180,7 +180,7 @@ https://github.com/Tencent/APIJSON/wiki
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
* **社区影响力大** (GitHub 16K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
-* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前七、GitHub Java Top 100、Trending 日周月榜大满贯 等)
+* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前七、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
From 35012589cfe57991523eab7c0d4f886cbdb0ad85 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 30 Jun 2024 18:02:00 +0800
Subject: [PATCH 055/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8A=8A=E5=BC=95?=
=?UTF-8?q?=E7=94=A8=E8=B5=8B=E5=80=BC=E8=B7=AF=E5=BE=84=20URL=20encode=20?=
=?UTF-8?q?=E5=90=8E=E7=9A=84=E5=80=BC=20decode=20=E5=9B=9E=E5=8E=9F?=
=?UTF-8?q?=E5=A7=8B=E5=80=BC=EF=BC=8C=E4=BE=8B=E5=A6=82=20%2Fuser%2Flist?=
=?UTF-8?q?=20->=20/user/list?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/apijson/orm/AbstractParser.java | 33 +++++++++++++++----
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 1a7441086..44b196615 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -12,7 +12,9 @@
import java.io.UnsupportedEncodingException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
+import java.net.URLDecoder;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
@@ -1778,15 +1780,17 @@ public static V getValue(JSONObject parent, String[] pathKeys
}
//逐层到达child的直接容器JSONObject parent
- final int last = pathKeys.length - 1;
+ int last = pathKeys.length - 1;
for (int i = 0; i < last; i++) {//一步一步到达指定位置
if (parent == null) {//不存在或路径错误(中间的key对应value不是JSONObject)
break;
}
- parent = getJSONObject(parent, pathKeys[i]);
+
+ String k = getDecodedKey(pathKeys[i]);
+ parent = getJSONObject(parent, k);
}
- return parent == null ? null : (V) parent.get(pathKeys[last]);
+ return parent == null ? null : (V) parent.get(getDecodedKey(pathKeys[last]));
}
@@ -1912,18 +1916,21 @@ public Object getValueByPath(String valuePath) {
}
//逐层到达targetKey的直接容器JSONObject parent
- if (keys != null && keys.length > 1) {
- for (int i = 0; i < keys.length - 1; i++) {//一步一步到达指定位置parentPath
+ int last = keys == null ? -1 : keys.length - 1;
+ if (last >= 1) {
+ for (int i = 0; i < last; i++) {//一步一步到达指定位置parentPath
if (parent == null) {//不存在或路径错误(中间的key对应value不是JSONObject)
break;
}
- parent = getJSONObject(parent, keys[i]);
+
+ String k = getDecodedKey(keys[i]);
+ parent = getJSONObject(parent, k);
}
}
if (parent != null) {
Log.i(TAG, "getValueByPath >> get from queryResultMap >> return parent.get(keys[keys.length - 1]);");
- target = keys == null || keys.length <= 0 ? parent : parent.get(keys[keys.length - 1]); //值为null应该报错NotExistExeption,一般都是id关联,不可为null,否则可能绕过安全机制
+ target = last < 0 ? parent : parent.get(getDecodedKey(keys[last])); //值为null应该报错NotExistExeption,一般都是id关联,不可为null,否则可能绕过安全机制
if (target != null) {
Log.i(TAG, "getValueByPath >> getValue >> return target = " + target);
return target;
@@ -1942,6 +1949,18 @@ public Object getValueByPath(String valuePath) {
return null;
}
+ /**解码 引用赋值 路径中的 key,支持把 URL encode 后的值,转为 decode 后的原始值,例如 %2Fuser%2Flist -> /user/list ; %7B%7D -> []
+ * @param key
+ * @return
+ */
+ public static String getDecodedKey(String key) {
+ try {
+ return URLDecoder.decode(key, StandardCharsets.UTF_8);
+ } catch (Throwable e) {
+ return key;
+ }
+ }
+
//依赖引用关系 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
From 8dbaa2dea9e063eac2bbf76f5f5155390eba512f Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 30 Jun 2024 18:43:15 +0800
Subject: [PATCH 056/184] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20format:=20true=20?=
=?UTF-8?q?=E5=AF=B9=E5=BA=94=20key=20=E8=BF=94=E5=9B=9E=E4=B8=8D=E6=98=AF?=
=?UTF-8?q?=E5=B0=8F=E9=A9=BC=E5=B3=B0=EF=BC=8C=E8=80=8C=E6=98=AF=E5=BC=BA?=
=?UTF-8?q?=E5=88=B6=E5=B0=8F=E5=86=99=EF=BC=8C=E4=BE=8B=E5=A6=82=20User[]?=
=?UTF-8?q?=20=20=E8=BF=94=E5=9B=9E=E4=B8=8D=E6=98=AF=20userList=20?=
=?UTF-8?q?=E8=80=8C=E6=98=AF=20userlist?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/JSONResponse.java | 114 ++++++++++++++++--
1 file changed, 101 insertions(+), 13 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONResponse.java b/APIJSONORM/src/main/java/apijson/JSONResponse.java
index 9f61edadf..21f3fe8f6 100755
--- a/APIJSONORM/src/main/java/apijson/JSONResponse.java
+++ b/APIJSONORM/src/main/java/apijson/JSONResponse.java
@@ -485,14 +485,14 @@ public static String formatKey(String fullName, boolean formatColon, boolean for
if (formatAt) { //关键词只去掉前缀,不格式化单词,例如 @a-b 返回 a-b ,最后不会调用 setter
fullName = formatAt(fullName);
}
- if (formatHyphen) {
- fullName = formatHyphen(fullName, firstCase != null);
+ if (formatHyphen && fullName.contains("-")) {
+ fullName = formatHyphen(fullName, true);
}
- if (formatUnderline) {
- fullName = formatUnderline(fullName, firstCase != null);
+ if (formatUnderline && fullName.contains("_")) {
+ fullName = formatUnderline(fullName, true);
}
- if (formatDollar) {
- fullName = formatDollar(fullName, firstCase != null);
+ if (formatDollar && fullName.contains("$")) {
+ fullName = formatDollar(fullName, true);
}
// 默认不格式化普通 key:value (value 不为 [], {}) 的 key
@@ -520,32 +520,100 @@ public static String formatColon(@NotNull String key) {
* @param key
* @return
*/
+ public static String formatHyphen(@NotNull String key) {
+ return StringUtil.firstCase(formatHyphen(key, true), false);
+ }
+ /**A-b-cd-Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
public static String formatHyphen(@NotNull String key, Boolean firstCase) {
- return formatDivider(key, "-", firstCase);
+ return formatHyphen(key, firstCase, false);
+ }
+ /**A-b-cd-Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @param otherCase 非首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
+ public static String formatHyphen(@NotNull String key, Boolean firstCase, Boolean otherCase) {
+ return formatDivider(key, "-", firstCase, otherCase);
}
/**A_b_cd_Efg => ABCdEfg
* @param key
* @return
*/
+ public static String formatUnderline(@NotNull String key) {
+ return StringUtil.firstCase(formatUnderline(key, true), false);
+ }
+ /**A_b_cd_Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
public static String formatUnderline(@NotNull String key, Boolean firstCase) {
- return formatDivider(key, "_", firstCase);
+ return formatUnderline(key, firstCase, false);
+ }
+ /**A_b_cd_Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @param otherCase 非首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
+ public static String formatUnderline(@NotNull String key, Boolean firstCase, Boolean otherCase) {
+ return formatDivider(key, "_", firstCase, otherCase);
}
/**A$b$cd$Efg => ABCdEfg
* @param key
* @return
*/
+ public static String formatDollar(@NotNull String key) {
+ return StringUtil.firstCase(formatDollar(key, true), false);
+ }
+ /**A$b$cd$Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
public static String formatDollar(@NotNull String key, Boolean firstCase) {
- return formatDivider(key, "$", firstCase);
+ return formatDollar(key, firstCase, false);
+ }
+ /**A$b$cd$Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @param otherCase 非首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
+ public static String formatDollar(@NotNull String key, Boolean firstCase, Boolean otherCase) {
+ return formatDivider(key, "$", firstCase, otherCase);
}
/**A.b.cd.Efg => ABCdEfg
* @param key
* @return
*/
+ public static String formatDot(@NotNull String key) {
+ return StringUtil.firstCase(formatDot(key, true), false);
+ }
+ /**A.b.cd.Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
public static String formatDot(@NotNull String key, Boolean firstCase) {
- return formatDivider(key, ".", firstCase);
+ return formatDot(key, firstCase, false);
+ }
+ /**A.b.cd.Efg => ABCdEfg
+ * @param key
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @param otherCase 非首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
+ public static String formatDot(@NotNull String key, Boolean firstCase, Boolean otherCase) {
+ return formatDivider(key, ".", firstCase, otherCase);
}
/**A/b/cd/Efg => ABCdEfg
@@ -559,16 +627,36 @@ public static String formatDivider(@NotNull String key, Boolean firstCase) {
/**去除分割符,返回驼峰格式
* @param key
* @param divider
- * @param firstCase
+ * @return
+ */
+ public static String formatDivider(@NotNull String key, @NotNull String divider) {
+ return StringUtil.firstCase(formatDivider(key, divider, true), false);
+ }
+ /**去除分割符,返回驼峰格式
+ * @param key
+ * @param divider
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
* @return
*/
public static String formatDivider(@NotNull String key, @NotNull String divider, Boolean firstCase) {
+ return formatDivider(key, divider, firstCase, false);
+ }
+
+ /**去除分割符,返回驼峰格式
+ * @param key
+ * @param divider
+ * @param firstCase 首字符的大小写,true-大写,false-小写,null-不处理
+ * @param otherCase 非首字符的大小写,true-大写,false-小写,null-不处理
+ * @return
+ */
+ public static String formatDivider(@NotNull String key, @NotNull String divider, Boolean firstCase, Boolean otherCase) {
String[] parts = StringUtil.split(key, divider);
StringBuilder name = new StringBuilder();
for (String part : parts) {
- part = part.toLowerCase(); // 始终小写,也方便反过来 ABCdEfg -> A_b_cd_Efg
+ if (otherCase != null) {
+ part = otherCase ? part.toUpperCase() : part.toLowerCase();
+ }
if (firstCase != null) {
- // 始终小写, A_b_cd_Efg -> ABCdEfg, firstCase ? part.toLowerCase() : part.toUpperCase();
part = StringUtil.firstCase(part, firstCase);
}
name.append(part);
From 53adfabf14842b50e62151c94603a0d1821dca11 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jun 2024 19:10:54 +0800
Subject: [PATCH 057/184] Update Document.md
---
Document.md | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/Document.md b/Document.md
index 38adef507..90148bedc 100644
--- a/Document.md
+++ b/Document.md
@@ -403,24 +403,24 @@ DELETE: 删除数据 | base_url/delete/ | { TableName:{<
功能 | 键值对格式 | 使用示例
------------ | ------------ | ------------
- 查询数组 | "key[]":{},后面是JSONObject,key可省略。当key和里面的Table名相同时,Table会被提取出来,即 {Table:{Content}} 会被转化为 {Content} | [{"User[]":{"User":{}}}](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{}}}),查询一个User数组。这里key和Table名都是User,User会被提取出来,即 {"User":{"id", ...}} 会被转化为 {"id", ...},如果要进一步提取User中的id,可以把User[]改为User-id[]
- 匹配选项范围 | "key{}":[],后面是JSONArray,作为key可取的值的选项 | ["id{}":[38710,82001,70793]](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":[38710,82001,70793]}}}),对应SQL是`id IN(38710,82001,70793)`,查询id符合38710,82001,70793中任意一个的一个User数组
- 匹配条件范围 | "key{}":"条件0,条件1...",条件为SQL表达式字符串,可进行数字比较运算等 | ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}),对应SQL是`id<=80000 OR id>90000`,查询id符合id\<=80000 \| id>90000的一个User数组
- 包含选项范围 | "key<\>":Object => "key<\>":[Object],key对应值的类型必须为JSONArray,Object类型不能为JSON | ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}),对应SQL是`json_contains(contactIdList,38710)`,查询contactIdList包含38710的一个User数组
+ 查询数组 | "key[]":{},后面是 JSONObject,key 可省略。当 key 和里面的 Table 名相同时,Table 会被提取出来,即 {Table:{Content}} 会被转化为 {Content} | [{"User[]":{"User":{}}}](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{}}}),查询一个 User 数组。这里 key 和 Table 名都是 User,User 会被提取出来,即 {"User":{"id", ...}} 会被转化为 {"id", ...},如果要进一步提取 User 中的 id,可以把 User[] 改为 User-id[],其中 - 用来分隔路径中涉及的 key
+ 匹配选项范围 | "key{}":[],后面是 JSONArray,作为 key 可取的值的选项 | ["id{}":[38710,82001,70793]](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":[38710,82001,70793]}}}),对应 SQL 是`id IN(38710,82001,70793)`,查询 id 符合 38710,82001,70793 中任意一个的一个 User 数组
+ 匹配条件范围 | "key{}":"条件0,条件1...",条件为 SQL 表达式字符串,可进行数字比较运算等 | ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}),对应 SQL 是`id<=80000 OR id>90000`,查询 id 符合 id\<=80000 \| id>90000 的一个 User 数组
+ 包含选项范围 | "key<\>":Object => "key<\>":[value],key 对应值的类型必须为 JSONArray,value 类型不能为 JSON | ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}),对应SQL是`json_contains(contactIdList,38710)`,查询 contactIdList 包含 38710 的一个 User 数组
判断是否存在 | "key}{@":{ "from":"Table", "Table":{ ... } } 其中: }{ 表示 EXISTS; key 用来标识是哪个判断; @ 后面是 子查询 对象,具体见下方 子查询 的说明。 | ["id}{@":{ "from":"Comment", "Comment":{ "momentId":15 } }](http://apijson.cn:8080/get/{"User":{"id}{@":{"from":"Comment","Comment":{"momentId":15}}}}) WHERE EXISTS(SELECT * FROM Comment WHERE momentId=15)
- 远程调用函数 | "key()":"函数表达式",函数表达式为 function(key0,key1...),会调用后端对应的函数 function(JSONObject request, String key0, String key1...),实现 参数校验、数值计算、数据同步、消息推送、字段拼接、结构变换 等特定的业务逻辑处理, 可使用 - 和 + 表示优先级,解析 key-() > 解析当前对象 > 解析 key() > 解析子对象 > 解析 key+() | ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}),会调用远程函数 [boolean isContain(JSONObject request, String array, String value)](https://github.com/APIJSON/apijson-framework/blob/master/src/main/java/apijson/framework/APIJSONFunctionParser.java#L361-L374) ,然后变为 "isPraised":true 这种(假设点赞用户id列表包含了userId,即这个User点了赞)
- 存储过程 | "@key()":"SQL函数表达式",函数表达式为 function(key0,key1...) 会调用后端数据库对应的存储过程 SQL函数 function(String key0, String key1...) 除了参数会提前赋值,其它和 远程函数 一致 | ["@limit":10, "@offset":0, "@procedure()":"getCommentByUserId(id,@limit,@offset)"](http://apijson.cn:8080/get/{"User":{"@limit":10,"@offset":0,"@procedure()":"getCommentByUserId(id,@limit,@offset)"}}) 会转为 `getCommentByUserId(38710,10,0)` 来调用存储过程 SQL 函数 `getCommentByUserId(IN id bigint, IN limit int, IN offset int)` 然后变为 "procedure":{ "count":-1, "update":false, "list":[] } 其中 count 是指写操作影响记录行数,-1 表示不是写操作;update 是指是否为写操作(增删改);list 为返回结果集
- 引用赋值 | "key@":"key0/key1/.../refKey",引用路径为用/分隔的字符串。以/开头的是缺省引用路径,从声明key所处容器的父容器路径开始;其它是完整引用路径,从最外层开始。 被引用的refKey必须在声明key的上面。如果对refKey的容器指定了返回字段,则被引用的refKey必须写在@column对应的值内,例如 "@column":"refKey,key1,..." | ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) User内的id引用了与User同级的Moment内的userId, 即User.id = Moment.userId,请求完成后 "id@":"/Moment/userId" 会变成 "id":38710
- 子查询 | "key@":{ "range":"ALL", "from":"Table", "Table":{ ... } } 其中: range 可为 ALL,ANY; from 为目标表 Table 的名称; @ 后面的对象类似数组对象,可使用 count 和 join 等功能。 | ["id@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id=(SELECT min(userId) FROM Comment)
- 模糊搜索 | `"key$":"SQL搜索表达式"` => `"key$":["SQL搜索表达式"]`,任意SQL搜索表达式字符串,如 %key%(包含key), key%(以key开始), %k%e%y%(包含字母k,e,y) 等,%表示任意字符 | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}),对应SQL是`name LIKE '%m%'`,查询name包含"m"的一个User数组
- 正则匹配 | "key~":"正则表达式" => "key~":["正则表达式"],任意正则表达式字符串,如 ^[0-9]+$ ,*~ 忽略大小写,可用于高级搜索 | ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}),对应SQL是`name REGEXP '^[0-9]+$'`,查询name中字符全为数字的一个User数组
- 连续范围 | "key%":"start,end" => "key%":["start,end"],其中 start 和 end 都只能为 Boolean, Number, String 中的一种,如 "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"] ,可用于连续范围内的筛选 | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}),对应SQL是`date BETWEEN '2017-10-01' AND '2018-10-01'`,查询在2017-10-01和2018-10-01期间注册的用户的一个User数组
- 新建别名 | "name:alias",name映射为alias,用alias替代name。可用于 column,Table,SQL函数 等。只用于GET类型、HEAD类型的请求 | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}),对应SQL是`toId AS parentId`,将查询的字段toId变为parentId返回
- 增加 或 扩展 | "key+":Object,Object的类型由key指定,且类型为Number,String,JSONArray中的一种。如 82001,"apijson",["url0","url1"] 等。只用于PUT请求 | "praiseUserIdList+":[82001],对应SQL是`json_insert(praiseUserIdList,82001)`,添加一个点赞用户id,即这个用户点了赞
+ 远程调用函数 | "key()":"函数表达式",函数表达式为 function(key0,key1...),会调用后端对应的函数 function(JSONObject request, String key0, String key1...),实现 参数校验、数值计算、数据同步、消息推送、字段拼接、结构变换 等特定的业务逻辑处理, 可使用 - 和 + 表示优先级,解析 key-() > 解析当前对象 > 解析 key() > 解析子对象 > 解析 key+() | ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}),会调用远程函数 [boolean isContain(JSONObject request, String array, String value)](https://github.com/APIJSON/apijson-framework/blob/master/src/main/java/apijson/framework/APIJSONFunctionParser.java#L361-L374) ,然后变为 "isPraised":true 这种(假设点赞用户 id 列表包含了 userId,即这个 User 点了赞)
+ 存储过程 | "@key()":"SQL函数表达式",函数表达式为 function(key0,key1...) 会调用后端数据库对应的存储过程 SQL 函数 function(String key0, String key1...) 除了参数会提前赋值,其它和 远程函数 一致 | ["@limit":10, "@offset":0, "@procedure()":"getCommentByUserId(id,@limit,@offset)"](http://apijson.cn:8080/get/{"User":{"@limit":10,"@offset":0,"@procedure()":"getCommentByUserId(id,@limit,@offset)"}}) 会转为 `getCommentByUserId(38710,10,0)` 来调用存储过程 SQL 函数 `getCommentByUserId(IN id bigint, IN limit int, IN offset int)` 然后变为 "procedure":{ "count":-1, "update":false, "list":[] } 其中 count 是指写操作影响记录行数,-1 表示不是写操作;update 是指是否为写操作(增删改);list 为返回结果集
+ 引用赋值 | "key@":"key0/key1/.../refKey",引用路径为用 / 分隔的字符串。以 / 开头的是缺省引用路径,从声明 key 所处容器的父容器路径开始;其它是完整引用路径,从最外层开始。 被引用的 refKey 必须在声明 key 的上面。如果对 refKey 的容器指定了返回字段,则被引用的 refKey 必须写在 @column 对应的值内,例如 "@column":"refKey,key1,..." | ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) User 内的 id 引用了与 User 同级的 Moment 内的 userId, 即 User.id = Moment.userId,请求完成后 "id@":"/Moment/userId" 会变成 "id":38710
+ 子查询 | "key@":{ "range":"ALL", "from":"Table", // 可省略,默认为首个表对象 key 名 "Table":{ ... } } 其中: range 可为 ALL,ANY; from 为目标表 Table 的名称; @ 后面的对象类似数组对象,可使用 count 和 join 等功能。 | ["id@":{ "from":"Comment", // 可省略 "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id=(SELECT min(userId) FROM Comment)
+ 模糊搜索 | `"key$":"SQL搜索表达式"` => `"key$":["SQL搜索表达式"]`,任意 SQL 搜索表达式字符串,如 %key%(包含 key), key%(以 key 开始), %k%e%y%(包含字母 k,e,y) 等,% 表示任意字符 | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}),对应 SQL 是`name LIKE '%m%'`,查询 name 包含 "m" 的一个 User 数组
+ 正则匹配 | "key~":"正则表达式" => "key~":["正则表达式"],任意正则表达式字符串,如 ^[0-9]+$ ,*~ 忽略大小写,可用于高级搜索 | ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}),对应 SQL 是`name REGEXP '^[0-9]+$'`,查询 name 中字符全为数字的一个 User 数组
+ 连续范围 | "key%":"start,end" => "key%":["start,end"],其中 start 和 end 都只能为 Number, String 中的一种,如 "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"] ,可用于连续范围内的筛选 | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}),对应SQL是`date BETWEEN '2017-10-01' AND '2018-10-01'`,查询在2017-10-01和2018-10-01期间注册的用户的一个User数组
+ 新建别名 | "name:alias",name 映射为 alias,用 alias 替代 name。可用于 column,Table,SQL 函数 等。只用于 GET 类型、HEAD 类型的请求 | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}),对应 SQL 是`toId AS parentId`,将查询的字段 toId 变为 parentId 返回
+ 增加 或 扩展 | "key+":Object,Object的类型由key指定,且类型为 Number,String,JSONArray 中的一种。如 82001,"apijson",["url0","url1"] 等。只用于 PUT 请求 | "praiseUserIdList+":[82001],对应 SQL 是`json_insert(praiseUserIdList,82001)`,添加一个点赞用户 id,即这个用户点了赞
减少 或 去除 | "key-":Object,与"key+"相反 | "balance-":100.00,对应SQL是`balance = balance - 100.00`,余额减少100.00,即花费了100元
- 比较运算 | >, <, >=, <= 比较运算符,用于 ① 提供 "id{}":"<=90000" 这种条件范围的简化写法 ② 实现子查询相关比较运算 不支持 "key=":Object 和 "key!=":Object 这两种写法,直接用更简单的 "key":Object 和 "key!":Object 替代。 | ① ["id<=":90000](http://apijson.cn:8080/get/{"[]":{"User":{"id<=":90000}}}),对应SQL是`id<=90000`,查询符合id<=90000的一个User数组 ② ["id>@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id>@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id>(SELECT min(userId) FROM Comment)
- 逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于"key&{}":"条件"等 ② \| 可用于"key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如"key!":Object,也可像&,\|一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同"id{}":">90000,<=80000",对应SQL是`id>90000 OR id<=80000`,即id满足id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应SQL是`id NOT IN(82001,38710)`,即id满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
- 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中{}内的关键词,Object的类型由key指定 ① "count":Integer,查询数量,0 表示最大值,默认最大值为100 ② "page":Integer,查询页码,从0开始,默认最大值为100,一般和count一起用 ③ "query":Integer,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应SQL是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应SQL是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", "info@":"/[]/info"](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, //总数 "info":{ //分页详情 "total":139, //总数 "count":5, //每页数量 "page":0, //当前页码 "max":27, //最大页码 "more":true, //是否还有更多 "first":true, //是否为首页 "last":false //是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" //主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", //自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
- 对象关键词,可自定义 | "@key":Object,@key为 Table:{} 中{}内的关键词,Object的类型由@key指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果@column里声明了Table的id,则id也必须在@group中声明;其它情况下必须满足至少一个条件: 1.分组的key在@column里声明 2.Table主键在@group中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL函数条件,一般和@group一起用,函数一般在@column里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防SQL注入 ⑬ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索name或tag任何一个字段包含字符a的User列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询id,sex,name这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应SQL是`SELECT id,sex,name` ③ 查询按 name降序、id默认顺序 排序的User数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应SQL是`ORDER BY name DESC,id` ④ 查询按userId分组的Moment数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应SQL是`GROUP BY userId,id` ⑤ 查询 按userId分组、id最大值>=100 的Moment数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应SQL是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应SQL是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应SQL是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应SQL是`EXPLAIN` ⑫ 统计最近一周偶数userId的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应SQL是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 从pictureList获取第0张图片: ["@position":0, //自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
- 全局关键词 | 为最外层对象 {} 内的关键词。其中 @database,@schema, @datasource, @role, @explain 基本同对象关键词,见上方说明,区别是全局关键词会每个表对象中没有时自动放入,作为默认值。 ① "tag":String,后面的 tag 是非 GET、HEAD 请求中匹配请求的 JSON 结构的标识,一般是要查询的 Table 的名称或该名称对应的数组 Table[] 或 Table:[],由后端 Request 表中指定。 ② "version":Integer,接口版本,version 不传、为 null 或 <=0 都会使用最高版本,传了其它有效值则会使用最接近它的最低版本,由后端 Request 表中指定。 ③ "format":Boolean,格式化返回 Response JSON 的 key,一般是将 TableName 转为 tableName, TableName[] 转为 tableNameList, Table:alias 转为 alias, TableName-key[] 转为 tableNameKeyList 等小驼峰格式。 | ① 查隐私信息: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
+ 比较运算 | >, <, >=, <= 比较运算符,用于 ① 提供 "id{}":"<=90000" 这种条件范围的简化写法 ② 实现子查询相关比较运算 不支持 "key=":Object 和 "key!=":Object 这两种写法,直接用更简单的 "key":Object 和 "key!":Object 替代。 | ① ["id<=":90000](http://apijson.cn:8080/get/{"[]":{"User":{"id<=":90000}}}),对应 SQL 是`id<=90000`,查询符合id<=90000的一个User数组 ② ["id>@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id>@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id>(SELECT min(userId) FROM Comment)
+ 逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于 "key&{}":"条件"等 ② \| 可用于 "key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同"id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
+ 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中{}内的关键词,Object的类型由key指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
+ 对象关键词,可自定义 | "@key":Object,@key 为 Table:{} 中 {} 内的关键词,Object 的类型由 @key 指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果 @column 里声明了 Table 的 id,则 id 也必须在 @group 中声明;其它情况下必须满足至少一个条件: 1.分组的 key 在 @column 里声明 2.Table 主键在 @group 中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL 函数条件,一般和 @group 一起用,函数一般在 @column 里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防 SQL 注入 ⑬ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name` ③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id` ④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id` ⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应 SQL 是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应 SQL 是`EXPLAIN` ⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 从pictureList 获取第 0 张图片: ["@position":0, // 自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
+ 全局关键词 | 为最外层对象 {} 内的关键词。其中 @database,@schema, @datasource, @role, @explain 基本同对象关键词,见上方说明,区别是全局关键词会每个表对象中没有时自动放入,作为默认值。 ① "tag":"Table",后面的 tag 是非 GET、HEAD 请求中匹配请求的 JSON 结构的标识,一般是要查询的 Table 的名称或该名称对应的数组 Table[] 或 Table:[],由后端 Request 表中指定。 ② "version":1,接口版本,version 不传、为 null 或 <=0 都会使用最高版本,传了其它有效值则会使用最接近它的最低版本,由后端 Request 表中指定。 ③ "format":true,格式化返回 Response JSON 的 key,一般是将 TableName 转为 tableName, TableName[] 转为 tableNameList, Table:alias 转为 alias, TableName-key[] 转为 tableNameKeyList 等小驼峰格式。 | ① 查隐私信息: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
From 9e1187cfae3d6d44f41e4f5d1b9522568427a8c2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jun 2024 19:16:37 +0800
Subject: [PATCH 058/184] Update Document.md
---
Document.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Document.md b/Document.md
index 90148bedc..42c70036d 100644
--- a/Document.md
+++ b/Document.md
@@ -406,7 +406,7 @@ DELETE: 删除数据 | base_url/delete/ | { TableName:{<
查询数组 | "key[]":{},后面是 JSONObject,key 可省略。当 key 和里面的 Table 名相同时,Table 会被提取出来,即 {Table:{Content}} 会被转化为 {Content} | [{"User[]":{"User":{}}}](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{}}}),查询一个 User 数组。这里 key 和 Table 名都是 User,User 会被提取出来,即 {"User":{"id", ...}} 会被转化为 {"id", ...},如果要进一步提取 User 中的 id,可以把 User[] 改为 User-id[],其中 - 用来分隔路径中涉及的 key
匹配选项范围 | "key{}":[],后面是 JSONArray,作为 key 可取的值的选项 | ["id{}":[38710,82001,70793]](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":[38710,82001,70793]}}}),对应 SQL 是`id IN(38710,82001,70793)`,查询 id 符合 38710,82001,70793 中任意一个的一个 User 数组
匹配条件范围 | "key{}":"条件0,条件1...",条件为 SQL 表达式字符串,可进行数字比较运算等 | ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}),对应 SQL 是`id<=80000 OR id>90000`,查询 id 符合 id\<=80000 \| id>90000 的一个 User 数组
- 包含选项范围 | "key<\>":Object => "key<\>":[value],key 对应值的类型必须为 JSONArray,value 类型不能为 JSON | ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}),对应SQL是`json_contains(contactIdList,38710)`,查询 contactIdList 包含 38710 的一个 User 数组
+ 包含选项范围 | "key<\>":value => "key<\>":[value],key 对应值的类型必须为 JSONArray,value 值类型只能为 Boolean, Number, String 中的一种 | ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}),对应SQL是`json_contains(contactIdList,38710)`,查询 contactIdList 包含 38710 的一个 User 数组
判断是否存在 | "key}{@":{ "from":"Table", "Table":{ ... } } 其中: }{ 表示 EXISTS; key 用来标识是哪个判断; @ 后面是 子查询 对象,具体见下方 子查询 的说明。 | ["id}{@":{ "from":"Comment", "Comment":{ "momentId":15 } }](http://apijson.cn:8080/get/{"User":{"id}{@":{"from":"Comment","Comment":{"momentId":15}}}}) WHERE EXISTS(SELECT * FROM Comment WHERE momentId=15)
远程调用函数 | "key()":"函数表达式",函数表达式为 function(key0,key1...),会调用后端对应的函数 function(JSONObject request, String key0, String key1...),实现 参数校验、数值计算、数据同步、消息推送、字段拼接、结构变换 等特定的业务逻辑处理, 可使用 - 和 + 表示优先级,解析 key-() > 解析当前对象 > 解析 key() > 解析子对象 > 解析 key+() | ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}),会调用远程函数 [boolean isContain(JSONObject request, String array, String value)](https://github.com/APIJSON/apijson-framework/blob/master/src/main/java/apijson/framework/APIJSONFunctionParser.java#L361-L374) ,然后变为 "isPraised":true 这种(假设点赞用户 id 列表包含了 userId,即这个 User 点了赞)
存储过程 | "@key()":"SQL函数表达式",函数表达式为 function(key0,key1...) 会调用后端数据库对应的存储过程 SQL 函数 function(String key0, String key1...) 除了参数会提前赋值,其它和 远程函数 一致 | ["@limit":10, "@offset":0, "@procedure()":"getCommentByUserId(id,@limit,@offset)"](http://apijson.cn:8080/get/{"User":{"@limit":10,"@offset":0,"@procedure()":"getCommentByUserId(id,@limit,@offset)"}}) 会转为 `getCommentByUserId(38710,10,0)` 来调用存储过程 SQL 函数 `getCommentByUserId(IN id bigint, IN limit int, IN offset int)` 然后变为 "procedure":{ "count":-1, "update":false, "list":[] } 其中 count 是指写操作影响记录行数,-1 表示不是写操作;update 是指是否为写操作(增删改);list 为返回结果集
From e267b68241061c89720e05c59a1e33f84bf0887d Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jun 2024 19:28:55 +0800
Subject: [PATCH 059/184] Update Document.md
---
Document.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Document.md b/Document.md
index 42c70036d..723550066 100644
--- a/Document.md
+++ b/Document.md
@@ -419,8 +419,8 @@ DELETE: 删除数据 | base_url/delete/ | { TableName:{<
增加 或 扩展 | "key+":Object,Object的类型由key指定,且类型为 Number,String,JSONArray 中的一种。如 82001,"apijson",["url0","url1"] 等。只用于 PUT 请求 | "praiseUserIdList+":[82001],对应 SQL 是`json_insert(praiseUserIdList,82001)`,添加一个点赞用户 id,即这个用户点了赞
减少 或 去除 | "key-":Object,与"key+"相反 | "balance-":100.00,对应SQL是`balance = balance - 100.00`,余额减少100.00,即花费了100元
比较运算 | >, <, >=, <= 比较运算符,用于 ① 提供 "id{}":"<=90000" 这种条件范围的简化写法 ② 实现子查询相关比较运算 不支持 "key=":Object 和 "key!=":Object 这两种写法,直接用更简单的 "key":Object 和 "key!":Object 替代。 | ① ["id<=":90000](http://apijson.cn:8080/get/{"[]":{"User":{"id<=":90000}}}),对应 SQL 是`id<=90000`,查询符合id<=90000的一个User数组 ② ["id>@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id>@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id>(SELECT min(userId) FROM Comment)
- 逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于 "key&{}":"条件"等 ② \| 可用于 "key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同"id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
- 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中{}内的关键词,Object的类型由key指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
+ 逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于 "key&{}":"条件"等 ② \| 可用于 "key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同 "id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
+ 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
对象关键词,可自定义 | "@key":Object,@key 为 Table:{} 中 {} 内的关键词,Object 的类型由 @key 指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果 @column 里声明了 Table 的 id,则 id 也必须在 @group 中声明;其它情况下必须满足至少一个条件: 1.分组的 key 在 @column 里声明 2.Table 主键在 @group 中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL 函数条件,一般和 @group 一起用,函数一般在 @column 里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防 SQL 注入 ⑬ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name` ③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id` ④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id` ⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应 SQL 是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应 SQL 是`EXPLAIN` ⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 从pictureList 获取第 0 张图片: ["@position":0, // 自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
全局关键词 | 为最外层对象 {} 内的关键词。其中 @database,@schema, @datasource, @role, @explain 基本同对象关键词,见上方说明,区别是全局关键词会每个表对象中没有时自动放入,作为默认值。 ① "tag":"Table",后面的 tag 是非 GET、HEAD 请求中匹配请求的 JSON 结构的标识,一般是要查询的 Table 的名称或该名称对应的数组 Table[] 或 Table:[],由后端 Request 表中指定。 ② "version":1,接口版本,version 不传、为 null 或 <=0 都会使用最高版本,传了其它有效值则会使用最接近它的最低版本,由后端 Request 表中指定。 ③ "format":true,格式化返回 Response JSON 的 key,一般是将 TableName 转为 tableName, TableName[] 转为 tableNameList, Table:alias 转为 alias, TableName-key[] 转为 tableNameKeyList 等小驼峰格式。 | ① 查隐私信息: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
From 02865ec955fef8838d1b8dee5cdc95021d010639 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 30 Jun 2024 23:13:53 +0800
Subject: [PATCH 060/184] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=207.0.3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 45f4522ca..d466682ca 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.0.0
+ 7.0.3
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 3bdad0525..662dc912c 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.0.0";
+ public static final String VERSION = "7.0.3";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From 225abd5b67666e9eb93242904a08b126c56a9654 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Jul 2024 23:57:55 +0800
Subject: [PATCH 061/184] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20lanmuc-=E5=90=8E=E7=AB=AF=E4=BD=8E?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E4=BA=A7=E6=8E=A5=E5=8F=A3=E7=9A=84?=
=?UTF-8?q?=E5=B9=B3=E5=8F=B0=EF=BC=8C=E5=85=BC=E5=AE=B9=E9=85=8D=E7=BD=AE?=
=?UTF-8?q?=E5=BC=8F=E6=8E=A5=E5=8F=A3=E5=92=8C=E7=BC=96=E5=86=99=E5=BC=8F?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=8F=AF=E5=81=9A=E5=88=B0=E5=BF=AB?=
=?UTF-8?q?=E9=80=9F=E7=94=9F=E4=BA=A7=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=B8=8A?=
=?UTF-8?q?=E7=BA=BF=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
感谢作者的热心贡献,右上角点亮 ⭐️Star 支持下 TA 吧~
https://gitee.com/element-admin/lanmuc
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 8d6afdad7..bb9c01ed1 100644
--- a/README.md
+++ b/README.md
@@ -712,6 +712,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) 一个快速构建 APIJSON 查询条件的插件
[apijson-builder](https://github.com/yeli19950109/apijson-builder) 简单包装 APIJSON,相比直接构造查询 JSON 更好记,ts 编写,调整了一些参数和使用方式
+
+[lanmuc](https://gitee.com/element-admin/lanmuc) 后端低代码生产接口的平台,兼容配置式接口和编写式接口,可做到快速生产接口,上线项目
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
From e6cf73e202df6d907162d12fc55a66d6e43bf8a2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 28 Jul 2024 23:30:12 +0800
Subject: [PATCH 062/184] Update jitpack.yml
---
jitpack.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/jitpack.yml b/jitpack.yml
index 9e42c425a..f1e4ecdd1 100644
--- a/jitpack.yml
+++ b/jitpack.yml
@@ -1,6 +1,6 @@
jdk:
- - openjdk17
+ - openjdk8
before_install:
- - sdk install java 17.0.6-open
- - sdk use java 17.0.6-open
+ - sdk install java 1.8.0-open
+ - sdk use java 1.8.0-open
From f787f8e6038dedd9e55a238fbcb7bc7757871f11 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 28 Jul 2024 23:31:30 +0800
Subject: [PATCH 063/184] Update jitpack.yml
---
jitpack.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/jitpack.yml b/jitpack.yml
index f1e4ecdd1..9e42c425a 100644
--- a/jitpack.yml
+++ b/jitpack.yml
@@ -1,6 +1,6 @@
jdk:
- - openjdk8
+ - openjdk17
before_install:
- - sdk install java 1.8.0-open
- - sdk use java 1.8.0-open
+ - sdk install java 17.0.6-open
+ - sdk use java 17.0.6-open
From e16718fc7aabfefeeb22e03a9bd9613aadff3301 Mon Sep 17 00:00:00 2001
From: zxcwindy
Date: Tue, 30 Jul 2024 15:56:51 +0800
Subject: [PATCH 064/184] =?UTF-8?q?=E5=8C=97=E6=98=8E=E8=BD=AF=E4=BB=B6?=
=?UTF-8?q?=E7=99=BB=E8=AE=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index bb9c01ed1..277743c5d 100644
--- a/README.md
+++ b/README.md
@@ -355,6 +355,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [上海麦市信息科技有限公司](https://www.masscms.com)
* [上海翊丞互联网科技有限公司](http://www.renrencjl.com/home)
* [上海直真君智科技有限公司](http://www.zzjunzhi.com)
+ * [北明软件有限公司](https://www.bmsoft.com.cn/)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、1 个美国加州大学学生、3 个 SUSTech 学生等):
From 746f291a619c4a792907c0dfa4a74eea5dae34f9 Mon Sep 17 00:00:00 2001
From: LY
Date: Wed, 7 Aug 2024 15:05:22 +0800
Subject: [PATCH 065/184] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E8=87=AA?=
=?UTF-8?q?=E5=85=B3=E8=81=94=E6=9F=A5=E8=AF=A2=E6=97=B6=EF=BC=8C=E5=AF=B9?=
=?UTF-8?q?Table:alias=E7=9A=84=E5=86=99=E6=B3=95sql=E8=A1=A8=E5=90=8D?=
=?UTF-8?q?=E6=8B=BC=E5=86=99=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 106 +++++++++---------
1 file changed, 56 insertions(+), 50 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 6b714e587..6131e3be3 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -188,7 +188,7 @@ public abstract class AbstractSQLConfig implements SQLConfig();
+ ALLOW_PARTIAL_UPDATE_FAIL_TABLE_MAP = new HashMap<>();
CONFIG_TABLE_LIST = new ArrayList<>(); // Table, Column 等是系统表 AbstractVerifier.SYSTEM_ACCESS_MAP.keySet());
CONFIG_TABLE_LIST.add(Function.class.getSimpleName());
@@ -492,8 +492,8 @@ public abstract class AbstractSQLConfig implements SQLConfig raw = getRaw();
- // 提前把 @having& 转为 @having,或者干脆不允许 @raw:"@having&" boolean containRaw = raw != null && (raw.contains(KEY_HAVING) || raw.contains(KEY_HAVING_AND));
+ // 提前把 @having& 转为 @having,或者干脆不允许 @raw:"@having&" boolean containRaw = raw != null && (raw.contains(KEY_HAVING) || raw.contains(KEY_HAVING_AND));
boolean containRaw = raw != null && raw.contains(KEY_HAVING);
// 直接把 having 类型从 Map 定改为 Map,避免额外拷贝
@@ -2657,12 +2663,12 @@ public String getLimitString() {
}
return getLimitString(
- getPage()
- , getCount()
- , isOracle() || isSQLServer() || isDb2()
- , isOracle() || isDameng() || isKingBase()
- , isPresto() || isTrino()
- );
+ getPage()
+ , getCount()
+ , isOracle() || isSQLServer() || isDb2()
+ , isOracle() || isDameng() || isKingBase()
+ , isPresto() || isTrino()
+ );
}
/**获取限制数量及偏移量
* @param page
@@ -2672,7 +2678,7 @@ public String getLimitString() {
* @return
*/
public static String getLimitString(int page, int count, boolean isTSQL, boolean isOracle) {
- return getLimitString(page, count, isTSQL, isOracle, false);
+ return getLimitString(page, count, isTSQL, isOracle, false);
}
/**获取限制数量及偏移量
* @param page
@@ -2685,17 +2691,17 @@ public static String getLimitString(int page, int count, boolean isTSQL, boolean
public static String getLimitString(int page, int count, boolean isTSQL, boolean isOracle, boolean isPresto) {
int offset = getOffset(page, count);
- if (isOracle) { // TODO 判断版本,高版本可以用 OFFSET FETCH
- return " WHERE ROWNUM BETWEEN " + offset + " AND " + (offset + count);
- }
+ if (isOracle) { // TODO 判断版本,高版本可以用 OFFSET FETCH
+ return " WHERE ROWNUM BETWEEN " + offset + " AND " + (offset + count);
+ }
if (isTSQL) { // OFFSET FECTH 中所有关键词都不可省略, 另外 Oracle 数据库使用子查询加 where 分页
return " OFFSET " + offset + " ROWS FETCH FIRST " + count + " ROWS ONLY";
}
- if (isPresto) { // https://prestodb.io/docs/current/sql/select.html
- return (offset <= 0 ? "" : " OFFSET " + offset) + " LIMIT " + count;
- }
+ if (isPresto) { // https://prestodb.io/docs/current/sql/select.html
+ return (offset <= 0 ? "" : " OFFSET " + offset) + " LIMIT " + count;
+ }
return " LIMIT " + count + (offset <= 0 ? "" : " OFFSET " + offset); // DELETE, UPDATE 不支持 OFFSET
}
@@ -3868,17 +3874,17 @@ public String getRegExpString(String key, String column, String value, boolean i
if (isOracle() || isDameng() || isKingBase() || (isMySQL() && getDBVersionNums()[0] >= 8)) {
return "regexp_like(" + getKey(column) + ", " + getValue(key, column, value) + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
- if (isPresto() || isTrino()) {
- return "regexp_like(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
- }
+ if (isPresto() || isTrino()) {
+ return "regexp_like(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ + ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
+ }
if (isClickHouse()) {
return "match(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
}
- if (isElasticsearch()) {
- return getKey(column) + " RLIKE " + getValue(key, column, value);
- }
+ if (isElasticsearch()) {
+ return getKey(column) + " RLIKE " + getValue(key, column, value);
+ }
if (isHive()) {
return (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ " REGEXP " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "");
@@ -4135,7 +4141,7 @@ public String getExistsString(String key, String column, Object value, String ra
/**WHERE key contains value
* @param key
* @param value
- * @return {@link #getContainString(String, String, Object[], int)}
+ * @return {@link #getContainString(String, String, Object[], int)}
* @throws NotExistException
*/
@JSONField(serialize = false)
@@ -4196,11 +4202,11 @@ else if (isOracle() || isDameng() || isKingBase()) {
condition += ("json_textcontains(" + getKey(column) + ", " + (StringUtil.isEmpty(path, true)
? "'$'" : getValue(key, column, path)) + ", " + getValue(key, column, c == null ? null : c.toString()) + ")");
}
- else if (isPresto() || isTrino()) {
- condition += ("json_array_contains(cast(" + getKey(column) + " AS VARCHAR), "
+ else if (isPresto() || isTrino()) {
+ condition += ("json_array_contains(cast(" + getKey(column) + " AS VARCHAR), "
+ getValue(key, column, c) + (StringUtil.isEmpty(path, true)
? "" : ", " + getValue(key, column, path)) + ")");
- }
+ }
else {
String v = c == null ? "null" : (c instanceof Boolean || c instanceof Number ? c.toString() : "\"" + c + "\"");
if (isClickHouse()) {
@@ -4613,7 +4619,7 @@ protected String getOraclePageSql(String sql) {
}
int offset = getOffset(getPage(), count);
String alias = getAliasWithQuote();
- String quote = getQuote();
+ String quote = getQuote();
return "SELECT * FROM (SELECT " + alias + ".*, ROWNUM "+ quote + "RN" + quote +" FROM (" + sql + ") " + alias
+ " WHERE ROWNUM <= " + (offset + count) + ") WHERE "+ quote + "RN" + quote +" > " + offset;
}
@@ -4816,7 +4822,7 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
String rt = on.getRelateType();
if (StringUtil.isEmpty(rt, false)) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else {
onJoinComplexRelation(sql, quote, j, jt, onList, on);
@@ -4828,7 +4834,7 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
}
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " " + rt + " "
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else if (rt.endsWith("$")) {
String t = rt.substring(0, rt.length() - 1);
@@ -4874,11 +4880,11 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
if (l <= 0 && r <= 0) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " LIKE " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ + " LIKE " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + quote + on.getTargetTable() + quote
+ + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote
+ "." + quote + on.getTargetKey() + quote + (r <= 0 ? ")" : ", '" + r + "')");
}
}
@@ -4887,38 +4893,38 @@ else if (rt.endsWith("~")) {
if (isPostgreSQL() || isInfluxDB()) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote
+ (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ")
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else if (isOracle() || isDameng() || isKingBase()) {
sql += (first ? ON : AND) + "regexp_like(" + quote + jt + quote + "." + quote + on.getKey() + quote
- + ", " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote
+ + ", " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote
+ (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
- else if (isPresto() || isTrino()) {
- sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + quote
+ else if (isPresto() || isTrino()) {
+ sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + quote
+ jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + on.getTargetTable()
+ + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
- }
+ }
else if (isClickHouse()) {
sql += (first ? ON : AND) + "match(" + (ignoreCase ? "lower(" : "") + quote + jt
+ quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + on.getTargetTable()
+ + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
}
else if (isElasticsearch()) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " RLIKE " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
- }
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ + " RLIKE " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ }
else if (isHive()) {
- sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + on.getTargetTable()
+ sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
+ + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "");
- }
+ }
else {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ " REGEXP " + (ignoreCase ? "" : "BINARY ")
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
}
else if ("{}".equals(rt) || "<>".equals(rt)) {
@@ -4950,12 +4956,12 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String arrKeyPath;
String itemKeyPath;
if ("{}".equals(rt)) {
- arrKeyPath = quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ arrKeyPath = quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
itemKeyPath = quote + jt + quote + "." + quote + on.getKey() + quote;
}
else {
arrKeyPath = quote + jt + quote + "." + quote + on.getKey() + quote;
- itemKeyPath = quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ itemKeyPath = quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
if (isPostgreSQL() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
From 6bfb21840d8b9152dc1131d1cd8ba7c0a9137de6 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 11 Aug 2024 17:08:25 +0800
Subject: [PATCH 066/184] =?UTF-8?q?getSQLTableWithAlias=20=E5=B8=A6?=
=?UTF-8?q?=E4=B8=8A=E5=8E=9F=E8=A1=A8=E5=90=8D=EF=BC=8C=E9=81=BF=E5=85=8D?=
=?UTF-8?q?=20alias=20=E4=B8=8E=E5=85=B6=E5=AE=83=E8=A1=A8=E5=90=8D/?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5=20=E5=86=B2=E7=AA=81=EF=BC=9B=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=20GitHub=20=E5=AF=B9=20tab=20=E5=92=8C=20IDE=20?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E4=B8=8D=E4=B8=80=E8=87=B4=E5=AF=BC=E8=87=B4?=
=?UTF-8?q?=E7=BC=A9=E8=BF=9B=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 104 +++++++++---------
1 file changed, 52 insertions(+), 52 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 6131e3be3..5014f0eb6 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -143,9 +143,9 @@ public abstract class AbstractSQLConfig implements SQLConfig COLUMN_KEY_MAP;
- /**
- * 允许批量增删改部分记录失败的表
- */
+ /**
+ * 允许批量增删改部分记录失败的表
+ */
public static Map ALLOW_PARTIAL_UPDATE_FAIL_TABLE_MAP;
public static List CONFIG_TABLE_LIST;
public static List DATABASE_LIST;
@@ -188,7 +188,7 @@ public abstract class AbstractSQLConfig implements SQLConfig();
+ ALLOW_PARTIAL_UPDATE_FAIL_TABLE_MAP = new HashMap<>();
CONFIG_TABLE_LIST = new ArrayList<>(); // Table, Column 等是系统表 AbstractVerifier.SYSTEM_ACCESS_MAP.keySet());
CONFIG_TABLE_LIST.add(Function.class.getSimpleName());
@@ -492,8 +492,8 @@ public abstract class AbstractSQLConfig implements SQLConfig= 8)) {
return "regexp_like(" + getKey(column) + ", " + getValue(key, column, value) + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
- if (isPresto() || isTrino()) {
- return "regexp_like(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ if (isPresto() || isTrino()) {
+ return "regexp_like(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
- }
+ }
if (isClickHouse()) {
return "match(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
}
- if (isElasticsearch()) {
- return getKey(column) + " RLIKE " + getValue(key, column, value);
- }
+ if (isElasticsearch()) {
+ return getKey(column) + " RLIKE " + getValue(key, column, value);
+ }
if (isHive()) {
return (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ " REGEXP " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "");
@@ -4202,11 +4202,11 @@ else if (isOracle() || isDameng() || isKingBase()) {
condition += ("json_textcontains(" + getKey(column) + ", " + (StringUtil.isEmpty(path, true)
? "'$'" : getValue(key, column, path)) + ", " + getValue(key, column, c == null ? null : c.toString()) + ")");
}
- else if (isPresto() || isTrino()) {
- condition += ("json_array_contains(cast(" + getKey(column) + " AS VARCHAR), "
+ else if (isPresto() || isTrino()) {
+ condition += ("json_array_contains(cast(" + getKey(column) + " AS VARCHAR), "
+ getValue(key, column, c) + (StringUtil.isEmpty(path, true)
? "" : ", " + getValue(key, column, path)) + ")");
- }
+ }
else {
String v = c == null ? "null" : (c instanceof Boolean || c instanceof Number ? c.toString() : "\"" + c + "\"");
if (isClickHouse()) {
@@ -4619,7 +4619,7 @@ protected String getOraclePageSql(String sql) {
}
int offset = getOffset(getPage(), count);
String alias = getAliasWithQuote();
- String quote = getQuote();
+ String quote = getQuote();
return "SELECT * FROM (SELECT " + alias + ".*, ROWNUM "+ quote + "RN" + quote +" FROM (" + sql + ") " + alias
+ " WHERE ROWNUM <= " + (offset + count) + ") WHERE "+ quote + "RN" + quote +" > " + offset;
}
@@ -4796,10 +4796,10 @@ public String getJoinString() throws Exception {
//if (changed) {
- // List opvl = getPreparedValueList();
- // if (opvl != null && opvl.isEmpty() == false) {
- // pvl.addAll(opvl);
- // }
+ // List opvl = getPreparedValueList();
+ // if (opvl != null && opvl.isEmpty() == false) {
+ // pvl.addAll(opvl);
+ // }
setPreparedValueList(pvl);
//}
@@ -4822,7 +4822,7 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
String rt = on.getRelateType();
if (StringUtil.isEmpty(rt, false)) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
- + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else {
onJoinComplexRelation(sql, quote, j, jt, onList, on);
@@ -4834,7 +4834,7 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
}
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " " + rt + " "
- + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else if (rt.endsWith("$")) {
String t = rt.substring(0, rt.length() - 1);
@@ -4880,11 +4880,11 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
if (l <= 0 && r <= 0) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " LIKE " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ + " LIKE " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote
+ + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote
+ "." + quote + on.getTargetKey() + quote + (r <= 0 ? ")" : ", '" + r + "')");
}
}
@@ -4893,38 +4893,38 @@ else if (rt.endsWith("~")) {
if (isPostgreSQL() || isInfluxDB()) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote
+ (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ")
- + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
else if (isOracle() || isDameng() || isKingBase()) {
sql += (first ? ON : AND) + "regexp_like(" + quote + jt + quote + "." + quote + on.getKey() + quote
- + ", " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote
+ + ", " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote
+ (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
- else if (isPresto() || isTrino()) {
- sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + quote
+ else if (isPresto() || isTrino()) {
+ sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + quote
+ jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
- }
+ }
else if (isClickHouse()) {
sql += (first ? ON : AND) + "match(" + (ignoreCase ? "lower(" : "") + quote + jt
+ quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
}
else if (isElasticsearch()) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " RLIKE " + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
- }
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ + " RLIKE " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ }
else if (isHive()) {
- sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias())
+ sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
+ + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
+ quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "");
- }
+ }
else {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ " REGEXP " + (ignoreCase ? "" : "BINARY ")
- + quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
}
}
else if ("{}".equals(rt) || "<>".equals(rt)) {
From 8dbdc4f6e2a7470c1b2bb523addf1d37f657b0fa Mon Sep 17 00:00:00 2001
From: afumu
Date: Thu, 29 Aug 2024 16:11:09 +0800
Subject: [PATCH 067/184] =?UTF-8?q?=E5=A2=9E=E5=8A=A0SQLite=E6=94=AF?=
=?UTF-8?q?=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 8 ++++++++
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
2 files changed, 10 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 5014f0eb6..bcb1af598 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1301,6 +1301,14 @@ public static boolean isMQ(String db) {
return DATABASE_MQ.equals(db) || isKafka(db);
}
+ @Override
+ public boolean isSQLite() {
+ return isSQLite(getSQLDatabase());
+ }
+ public static boolean isSQLite(String db) {
+ return DATABASE_SQLITE.equals(db);
+ }
+
@Override
public String getQuote() {
if(isElasticsearch()) {
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 7372630c5..7ed6cf663 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -41,6 +41,7 @@ public interface SQLConfig {
String DATABASE_MONGODB = "MONGODB"; // https://www.mongodb.com/docs/atlas/data-federation/query/query-with-sql
String DATABASE_KAFKA = "KAFKA"; // https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Kafka
String DATABASE_MQ = "MQ"; //
+ String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
String SCHEMA_INFORMATION = "information_schema"; //MySQL, PostgreSQL, SQL Server 都有的系统模式
String SCHEMA_SYS = "sys"; //SQL Server 系统模式
@@ -91,6 +92,7 @@ public interface SQLConfig {
boolean isMongoDB();
boolean isKafka();
boolean isMQ();
+ boolean isSQLite();
// 暂时只兼容以上几种
From 44e596ff2718412235967b54b11c51369c3ecf0f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 8 Sep 2024 23:07:44 +0800
Subject: [PATCH 068/184] =?UTF-8?q?17K+=20Star=20=E5=9C=A8=20400W=C2=A0Jav?=
=?UTF-8?q?a=20=E9=A1=B9=E7=9B=AE=E6=8E=92=E5=90=8D=E5=89=8D=20100?=
=?UTF-8?q?=EF=BC=8C=E8=BF=9C=E8=B6=85=20FLAG,=20BAT=20=E7=AD=89=E5=9B=BD?=
=?UTF-8?q?=E5=86=85=E5=A4=96=E7=BB=9D=E5=A4=A7=E9=83=A8=E5=88=86=E5=BC=80?=
=?UTF-8?q?=E6=BA=90=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/edit/master/README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 277743c5d..403be4cf7 100644
--- a/README.md
+++ b/README.md
@@ -179,7 +179,7 @@ https://github.com/Tencent/APIJSON/wiki
* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽)
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 16K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前七、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
From 886344adb6c1cfae650402ec06cdd4b0433c275a Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Thu, 19 Sep 2024 00:52:43 +0800
Subject: [PATCH 069/184] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20UnitAuto=20?=
=?UTF-8?q?=E7=AE=80=E4=BB=8B:=20=E6=9C=80=E5=85=88=E8=BF=9B=E3=80=81?=
=?UTF-8?q?=E6=9C=80=E7=9C=81=E4=BA=8B=E3=80=81ROI=20=E6=9C=80=E9=AB=98?=
=?UTF-8?q?=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8C=E6=9C=BA?=
=?UTF-8?q?=E5=99=A8=E5=AD=A6=E4=B9=A0=20=E9=9B=B6=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E3=80=81=E5=85=A8=E6=96=B9=E4=BD=8D=E3=80=81=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E5=8C=96=20=E6=B5=8B=E8=AF=95=20=E6=96=B9=E6=B3=95/=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=EF=BC=8C=E7=94=A8=E6=88=B7=E5=8C=85=E5=90=AB=E8=85=BE?=
=?UTF-8?q?=E8=AE=AF=E3=80=81=E5=BF=AB=E6=89=8B=E3=80=81=E6=9F=90=20500=20?=
=?UTF-8?q?=E5=BC=BA=E5=B7=A8=E5=A4=B4=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
支持 Java, Go, C++, Python, Kotlin, Android 等。
创作不易、坚持更难,右上角点亮 ⭐ Star 支持下本项目吧,谢谢 ^_^
https://github.com/TommyLemon/UnitAuto
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 403be4cf7..b2aa8221c 100644
--- a/README.md
+++ b/README.md
@@ -630,7 +630,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[APIAuto](https://github.com/TommyLemon/APIAuto) 敏捷开发最强大易用的接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释
-[UnitAuto](https://github.com/TommyLemon/UnitAuto) 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能
+[UnitAuto](https://github.com/TommyLemon/UnitAuto) 最先进、最省事、ROI 最高的单元测试,机器学习 零代码、全方位、自动化 测试 方法/函数,用户包含腾讯、快手、某 500 强巨头等
[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,一键批量生成参数组合、快速构造大量测试数据
From b749e538a109495ccaa60663c6b9b48c54c540f5 Mon Sep 17 00:00:00 2001
From: alittle-yu <131329000+alittle-yu@users.noreply.github.com>
Date: Thu, 26 Sep 2024 09:30:44 +0800
Subject: [PATCH 070/184] =?UTF-8?q?fix=EF=BC=9A=E8=B0=83=E6=95=B4parseResp?=
=?UTF-8?q?onse=E8=8E=B7=E5=8F=96SQLExecutor=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractParser.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 44b196615..4da6aa249 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -520,7 +520,7 @@ public JSONObject parseResponse(JSONObject request) {
queryResultMap = new HashMap();
Exception error = null;
- sqlExecutor = createSQLExecutor();
+ sqlExecutor = getSQLExecutor();
onBegin();
try {
queryDepth = 0;
From 016f31a63eee52ac7350482a25e39eb2a7e47c54 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 28 Sep 2024 11:25:52 +0800
Subject: [PATCH 071/184] =?UTF-8?q?=E9=80=9A=E7=94=A8=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=EF=BC=9A=E5=8A=9F=E8=83=BD=E7=AC=A6=E6=96=B0=E5=A2=9E=E7=A9=BA?=
=?UTF-8?q?=E5=80=BC=E9=94=AE=E5=80=BC=E5=AF=B9=20"@null":"key1,key2..."?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/blob/master/Document.md#32-%E5%8A%9F%E8%83%BD%E7%AC%A6
---
Document.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Document.md b/Document.md
index 723550066..e20bfbb27 100644
--- a/Document.md
+++ b/Document.md
@@ -421,6 +421,6 @@ DELETE: 删除数据 | base_url/delete/ | { TableName:{<
比较运算 | >, <, >=, <= 比较运算符,用于 ① 提供 "id{}":"<=90000" 这种条件范围的简化写法 ② 实现子查询相关比较运算 不支持 "key=":Object 和 "key!=":Object 这两种写法,直接用更简单的 "key":Object 和 "key!":Object 替代。 | ① ["id<=":90000](http://apijson.cn:8080/get/{"[]":{"User":{"id<=":90000}}}),对应 SQL 是`id<=90000`,查询符合id<=90000的一个User数组 ② ["id>@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id>@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id>(SELECT min(userId) FROM Comment)
逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于 "key&{}":"条件"等 ② \| 可用于 "key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同 "id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
- 对象关键词,可自定义 | "@key":Object,@key 为 Table:{} 中 {} 内的关键词,Object 的类型由 @key 指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果 @column 里声明了 Table 的 id,则 id 也必须在 @group 中声明;其它情况下必须满足至少一个条件: 1.分组的 key 在 @column 里声明 2.Table 主键在 @group 中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL 函数条件,一般和 @group 一起用,函数一般在 @column 里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防 SQL 注入 ⑬ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name` ③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id` ④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id` ⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应 SQL 是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应 SQL 是`EXPLAIN` ⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 从pictureList 获取第 0 张图片: ["@position":0, // 自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
+ 对象关键词,可自定义 | "@key":Object,@key 为 Table:{} 中 {} 内的关键词,Object 的类型由 @key 指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果 @column 里声明了 Table 的 id,则 id 也必须在 @group 中声明;其它情况下必须满足至少一个条件: 1.分组的 key 在 @column 里声明 2.Table 主键在 @group 中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL 函数条件,一般和 @group 一起用,函数一般在 @column 里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防 SQL 注入 ⑬ "@null":"key1,key2...",空值键值对,自动插入 key1:null, key2:null ... 并作为有效键值对执行,作为条件时对应 SQL 是 `WHERE tag IS NULL`,作为值时对应 SQL 是 `SET tag = NULL` ⑭ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name` ③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id` ④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id` ⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应 SQL 是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应 SQL 是`EXPLAIN` ⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 把用户的标签设置为空 ["@null":"tag"](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/put/User&json={%22id%22:82001,%22@null%22:%22tag%22,%22@explain%22:true}) ⑭ 从pictureList 获取第 0 张图片: ["@position":0, // 自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
全局关键词 | 为最外层对象 {} 内的关键词。其中 @database,@schema, @datasource, @role, @explain 基本同对象关键词,见上方说明,区别是全局关键词会每个表对象中没有时自动放入,作为默认值。 ① "tag":"Table",后面的 tag 是非 GET、HEAD 请求中匹配请求的 JSON 结构的标识,一般是要查询的 Table 的名称或该名称对应的数组 Table[] 或 Table:[],由后端 Request 表中指定。 ② "version":1,接口版本,version 不传、为 null 或 <=0 都会使用最高版本,传了其它有效值则会使用最接近它的最低版本,由后端 Request 表中指定。 ③ "format":true,格式化返回 Response JSON 的 key,一般是将 TableName 转为 tableName, TableName[] 转为 tableNameList, Table:alias 转为 alias, TableName-key[] 转为 tableNameKeyList 等小驼峰格式。 | ① 查隐私信息: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
From 041735362050f0e1764ef2793a7a24f3946b4744 Mon Sep 17 00:00:00 2001
From: Damon Nicola <69659973+Reynold3D@users.noreply.github.com>
Date: Sun, 29 Sep 2024 17:22:43 +0800
Subject: [PATCH 072/184] =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=99=BB=E8=AE=B0?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E4=B8=8A=E6=B5=B7=E9=92=B0=E4=BA=BF?=
=?UTF-8?q?=E7=8E=AF=E4=BF=9D=E7=A7=91=E6=8A=80=E6=9C=89=E9=99=90=E5=85=AC?=
=?UTF-8?q?=E5=8F=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index b2aa8221c..4acdba485 100644
--- a/README.md
+++ b/README.md
@@ -356,6 +356,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [上海翊丞互联网科技有限公司](http://www.renrencjl.com/home)
* [上海直真君智科技有限公司](http://www.zzjunzhi.com)
* [北明软件有限公司](https://www.bmsoft.com.cn/)
+ * [上海钰亿环保科技有限公司](#)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、1 个美国加州大学学生、3 个 SUSTech 学生等):
From 6ea0b9b60bbe49e7793a06c22c9cd22ad96e3d1a Mon Sep 17 00:00:00 2001
From: calmcc
Date: Thu, 17 Oct 2024 16:09:21 +0800
Subject: [PATCH 073/184] =?UTF-8?q?1.=E5=AF=B9=E6=9F=90=E4=BA=9B=E9=9C=80?=
=?UTF-8?q?=E8=A6=81=E8=BE=93=E5=87=BAnull=E5=80=BC=E5=AD=97=E6=AE=B5?=
=?UTF-8?q?=E7=9A=84=E9=9C=80=E6=B1=82=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=BE=93?=
=?UTF-8?q?=E5=87=BAnull=E5=80=BC=E7=9A=84=E5=BC=80=E5=85=B3=20=E5=9C=A8?=
=?UTF-8?q?=E5=85=A5=E5=8F=A3=E5=87=BD=E6=95=B0=E5=A2=9E=E5=8A=A0=E8=BF=99?=
=?UTF-8?q?=E4=B8=A4=E5=8F=A5=20JSON.DEFAULT=5FGENERATE=5FFEATURE=20|=3D?=
=?UTF-8?q?=20SerializerFeature.WriteMapNullValue.getMask();=20AbstractSQL?=
=?UTF-8?q?Executor.ENABLE=5FOUTPUT=5FNULL=5FCOLUMN=20=3D=20true;?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/JSON.java | 3 +-
.../java/apijson/orm/AbstractSQLExecutor.java | 43 ++++++++-----------
2 files changed, 19 insertions(+), 27 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index 48b80aac4..d7854aae1 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -8,7 +8,6 @@
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.alibaba.fastjson.JSONReader;
import java.util.List;
@@ -203,7 +202,7 @@ public static String toJSONString(Object obj, SerializerFeature... features) {
try {
return com.alibaba.fastjson.JSON.toJSONString(obj, features);
} catch (Exception e) {
- Log.e(TAG, "parseArray catch \n" + e.getMessage());
+ Log.e(TAG, "toJSONString catch \n" + e.getMessage());
}
return null;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 426a53ee5..36ef71ca9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -5,45 +5,32 @@
package apijson.orm;
+import apijson.*;
+import apijson.orm.Join.On;
+import apijson.orm.exception.NotExistException;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+
import java.io.BufferedReader;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.util.*;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Savepoint;
-import java.sql.Statement;
-import java.sql.Timestamp;
+import java.sql.*;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Year;
+import java.util.Date;
+import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-
-import apijson.JSONResponse;
-import apijson.Log;
-import apijson.NotNull;
-import apijson.RequestMethod;
-import apijson.StringUtil;
-import apijson.orm.Join.On;
-import apijson.orm.exception.NotExistException;
-
/**executor for query(read) or update(write) MySQL database
* @author Lemon
*/
public abstract class AbstractSQLExecutor implements SQLExecutor {
private static final String TAG = "AbstractSQLExecutor";
-
+ //是否返回 值为null的字段
+ public static boolean ENABLE_OUTPUT_NULL_COLUMN = false;
public static String KEY_RAW_LIST = "@RAW@LIST"; // 避免和字段命名冲突,不用 $RAW@LIST$ 是因为 $ 会在 fastjson 内部转义,浪费性能
private Parser parser;
@@ -918,8 +905,14 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, label, childMap);
// 主表必须 put 至少一个 null 进去,否则全部字段为 null 都不 put 会导致中断后续正常返回值
- if (value != null || (join == null && table.isEmpty())) {
+ if (value != null) {
table.put(label, value);
+ } else{
+ if (join == null && table.isEmpty()) {
+ table.put(label, null);
+ } else if (ENABLE_OUTPUT_NULL_COLUMN) {
+ table.put(label, null);
+ }
}
return table;
From 90b896c9d59cb6883b2e4469d3806e1c3ed815b9 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 27 Oct 2024 21:50:51 +0800
Subject: [PATCH 074/184] =?UTF-8?q?=E5=A4=9A=E5=B9=B4=E6=8C=81=E7=BB=AD?=
=?UTF-8?q?=E8=BF=AD=E4=BB=A3:=20=E8=87=AA=202016=20=E5=B9=B4=E8=B5=B7?=
=?UTF-8?q?=E5=B7=B2=E8=BF=9E=E7=BB=AD=E7=BB=B4=E6=8A=A4=207=20=E5=B9=B4?=
=?UTF-8?q?=E5=A4=9A=EF=BC=8C70+=20=E8=B4=A1=E7=8C=AE=E8=80=85=E3=80=8190+?=
=?UTF-8?q?=20=E5=8F=91=E7=89=88=E3=80=813000+=20=E6=8F=90=E4=BA=A4?=
=?UTF-8?q?=EF=BC=8C=E4=B8=8D=E6=96=AD=E6=9B=B4=E6=96=B0=E8=BF=AD=E4=BB=A3?=
=?UTF-8?q?=E4=B8=AD...?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4acdba485..0f69856ab 100644
--- a/README.md
+++ b/README.md
@@ -191,7 +191,7 @@ https://github.com/Tencent/APIJSON/wiki
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年起已连续维护 7 年多,60+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年起已连续维护 7 年多,70+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From 64042e59bd80146c1b589b95242e234a06fd5efb Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 3 Nov 2024 20:25:18 +0800
Subject: [PATCH 075/184] =?UTF-8?q?=E5=AE=9E=E6=97=B6=20=E9=9B=B6=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E3=80=81=E5=85=A8=E5=8A=9F=E8=83=BD=E3=80=81=E5=BC=BA?=
=?UTF-8?q?=E5=AE=89=E5=85=A8=20ORM=20=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0f69856ab..c1d70c7fe 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ This source code is licensed under the Apache License Version 2.0
APIJSON
- 🏆 零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
+ 🏆 实时 零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
English
From 4f6066cffaf1d06a41a7b4f6a127f22ae56c80c0 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 3 Nov 2024 20:27:54 +0800
Subject: [PATCH 076/184] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20review=5Fplan-=E5=A4=8D=E4=B9=A0=E6=8F=90?=
=?UTF-8?q?=E9=86=92Web=E7=89=88=EF=BC=88Java=E6=8A=80=E6=9C=AF=E7=BB=83?=
=?UTF-8?q?=E4=B9=A0=E9=A1=B9=E7=9B=AE=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
创作不易,右上角点亮 ⭐️ Star 支持热心的作者吧 ^_^
https://gitee.com/TommyLemon/review_plan
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index c1d70c7fe..f668756d4 100644
--- a/README.md
+++ b/README.md
@@ -716,6 +716,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-builder](https://github.com/yeli19950109/apijson-builder) 简单包装 APIJSON,相比直接构造查询 JSON 更好记,ts 编写,调整了一些参数和使用方式
[lanmuc](https://gitee.com/element-admin/lanmuc) 后端低代码生产接口的平台,兼容配置式接口和编写式接口,可做到快速生产接口,上线项目
+
+[review_plan](https://gitee.com/PPXcodeTry/review_plan) 复习提醒Web版(Java技术练习项目)
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
From 8f55a3cef023a155e4bbadabdcea85df9175a059 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 11 Nov 2024 00:45:01 +0800
Subject: [PATCH 077/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20APIJSON=20+=20Nutz?=
=?UTF-8?q?=20=E6=A1=86=E6=9E=B6=20+=20NutzBoot=20=E7=9A=84=20Demo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
创作不易,右上角点亮 ⭐️ Star 支持下热心的作者吧 ^_^
https://github.com/vincent109/apijson-nutz
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index f668756d4..0d43b0302 100644
--- a/README.md
+++ b/README.md
@@ -718,6 +718,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[lanmuc](https://gitee.com/element-admin/lanmuc) 后端低代码生产接口的平台,兼容配置式接口和编写式接口,可做到快速生产接口,上线项目
[review_plan](https://gitee.com/PPXcodeTry/review_plan) 复习提醒Web版(Java技术练习项目)
+
+[apijson-nutz](https://github.com/vincent109/apijson-nutz) APIJSON + Nutz 框架 + NutzBoot 的 Demo
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
From d9b5405290f0b745d98e57c02eb54a00ca3803a1 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 18 Nov 2024 00:41:57 +0800
Subject: [PATCH 078/184] =?UTF-8?q?=F0=9F=8F=86=20Tencent=20Top=206=20Open?=
=?UTF-8?q?=20Source=20Project,=20Achieved=205=20Awards=20Inside=20&=20Out?=
=?UTF-8?q?side=20Tencent=20=F0=9F=9A=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/blob/master/README-English.md
---
README-English.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README-English.md b/README-English.md
index 4d4aacb19..9329faf0e 100644
--- a/README-English.md
+++ b/README-English.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-
🏆 Tencent Top 7 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
+🏆 Tencent Top 6 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
中文版
From 1f3b59b5583eb2d0a7739dabfc2ef841525ff2ce Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 24 Nov 2024 23:57:06 +0800
Subject: [PATCH 079/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?key[=20=E8=A1=A8=E7=A4=BA=20length(key)=20=E5=92=8C=20key{=20?=
=?UTF-8?q?=E8=A1=A8=E7=A4=BA=20json=5Flength(key)=EF=BC=8C=E5=8F=AF?=
=?UTF-8?q?=E4=B8=8E=20=E4=B8=8E=E6=88=96=E9=9D=9E=E9=80=BB=E8=BE=91?=
=?UTF-8?q?=E7=AC=A6=E3=80=81=E5=85=B6=E5=AE=83=E5=90=84=E7=A7=8D=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=E7=AC=A6=20=E7=BB=84=E5=90=88=E4=BD=BF=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
.../src/main/java/apijson/JSONObject.java | 25 +++++++++
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
APIJSONORM/src/main/java/apijson/SQL.java | 7 +++
.../java/apijson/orm/AbstractSQLConfig.java | 51 ++++++++++++++-----
5 files changed, 72 insertions(+), 15 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index d466682ca..f668c0d49 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.0.3
+ 7.0.5
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 8aa8eac3d..32c5caabb 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -480,6 +480,31 @@ public JSONObject putsEmpty(String key, boolean isEmpty, boolean trim) {
public JSONObject putsLength(String key, String compare) {
return puts(key+"{}", SQL.length(key) + compare);
}
+ /**
+ * @param key
+ * @param compare <=, > ...
+ * @param value 1, 5, 3.14, -99 ...
+ * @return {@link #puts(String, Object)}
+ */
+ public JSONObject putsLength(String key, String compare, Object value) {
+ return puts(key+"["+(StringUtil.isEmpty(compare) || "=".equals(compare) ? "" : ("!=".equals(compare) ? "!" : compare)), value);
+ }
+ /**
+ * @param key
+ * @param compare <=0, >5 ...
+ * @return {@link #puts(String, Object)}
+ */
+ public JSONObject putsJSONLength(String key, String compare) {
+ return puts(key+"{}", SQL.json_length(key) + compare);
+ }
+ /**
+ * @param key
+ * @param compare <=0, >5 ...
+ * @return {@link #puts(String, Object)}
+ */
+ public JSONObject putsJSONLength(String key, String compare, Object value) {
+ return puts(key + "{" + (StringUtil.isEmpty(compare) || "=".equals(compare) ? "" : ("!=".equals(compare) ? "!" : compare)), value);
+ }
/**设置搜索
* type = SEARCH_TYPE_CONTAIN_FULL
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 662dc912c..7252eb5a7 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.0.3";
+ public static final String VERSION = "7.0.5";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 391d5db48..6cec79bd2 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -116,6 +116,13 @@ public static String lengthCompare(String s, String compare) {
public static String length(String s) {
return "length(" + s + ")";
}
+ /**
+ * @param s 因为POWER(x,y)等函数含有不只一个key,所以需要客户端添加进去,服务端检测到条件中有'('和')'时就不转换,直接当SQL语句查询
+ * @return "json_length(" + s + ")"
+ */
+ public static String json_length(String s) {
+ return "json_length(" + s + ")";
+ }
/**
* @param s 因为POWER(x,y)等函数含有不只一个key,所以需要客户端添加进去,服务端检测到条件中有'('和')'时就不转换,直接当SQL语句查询
* @return "char_length(" + s + ")"
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index bcb1af598..4c82ea30e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -3585,7 +3585,9 @@ public String getCompareString(String key, String column, Object value, String t
if (value != null && JSON.isBooleanOrNumberOrString(value) == false && value instanceof Subquery == false) {
throw new IllegalArgumentException(key + ":value 中 value 不合法!比较运算 [>, <, >=, <=] 只支持 [Boolean, Number, String] 内的类型 !");
}
- if (StringUtil.isName(column) == false) {
+
+ String rc = column.endsWith("[") || column.endsWith("{") ? column.substring(0, column.length() - 1) : column;
+ if ( ! StringUtil.isName(rc)) {
throw new IllegalArgumentException(key + ":value 中 key 不合法!比较运算 [>, <, >=, <=] 不支持 [&, !, |] 中任何逻辑运算符 !");
}
@@ -3594,7 +3596,16 @@ public String getCompareString(String key, String column, Object value, String t
}
public String getKey(String key) {
- if (isTest()) {
+ String lenFun = "";
+ if (key.endsWith("[")) {
+ lenFun = isSQLServer() ? "datalength" : "length";
+ key = key.substring(0, key.length() - 1);
+ }
+ else if (key.endsWith("{")) {
+ lenFun = "json_length";
+ key = key.substring(0, key.length() - 1);
+ }
+ else if (isTest()) {
if (key.contains("'")) { // || key.contains("#") || key.contains("--")) {
throw new IllegalArgumentException("参数 " + key + " 不合法!key 中不允许有单引号 ' !");
}
@@ -3606,13 +3617,18 @@ public String getKey(String key) {
if (expression == null) {
expression = COLUMN_KEY_MAP == null ? null : COLUMN_KEY_MAP.get(key);
}
+
+ String sqlKey;
if (expression == null) {
- return getSQLKey(key);
+ sqlKey = getSQLKey(key);
+ }
+ else {
+ // (name,tag) left(date,4) 等
+ List raw = getRaw();
+ sqlKey = parseSQLExpression(KEY_KEY, expression, raw != null && raw.contains(KEY_KEY), false);
}
- // (name,tag) left(date,4) 等
- List raw = getRaw();
- return parseSQLExpression(KEY_KEY, expression, raw != null && raw.contains(KEY_KEY), false);
+ return lenFun.isEmpty() ? sqlKey : lenFun + "(" + sqlKey + ")";
}
public String getSQLKey(String key) {
String q = getQuote();
@@ -6016,9 +6032,15 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理
}
}
- //TODO if (key.endsWith("-")) { // 表示 key 和 value 顺序反过来: value LIKE key
+ String len = "";
+ if (key.endsWith("[") || key.endsWith("{")) {
+ len = key.substring(key.length() - 1);
+ key = key.substring(0, key.length() - 1);
+ }
+
+ // TODO if (key.endsWith("-")) { // 表示 key 和 value 顺序反过来: value LIKE key ?
- //不用Logic优化代码,否则 key 可能变为 key| 导致 key=value 变成 key|=value 而出错
+ // 不用Logic优化代码,否则 key 可能变为 key| 导致 key=value 变成 key|=value 而出错
String last = key.isEmpty() ? "" : key.substring(key.length() - 1);
if ("&".equals(last) || "|".equals(last) || "!".equals(last)) {
key = key.substring(0, key.length() - 1);
@@ -6026,18 +6048,21 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理
last = null;//避免key + StringUtil.getString(last)错误延长
}
- //"User:toUser":User转换"toUser":User, User为查询同名Table得到的JSONObject。交给客户端处理更好
- if (isTableKey) {//不允许在column key中使用Type:key形式
- key = Pair.parseEntry(key, true).getKey();//table以左边为准
+ // "User:toUser":User转换"toUser":User, User为查询同名Table得到的JSONObject。交给客户端处理更好
+ if (isTableKey) { // 不允许在column key中使用Type:key形式
+ key = Pair.parseEntry(key, true).getKey(); // table以左边为准
} else {
- key = Pair.parseEntry(key).getValue();//column以右边为准
+ key = Pair.parseEntry(key).getValue();// column 以右边为准
}
if (verifyName && StringUtil.isName(key.startsWith("@") ? key.substring(1) : key) == false) {
throw new IllegalArgumentException(method + "请求,字符 " + originKey + " 不合法!"
- + " key:value 中的key只能关键词 '@key' 或 'key[逻辑符][条件符]' 或 PUT请求下的 'key+' / 'key-' !");
+ + " key:value 中的 key 只能关键词 '@key' 或 'key[长度符][逻辑符][条件符]' 或 PUT 请求下的 'key+' / 'key-' !"
+ + "长度符 只能为 [ - length 和 { - json_length,逻辑符 只能是 & - 与、| - 或、! - 非 !");
}
+ key += len;
+
if (saveLogic && last != null) {
key = key + last;
}
From d9555eab0babbfff7d5fc48d5c10c58895cd9ec3 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 24 Nov 2024 23:59:58 +0800
Subject: [PATCH 080/184] =?UTF-8?q?=E5=90=84=E9=A1=B9=E8=8D=A3=E8=AA=89?=
=?UTF-8?q?=E6=88=90=E5=B0=B1=20(=E8=85=BE=E8=AE=AF=E5=86=85=E5=A4=96=205?=
=?UTF-8?q?=20=E4=B8=AA=E5=A5=96=E9=A1=B9=E3=80=81=E8=85=BE=E8=AE=AF?=
=?UTF-8?q?=E5=BC=80=E6=BA=90=E5=89=8D=E5=85=AD=E3=80=81=E8=85=BE=E8=AE=AF?=
=?UTF-8?q?=E5=90=8E=E7=AB=AF=20Star=20=E7=AC=AC=E4=B8=80=E3=80=81Trending?=
=?UTF-8?q?=20=E6=97=A5=E5=91=A8=E6=9C=88=E6=A6=9C=E5=A4=A7=E6=BB=A1?=
=?UTF-8?q?=E8=B4=AF=20=E7=AD=89)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0d43b0302..ea6ffa173 100644
--- a/README.md
+++ b/README.md
@@ -180,7 +180,7 @@ https://github.com/Tencent/APIJSON/wiki
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
-* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前七、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
+* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前六、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
From 067e7b9f5bb1d5bc0794741c0ffb31ca75dfff92 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Mon, 25 Nov 2024 00:05:11 +0800
Subject: [PATCH 081/184] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20key[=20=E8=A1=A8?=
=?UTF-8?q?=E7=A4=BA=20length(key)=20=E5=92=8C=20key{=20=E8=A1=A8=E7=A4=BA?=
=?UTF-8?q?=20json=5Flength(key)=20=E4=B8=8D=E6=94=AF=E6=8C=81=20=3D=20?=
=?UTF-8?q?=E5=92=8C=20!=3D=20=E6=AF=94=E8=BE=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 4c82ea30e..4bbd6f578 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -3571,7 +3571,9 @@ public String getEqualString(String key, String column, Object value, String raw
if (not) {
column = column.substring(0, column.length() - 1);
}
- if (StringUtil.isName(column) == false) {
+
+ String rc = column.endsWith("[") || column.endsWith("{") ? column.substring(0, column.length() - 1) : column;
+ if (StringUtil.isName(rc) == false) {
throw new IllegalArgumentException(key + ":value 中key不合法!不支持 ! 以外的逻辑符 !");
}
@@ -6032,12 +6034,6 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理
}
}
- String len = "";
- if (key.endsWith("[") || key.endsWith("{")) {
- len = key.substring(key.length() - 1);
- key = key.substring(0, key.length() - 1);
- }
-
// TODO if (key.endsWith("-")) { // 表示 key 和 value 顺序反过来: value LIKE key ?
// 不用Logic优化代码,否则 key 可能变为 key| 导致 key=value 变成 key|=value 而出错
@@ -6045,7 +6041,13 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理
if ("&".equals(last) || "|".equals(last) || "!".equals(last)) {
key = key.substring(0, key.length() - 1);
} else {
- last = null;//避免key + StringUtil.getString(last)错误延长
+ last = null; // 避免key + StringUtil.getString(last) 错误延长
+ }
+
+ String len = "";
+ if (key.endsWith("[") || key.endsWith("{")) {
+ len = key.substring(key.length() - 1);
+ key = key.substring(0, key.length() - 1);
}
// "User:toUser":User转换"toUser":User, User为查询同名Table得到的JSONObject。交给客户端处理更好
From 95d281da2bfe559bd749960daf8136f14ce03e8b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 30 Nov 2024 22:34:51 +0800
Subject: [PATCH 082/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=99=BB=E8=AE=B0=20?=
=?UTF-8?q?APIJSON=20+=20NutzBoot(=E5=9F=BA=E4=BA=8E=20Nutz=20=E6=A1=86?=
=?UTF-8?q?=E6=9E=B6)=20=E6=8E=A5=E8=BF=91=E6=88=90=E5=93=81=E7=9A=84=20De?=
=?UTF-8?q?mo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
创作不易、坚持更难,右上角点亮 ⭐️ Star 支持下热心的作者吧 ^_^
https://github.com/vincent109/apijson-nutz
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index ea6ffa173..810386451 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ This source code is licensed under the Apache License Version 2.0
+
From 2b6a2a9c6a99053c0aee3a734ca8461be87afbdc Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 8 Dec 2024 22:28:53 +0800
Subject: [PATCH 083/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=88=86=E9=A1=B5=E9=A1=B5=E7=A0=81=20page=20=E4=BB=8E=201=20?=
=?UTF-8?q?=E5=BC=80=E5=A7=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/JSONRequest.java | 2 +-
.../main/java/apijson/orm/AbstractParser.java | 34 ++++++++++++++-----
.../src/main/java/apijson/orm/Parser.java | 3 +-
3 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java
index cf64f1875..62d724199 100755
--- a/APIJSONORM/src/main/java/apijson/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java
@@ -152,7 +152,7 @@ public JSONRequest setSubqueryRange(String range) {
}
/**set from for Subquery
- * @param range
+ * @param from
* @return
*/
public JSONRequest setSubqueryFrom(String from) {
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 4da6aa249..3a3822d25 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -70,8 +70,12 @@ public abstract class AbstractParser implements Parser, Par
public static boolean IS_PRINT_REQUEST_ENDTIME_LOG = false;
- public static int DEFAULT_QUERY_COUNT = 10;
+ /**
+ * 分页页码是否从 1 开始,默认为从 0 开始
+ */
+ public static boolean IS_START_FROM_1 = false;
public static int MAX_QUERY_PAGE = 100;
+ public static int DEFAULT_QUERY_COUNT = 10;
public static int MAX_QUERY_COUNT = 100;
public static int MAX_UPDATE_COUNT = 10;
public static int MAX_SQL_COUNT = 200;
@@ -79,15 +83,22 @@ public abstract class AbstractParser implements Parser, Par
public static int MAX_ARRAY_COUNT = 5;
public static int MAX_QUERY_DEPTH = 5;
+ public boolean isStartFrom1() {
+ return IS_START_FROM_1;
+ }
@Override
- public int getDefaultQueryCount() {
- return DEFAULT_QUERY_COUNT;
+ public int getMinQueryPage() {
+ return isStartFrom1() ? 1 : 0;
}
@Override
public int getMaxQueryPage() {
return MAX_QUERY_PAGE;
}
@Override
+ public int getDefaultQueryCount() {
+ return DEFAULT_QUERY_COUNT;
+ }
+ @Override
public int getMaxQueryCount() {
return MAX_QUERY_COUNT;
}
@@ -1183,23 +1194,28 @@ public JSONObject onObjectParse(final JSONObject request
if (max < 0) {
max = 0;
}
+ int min = getMinQueryPage();
+
+ page += min;
+ max += min;
JSONObject pagination = new JSONObject(true);
Object explain = rp.get(JSONResponse.KEY_EXPLAIN);
if (explain instanceof JSONObject) {
pagination.put(JSONResponse.KEY_EXPLAIN, explain);
}
+
pagination.put(JSONResponse.KEY_TOTAL, total);
pagination.put(JSONRequest.KEY_COUNT, count);
pagination.put(JSONRequest.KEY_PAGE, page);
pagination.put(JSONResponse.KEY_MAX, max);
pagination.put(JSONResponse.KEY_MORE, page < max);
- pagination.put(JSONResponse.KEY_FIRST, page == 0);
+ pagination.put(JSONResponse.KEY_FIRST, page == min);
pagination.put(JSONResponse.KEY_LAST, page == max);
putQueryResult(pathPrefix + JSONResponse.KEY_INFO, pagination);
- if (total <= count*page) {
+ if (total <= count*(page - min)) {
query = JSONRequest.QUERY_TOTAL;//数量不够了,不再往后查询
}
}
@@ -1285,14 +1301,16 @@ public JSONArray onArrayParse(JSONObject request, String parentPath, String name
query2 = JSONRequest.QUERY_ALL;
break;
default:
- throw new IllegalArgumentException(path + "/" + JSONRequest.KEY_QUERY + ":value 中 value 的值不合法!必须在 [0,1,2] 或 [TABLE, TOTAL, ALL] 内 !");
+ throw new IllegalArgumentException(path + "/" + JSONRequest.KEY_QUERY + ":value 中 value 的值不合法!必须在 [0, 1, 2] 或 [TABLE, TOTAL, ALL] 内 !");
}
}
- int page2 = page == null ? 0 : page;
+ int minPage = getMinQueryPage(); // 兼容各种传 0 或 null/undefined 自动转 0 导致的问题
+ int page2 = page == null || page == 0 ? 0 : page - minPage;
+
int maxPage = getMaxQueryPage();
if (page2 < 0 || page2 > maxPage) {
- throw new IllegalArgumentException(path + "/" + JSONRequest.KEY_PAGE + ":value 中 value 的值不合法!必须在 0-" + maxPage + " 内 !");
+ throw new IllegalArgumentException(path + "/" + JSONRequest.KEY_PAGE + ":value 中 value 的值不合法!必须在 " + minPage + "-" + maxPage + " 内 !");
}
//不用total限制数量了,只用中断机制,total只在query = 1,2的时候才获取
diff --git a/APIJSONORM/src/main/java/apijson/orm/Parser.java b/APIJSONORM/src/main/java/apijson/orm/Parser.java
index 969dff95b..7ed0d1b1c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Parser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Parser.java
@@ -83,8 +83,9 @@ JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, St
ObjectParser createObjectParser(JSONObject request, String parentPath, SQLConfig arrayConfig, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception;
- int getDefaultQueryCount();
+ int getMinQueryPage();
int getMaxQueryPage();
+ int getDefaultQueryCount();
int getMaxQueryCount();
int getMaxUpdateCount();
int getMaxSQLCount();
From f35e89c37a2282cb03abcdb8cf5817968433930a Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 8 Dec 2024 22:48:52 +0800
Subject: [PATCH 084/184] =?UTF-8?q?=E5=A4=9A=E5=B9=B4=E6=8C=81=E7=BB=AD?=
=?UTF-8?q?=E8=BF=AD=E4=BB=A3=20(=E8=87=AA=202016=20=E5=B9=B4=E8=B5=B7?=
=?UTF-8?q?=E5=B7=B2=E8=BF=9E=E7=BB=AD=E7=BB=B4=E6=8A=A4=208=20=E5=B9=B4?=
=?UTF-8?q?=E5=A4=9A=EF=BC=8C70+=20=E8=B4=A1=E7=8C=AE=E8=80=85=E3=80=8190+?=
=?UTF-8?q?=20=E5=8F=91=E7=89=88=E3=80=813000+=20=E6=8F=90=E4=BA=A4?=
=?UTF-8?q?=EF=BC=8C=E4=B8=8D=E6=96=AD=E6=9B=B4=E6=96=B0=E8=BF=AD=E4=BB=A3?=
=?UTF-8?q?=E4=B8=AD...)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 810386451..85c7d267b 100644
--- a/README.md
+++ b/README.md
@@ -192,7 +192,7 @@ https://github.com/Tencent/APIJSON/wiki
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年起已连续维护 7 年多,70+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年起已连续维护 8 年多,70+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From 86be77f6c5b375a7b0af0741b512fe782e1cd9fa Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 8 Dec 2024 22:52:15 +0800
Subject: [PATCH 085/184] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E4=B8=BA=207.1.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index f668c0d49..a0a31fa94 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.0.5
+ 7.1.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 7252eb5a7..73c913169 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.0.5";
+ public static final String VERSION = "7.1.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From cddf465cb911d844d18cd19f72151a7b3d0189f4 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 8 Dec 2024 23:21:51 +0800
Subject: [PATCH 086/184] =?UTF-8?q?=E5=A4=9A=E5=B9=B4=E6=8C=81=E7=BB=AD?=
=?UTF-8?q?=E8=BF=AD=E4=BB=A3=20(=E8=87=AA=202016=20=E5=B9=B4=E8=B5=B7?=
=?UTF-8?q?=E5=B7=B2=E8=BF=9E=E7=BB=AD=E7=BB=B4=E6=8A=A4=208=20=E5=B9=B4?=
=?UTF-8?q?=EF=BC=8C70+=20=E8=B4=A1=E7=8C=AE=E8=80=85=E3=80=81100+=20?=
=?UTF-8?q?=E5=8F=91=E7=89=88=E3=80=813000+=20=E6=8F=90=E4=BA=A4=EF=BC=8C?=
=?UTF-8?q?=E4=B8=8D=E6=96=AD=E6=9B=B4=E6=96=B0=E8=BF=AD=E4=BB=A3=E4=B8=AD?=
=?UTF-8?q?...)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 85c7d267b..2a2ab91b9 100644
--- a/README.md
+++ b/README.md
@@ -192,7 +192,7 @@ https://github.com/Tencent/APIJSON/wiki
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年起已连续维护 8 年多,70+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年起已连续维护 8 年,70+ 贡献者、100+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From 70f65fe10dca3fa3f865e69d50fbd79e4bca3e27 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Dec 2024 23:21:19 +0800
Subject: [PATCH 087/184] =?UTF-8?q?=E8=B4=A1=E7=8C=AE=E8=80=85=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E=20gorm-plus=20=E4=BD=9C=E8=80=85=EF=BC=8C=E6=84=9F?=
=?UTF-8?q?=E8=B0=A2=E7=83=AD=E5=BF=83=E8=B4=A1=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#%E8%B4%A1%E7%8C%AE%E8%80%85%E4%BB%AC
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 2a2ab91b9..2c96007d6 100644
--- a/README.md
+++ b/README.md
@@ -360,7 +360,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [上海钰亿环保科技有限公司](#)
### 贡献者们
-主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、1 个美国加州大学学生、3 个 SUSTech 学生等):
+主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、gorm-plus 作者、1 个美国加州大学学生、3 个 SUSTech 学生等):
https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
Date: Tue, 17 Dec 2024 09:13:55 +0800
Subject: [PATCH 088/184] =?UTF-8?q?commit=20update=20join=E6=94=AF?=
=?UTF-8?q?=E6=8C=81@cast=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B=E8=BD=AC?=
=?UTF-8?q?=E6=8D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/apijson/orm/AbstractSQLConfig.java | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 4bbd6f578..c7539f613 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4847,8 +4847,20 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
String rt = on.getRelateType();
if (StringUtil.isEmpty(rt, false)) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
- + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ //解决join不支持@cast问题
+ Map castMap = j.getJoinConfig().getCast();
+ if (castMap.isEmpty()) {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ } else {
+ String leftTableRelationSql = quote + jt + quote + "." + quote + on.getKey() + quote;
+ Object castValueType = castMap.get(on.getOriginKey());
+ if (castValueType != null) {
+ leftTableRelationSql = "CAST(" + leftTableRelationSql + " AS " + castValueType + ")";
+ }
+ sql += (first ? ON : AND) + leftTableRelationSql + (isNot ? " != " : " = ")
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
}
else {
onJoinComplexRelation(sql, quote, j, jt, onList, on);
From 1f2d304cb62c8295ccaed308a7ed3582072d0f3d Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 22 Dec 2024 20:17:51 +0800
Subject: [PATCH 089/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?Apache/IoTDB=20-=20=E4=B8=80=E4=BD=93=E5=8C=96=E6=94=B6?=
=?UTF-8?q?=E9=9B=86=E3=80=81=E5=AD=98=E5=82=A8=E3=80=81=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E4=B8=8E=E5=88=86=E6=9E=90=E7=89=A9=E8=81=94=E7=BD=91=E6=97=B6?=
=?UTF-8?q?=E5=BA=8F=E6=95=B0=E6=8D=AE=E7=9A=84=E8=BD=AF=E4=BB=B6=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F=EF=BC=9B=E5=AE=8C=E5=96=84=20AI=20=E5=90=91=E9=87=8F?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=20Milvus=20=E7=9A=84=20SQL=20?=
=?UTF-8?q?=E5=87=BD=E6=95=B0=E6=B3=A8=E5=86=8C=EF=BC=9B=E5=8D=87=E7=BA=A7?=
=?UTF-8?q?=E7=89=88=E6=9C=AC=E6=94=AF=207.2.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../java/apijson/orm/AbstractSQLConfig.java | 29 +++++++++++++++++--
.../src/main/java/apijson/orm/SQLConfig.java | 6 +++-
4 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index a0a31fa94..ada599ad7 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.1.0
+ 7.2.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 73c913169..2b6e28d93 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.1.0";
+ public static final String VERSION = "7.2.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 4bbd6f578..11192f073 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -216,6 +216,7 @@ public abstract class AbstractSQLConfig implements SQLConfig implements SQLConfig(); // 保证顺序,避免配置冲突等意外情况
RAW_MAP.put("+", "");
@@ -820,6 +822,18 @@ public abstract class AbstractSQLConfig implements SQLConfig parser;
@@ -1011,7 +1025,7 @@ public AbstractSQLConfig setMethod(RequestMethod method) {
}
@Override
public boolean isPrepared() {
- return prepared;
+ return prepared && ! isMongoDB(); // MongoDB JDBC 还不支持预编译;
}
@Override
public AbstractSQLConfig setPrepared(boolean prepared) {
@@ -1269,6 +1283,15 @@ public static boolean isTDengine(String db) {
return DATABASE_TDENGINE.equals(db);
}
+
+ public boolean isIoTDB() {
+ return isIoTDB(getDatabase());
+ }
+ public static boolean isIoTDB(String db) {
+ return DATABASE_IOTDB.equals(db);
+ }
+
+
@Override
public boolean isRedis() {
return isRedis(getSQLDatabase());
@@ -1310,8 +1333,8 @@ public static boolean isSQLite(String db) {
}
@Override
- public String getQuote() {
- if(isElasticsearch()) {
+ public String getQuote() { // MongoDB 同时支持 `tbl` 反引号 和 "col" 双引号
+ if(isElasticsearch() || isIoTDB()) {
return "";
}
return isMySQL() || isMariaDB() || isTiDB() || isClickHouse() || isTDengine() || isMilvus() ? "`" : "\"";
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 7ed6cf663..08ba86131 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -37,12 +37,15 @@ public interface SQLConfig {
String DATABASE_MILVUS = "MILVUS"; // https://milvus.io
String DATABASE_INFLUXDB = "INFLUXDB"; // https://www.influxdata.com/products/influxdb-overview
String DATABASE_TDENGINE = "TDENGINE"; // https://tdengine.com
+ String DATABASE_IOTDB = "IOTDB"; // https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-JDBC.html
+
String DATABASE_REDIS = "REDIS"; // https://redisql.com
String DATABASE_MONGODB = "MONGODB"; // https://www.mongodb.com/docs/atlas/data-federation/query/query-with-sql
String DATABASE_KAFKA = "KAFKA"; // https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Kafka
- String DATABASE_MQ = "MQ"; //
String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
+ String DATABASE_MQ = "MQ"; //
+
String SCHEMA_INFORMATION = "information_schema"; //MySQL, PostgreSQL, SQL Server 都有的系统模式
String SCHEMA_SYS = "sys"; //SQL Server 系统模式
String TABLE_SCHEMA = "table_schema";
@@ -88,6 +91,7 @@ public interface SQLConfig {
boolean isMilvus();
boolean isInfluxDB();
boolean isTDengine();
+ boolean isIoTDB();
boolean isRedis();
boolean isMongoDB();
boolean isKafka();
From 99b910a769317978ea73a210950e11b1afa1f947 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Dec 2024 20:26:36 +0800
Subject: [PATCH 090/184] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20JOIN=20ON=20?=
=?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=20@cast=EF=BC=8C=E6=84=9F=E8=B0=A2?=
=?UTF-8?q?=20lindaifeng=20=E7=9A=84=E8=B4=A1=E7=8C=AE~=20#785?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/pull/785
---
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index c7539f613..c492dd420 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4847,9 +4847,10 @@ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNu
String rt = on.getRelateType();
if (StringUtil.isEmpty(rt, false)) {
- //解决join不支持@cast问题
- Map castMap = j.getJoinConfig().getCast();
- if (castMap.isEmpty()) {
+ // 解决 JOIN ON 不支持 @cast 问题
+ SQLConfig jc = j.getJoinConfig();
+ Map castMap = jc == null ? null : jc.getCast();
+ if (castMap == null || castMap.isEmpty()) {
sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
+ quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
} else {
From 0cae115bef0ce7611b435240a3bf1ba326ac5d51 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 29 Dec 2024 21:45:15 +0800
Subject: [PATCH 091/184] =?UTF-8?q?=E8=9A=82=E8=9A=81=E9=9B=86=E5=9B=A2?=
=?UTF-8?q?=E6=BA=90=E4=BC=9E=E6=89=AB=E6=8F=8F=20APIJSON=20=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE=E4=BA=86=20issue=EF=BC=8C=E6=84=9F=E8=B0=A2=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://www.sourcebrella.com
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 2c96007d6..632fdc148 100644
--- a/README.md
+++ b/README.md
@@ -474,10 +474,10 @@ https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-还有为 APIJSON 扫描代码贡献 Issue 的 [奇安信代码卫士](https://github.com/QiAnXinCodeSafe) 和 [源伞科技](https://www.sourcebrella.com)
+还有为 APIJSON 扫描代码贡献 Issue 的 [蚂蚁集团源伞](https://www.sourcebrella.com) 和 [奇安信代码卫士](https://github.com/QiAnXinCodeSafe)
From 989499c6549e2d4860a43db0bf872a66e325c4a8 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 29 Dec 2024 22:20:46 +0800
Subject: [PATCH 092/184] =?UTF-8?q?=E8=9A=82=E8=9A=81=E9=9B=86=E5=9B=A2?=
=?UTF-8?q?=E6=BA=90=E4=BC=9E=E6=89=AB=E6=8F=8F=20APIJSON=20=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE=E4=BA=86=20issue=EF=BC=8C=E6=84=9F=E8=B0=A2=E8=B4=A1?=
=?UTF-8?q?=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://www.sourcebrella.com
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 632fdc148..34b06f07a 100644
--- a/README.md
+++ b/README.md
@@ -476,7 +476,7 @@ https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-
+
From 07b4acee9c6a2aad2b5b4ea83cf48e0e418c0b81 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 5 Jan 2025 23:13:18 +0800
Subject: [PATCH 093/184] =?UTF-8?q?=E6=96=87=E7=AB=A0=E6=96=B0=E5=A2=9E=20?=
=?UTF-8?q?wend=E7=9C=8B=E6=BA=90=E7=A0=81-ORM-APIJSON=EF=BC=8C=E6=84=9F?=
=?UTF-8?q?=E8=B0=A2=E5=8D=9A=E4=B8=BB=E7=9A=84=E8=B4=A1=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
点赞、收藏 支持下热心的作者吧 ^_^
https://itwend.blog.csdn.net/article/details/143980281
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 34b06f07a..41c0af399 100644
--- a/README.md
+++ b/README.md
@@ -610,7 +610,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[APIJSON语法使用,超详细](https://blog.csdn.net/qq_36565607/article/details/139167040)
-
+[wend看源码-ORM-APIJSON](https://itwend.blog.csdn.net/article/details/143980281)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
From 138205c44f708a943aa10ac849d4c9376175cb0d Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 11 Jan 2025 00:38:08 +0800
Subject: [PATCH 094/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?DuckDB-=E9=AB=98=E6=80=A7=E8=83=BD=E5=A4=9A=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E6=A0=BC=E5=BC=8F=20OLAP=20=E6=95=B0=E6=8D=AE=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 9 +++++++++
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
2 files changed, 11 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 821cfe112..4f098e069 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -224,6 +224,7 @@ public abstract class AbstractSQLConfig implements SQLConfig(); // 保证顺序,避免配置冲突等意外情况
@@ -1332,6 +1333,14 @@ public static boolean isSQLite(String db) {
return DATABASE_SQLITE.equals(db);
}
+ @Override
+ public boolean isDuckDB() {
+ return isDuckDB(getSQLDatabase());
+ }
+ public static boolean isDuckDB(String db) {
+ return DATABASE_DUCKDB.equals(db);
+ }
+
@Override
public String getQuote() { // MongoDB 同时支持 `tbl` 反引号 和 "col" 双引号
if(isElasticsearch() || isIoTDB()) {
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 08ba86131..1f8519383 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -43,6 +43,7 @@ public interface SQLConfig {
String DATABASE_MONGODB = "MONGODB"; // https://www.mongodb.com/docs/atlas/data-federation/query/query-with-sql
String DATABASE_KAFKA = "KAFKA"; // https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Kafka
String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
+ String DATABASE_DUCKDB = "DUCKDB"; // https://duckdb.org
String DATABASE_MQ = "MQ"; //
@@ -97,6 +98,7 @@ public interface SQLConfig {
boolean isKafka();
boolean isMQ();
boolean isSQLite();
+ boolean isDuckDB();
// 暂时只兼容以上几种
From 2cb277e12b71908ccd309de06d8c48d33981f2fe Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 11 Jan 2025 00:44:43 +0800
Subject: [PATCH 095/184] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E8=87=B3=207.3.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index ada599ad7..b40f66e5e 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.2.0
+ 7.3.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 2b6e28d93..3bfe4af98 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.2.0";
+ public static final String VERSION = "7.3.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From 3461521d3a438cd0818c77fcc46097624427bae6 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 13 Jan 2025 00:06:13 +0800
Subject: [PATCH 096/184] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=B8=A5=E8=B0=A8?=
=?UTF-8?q?=E8=A7=84=E8=8C=83=EF=BC=8C=E8=9A=82=E8=9A=81=E9=9B=86=E5=9B=A2?=
=?UTF-8?q?=E6=BA=90=E4=BC=9E=20Pinpoint=20=E4=BB=A3=E7=A0=81=E6=89=AB?=
=?UTF-8?q?=E6=8F=8F=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A=E5=B9=B3=E5=9D=87?=
=?UTF-8?q?=E6=AF=8F=E8=A1=8C=E4=BB=A3=E7=A0=81=20Bug=20=E7=8E=87=E4=BD=8E?=
=?UTF-8?q?=E8=87=B3=200.15%?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/blob/master/README.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-apijson
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 41c0af399..5ccf60161 100644
--- a/README.md
+++ b/README.md
@@ -189,10 +189,10 @@ https://github.com/Tencent/APIJSON/wiki
* **功能丰富强大** (增删改查、分页排序、分组聚合、各种条件、各种 JOIN、各种子查询、跨库连表 等零代码实现)
* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防 SQL 注入)
* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
-* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
+* **高质可靠代码** (代码严谨规范,蚂蚁集团源伞 Pinpoint 代码扫描分析报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年起已连续维护 8 年,70+ 贡献者、100+ 发版、3000+ 提交,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年起已连续维护 8 年多,70+ 贡献者、100+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From 5b2c1ed83996e3db7e21d0e683d48daac6659afc Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Thu, 30 Jan 2025 11:20:12 +0800
Subject: [PATCH 097/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?SurrealDB-=E5=85=B3=E7=B3=BB=E3=80=81=E6=97=B6=E5=BA=8F?=
=?UTF-8?q?=E3=80=81=E5=9B=BE=E3=80=81=E9=94=AE=E5=80=BC=E3=80=81=E6=90=9C?=
=?UTF-8?q?=E7=B4=A2=E3=80=81=E6=96=87=E6=A1=A3=20=E7=AD=89=E5=A4=9A?=
=?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=95=B0=E6=8D=AE=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/APIJSON/apijson-surrealdb
---
APIJSONORM/pom.xml | 2 +-
.../src/main/java/apijson/JSONObject.java | 27 ++++---
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../java/apijson/orm/AbstractSQLConfig.java | 75 ++++++++++++-------
.../src/main/java/apijson/orm/SQLConfig.java | 6 ++
5 files changed, 72 insertions(+), 40 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index b40f66e5e..b301c6d95 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.3.0
+ 7.4.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 32c5caabb..68fb30c57 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -139,8 +139,9 @@ public JSONObject setUserIdIn(List list) {
public static final String KEY_ROLE = "@role"; //角色,拥有对某些数据的某些操作的权限
public static final String KEY_DATABASE = "@database"; //数据库类型,默认为MySQL
- public static final String KEY_SCHEMA = "@schema"; //数据库,Table在非默认schema内时需要声明
public static final String KEY_DATASOURCE = "@datasource"; //数据源
+ public static final String KEY_NAMESPACE = "@namespace"; //命名空间,Table在非默认namespace内时需要声明
+ public static final String KEY_SCHEMA = "@schema"; //数据库,Table在非默认schema内时需要声明
public static final String KEY_EXPLAIN = "@explain"; //分析 true/false
public static final String KEY_CACHE = "@cache"; //缓存 RAM/ROM/ALL
public static final String KEY_COLUMN = "@column"; //查询的Table字段或SQL函数
@@ -169,8 +170,9 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST = new ArrayList();
TABLE_KEY_LIST.add(KEY_ROLE);
TABLE_KEY_LIST.add(KEY_DATABASE);
- TABLE_KEY_LIST.add(KEY_SCHEMA);
TABLE_KEY_LIST.add(KEY_DATASOURCE);
+ TABLE_KEY_LIST.add(KEY_NAMESPACE);
+ TABLE_KEY_LIST.add(KEY_SCHEMA);
TABLE_KEY_LIST.add(KEY_EXPLAIN);
TABLE_KEY_LIST.add(KEY_CACHE);
TABLE_KEY_LIST.add(KEY_COLUMN);
@@ -253,13 +255,6 @@ public JSONObject setRole(String role) {
public JSONObject setDatabase(String database) {
return puts(KEY_DATABASE, database);
}
- /**set schema where table was puts
- * @param schema
- * @return this
- */
- public JSONObject setSchema(String schema) {
- return puts(KEY_SCHEMA, schema);
- }
/**set datasource where table was puts
* @param datasource
* @return this
@@ -267,6 +262,20 @@ public JSONObject setSchema(String schema) {
public JSONObject setDatasource(String datasource) {
return puts(KEY_DATASOURCE, datasource);
}
+ /**set namespace where table was puts
+ * @param namespace
+ * @return this
+ */
+ public JSONObject setNamespace(String namespace) {
+ return puts(KEY_NAMESPACE, namespace);
+ }
+ /**set schema where table was puts
+ * @param schema
+ * @return this
+ */
+ public JSONObject setSchema(String schema) {
+ return puts(KEY_SCHEMA, schema);
+ }
/**set if return explain informations
* @param explain
* @return
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 3bfe4af98..3484638cb 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.3.0";
+ public static final String VERSION = "7.4.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 4f098e069..51b76e193 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -47,31 +47,11 @@
import apijson.orm.model.Table;
import apijson.orm.model.TestRecord;
-import static apijson.JSONObject.KEY_CACHE;
-import static apijson.JSONObject.KEY_CAST;
-import static apijson.JSONObject.KEY_COLUMN;
-import static apijson.JSONObject.KEY_COMBINE;
-import static apijson.JSONObject.KEY_DATABASE;
-import static apijson.JSONObject.KEY_DATASOURCE;
-import static apijson.JSONObject.KEY_EXPLAIN;
-import static apijson.JSONObject.KEY_FROM;
-import static apijson.JSONObject.KEY_GROUP;
-import static apijson.JSONObject.KEY_HAVING;
-import static apijson.JSONObject.KEY_HAVING_AND;
-import static apijson.JSONObject.KEY_ID;
-import static apijson.JSONObject.KEY_JSON;
-import static apijson.JSONObject.KEY_NULL;
-import static apijson.JSONObject.KEY_ORDER;
-import static apijson.JSONObject.KEY_KEY;
-import static apijson.JSONObject.KEY_RAW;
-import static apijson.JSONObject.KEY_ROLE;
-import static apijson.JSONObject.KEY_SCHEMA;
-import static apijson.JSONObject.KEY_USER_ID;
+import static apijson.JSONObject.*;
import static apijson.RequestMethod.DELETE;
import static apijson.RequestMethod.GET;
import static apijson.RequestMethod.POST;
import static apijson.RequestMethod.PUT;
-import static apijson.JSONObject.KEY_METHOD;
import static apijson.SQL.AND;
import static apijson.SQL.NOT;
import static apijson.SQL.ON;
@@ -122,6 +102,7 @@ public abstract class AbstractSQLConfig implements SQLConfig implements SQLConfig(); // 保证顺序,避免配置冲突等意外情况
@@ -300,7 +282,6 @@ public abstract class AbstractSQLConfig implements SQLConfig implements SQLConfig setNamespace(String namespace) {
+ this.namespace = namespace;
+ return this;
+ }
+
@Override
public String getSchema() {
return schema;
@@ -1374,7 +1379,7 @@ public String getSQLSchema() {
return SCHEMA_SYS; //SQL Server 在 sys 中的属性比 information_schema 中的要全,能拿到注释
}
if (AllTable.TAG.equals(table) || AllColumn.TAG.equals(table)
- || AllTableComment.TAG.equals(table) || AllTableComment.TAG.equals(table)) {
+ || AllTableComment.TAG.equals(table) || AllColumnComment.TAG.equals(table)) {
return ""; //Oracle, Dameng 的 all_tables, dba_tables 和 all_tab_columns, dba_columns 表好像不属于任何 Schema
}
@@ -1384,6 +1389,7 @@ public String getSQLSchema() {
}
return sch == null ? DEFAULT_SCHEMA : sch; // 最后代码默认兜底配置
}
+
@Override
public AbstractSQLConfig setSchema(String schema) {
if (schema != null) {
@@ -2696,6 +2702,14 @@ public String getLimitString() {
int offset = getOffset(getPage(), count);
return " LIMIT " + offset + ", " + count; // 目前 moql-transx 的限制
+ } else if (isSurrealDB()) {
+ if (count == 0) {
+ Parser parser = getParser();
+ count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
+ }
+
+ int offset = getOffset(getPage(), count);
+ return " START " + offset + " LIMIT " + count;
}
if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
@@ -5116,15 +5130,17 @@ public static SQLConfig newSQLConfig(RequestMethod method,
+ StringUtil.getString(DATABASE_LIST.toArray()) + "] 中的一种!");
}
- String schema = request.getString(KEY_SCHEMA);
String datasource = request.getString(KEY_DATASOURCE);
+ String namespace = request.getString(KEY_NAMESPACE);
+ String schema = request.getString(KEY_SCHEMA);
SQLConfig config = callback.getSQLConfig(method, database, schema, datasource, table);
config.setAlias(alias);
config.setDatabase(database); // 不删,后面表对象还要用的,必须放在 parseJoin 前
- config.setSchema(schema); // 不删,后面表对象还要用的
config.setDatasource(datasource); // 不删,后面表对象还要用的
+ config.setNamespace(namespace); // 不删,后面表对象还要用的
+ config.setSchema(schema); // 不删,后面表对象还要用的
if (isProcedure) {
return config;
@@ -5282,8 +5298,9 @@ else if (userId instanceof Subquery) {}
request.remove(KEY_ROLE);
request.remove(KEY_EXPLAIN);
request.remove(KEY_CACHE);
- request.remove(KEY_DATASOURCE);
request.remove(KEY_DATABASE);
+ request.remove(KEY_DATASOURCE);
+ request.remove(KEY_NAMESPACE);
request.remove(KEY_SCHEMA);
request.remove(KEY_FROM);
request.remove(KEY_COLUMN);
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 1f8519383..8e5755f89 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -44,6 +44,7 @@ public interface SQLConfig {
String DATABASE_KAFKA = "KAFKA"; // https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-Kafka
String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
String DATABASE_DUCKDB = "DUCKDB"; // https://duckdb.org
+ String DATABASE_SURREALDB = "SURREALDB"; // https://surrealdb.com
String DATABASE_MQ = "MQ"; //
@@ -99,6 +100,7 @@ public interface SQLConfig {
boolean isMQ();
boolean isSQLite();
boolean isDuckDB();
+ boolean isSurrealDB();
// 暂时只兼容以上几种
@@ -229,6 +231,10 @@ default int[] getDBVersionNums() {
String getDatabase();
SQLConfig setDatabase(String database);
+ String getSQLNamespace();
+ String getNamespace();
+ SQLConfig setNamespace(String namespace);
+
String getSQLSchema();
String getSchema();
SQLConfig setSchema(String schema);
From 2f5d947de21664ce77d16c20ec8851cd3d76e1dd Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 1 Feb 2025 20:14:54 +0800
Subject: [PATCH 098/184] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20SQL=20JOIN=20?=
=?UTF-8?q?=E5=9C=A8=E4=B8=80=E5=AF=B9=E5=A4=9A=E6=88=96=E5=A4=9A=E5=AF=B9?=
=?UTF-8?q?=E5=A4=9A=E6=97=B6=E8=BF=94=E5=9B=9E=E5=8D=95=E7=8B=AC=E6=9F=A5?=
=?UTF-8?q?=E8=AF=A2=E7=9A=84=E9=87=8D=E5=A4=8D=E5=89=AF=E8=A1=A8=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=EF=BC=8C=E8=80=8C=E4=B8=8D=E6=98=AF=20SQL=20=E7=BB=93?=
=?UTF-8?q?=E6=9E=9C=E9=9B=86=E9=87=8C=E7=9A=84=E5=89=AF=E8=A1=A8=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../apijson/orm/AbstractObjectParser.java | 48 ++++++++++++++-----
.../main/java/apijson/orm/AbstractParser.java | 41 +++++++++-------
.../java/apijson/orm/AbstractSQLConfig.java | 4 +-
.../java/apijson/orm/AbstractSQLExecutor.java | 23 +++++++--
.../main/java/apijson/orm/ObjectParser.java | 8 +++-
.../src/main/java/apijson/orm/Parser.java | 4 +-
6 files changed, 91 insertions(+), 37 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 94e2020d0..8f6494c15 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -123,6 +123,18 @@ public AbstractObjectParser setParentPath(String parentPath) {
return this;
}
+ protected JSONObject cache;
+ @Override
+ public JSONObject getCache() {
+ return cache;
+ }
+
+ @Override
+ public AbstractObjectParser setCache(JSONObject cache) {
+ this.cache = cache;
+ return this;
+ }
+
protected int position;
public int getPosition() {
return position;
@@ -243,6 +255,7 @@ public AbstractObjectParser parse(String name, boolean isReuse) throws Exception
int index = 0;
// hasOtherKeyNotFun = false;
+ JSONObject viceItem = null;
for (Entry entry : set) {
if (isBreakParse()) {
@@ -280,7 +293,14 @@ else if (value instanceof JSONObject) { // JSONObject,往下一级提取
childMap.put(key, (JSONObject)value);
}
else { // 直接解析并替换原来的,[]:{} 内必须直接解析,否则会因为丢掉count等属性,并且total@:"/[]/total"必须在[]:{} 后!
- response.put(key, onChildParse(index, key, (JSONObject)value));
+ JSON cache = index <= 0 || type != TYPE_ITEM || viceItem == null ? null : viceItem.getJSONObject(key);
+ JSON result = onChildParse(index, key, (JSONObject) value, cache);
+ if (index <= 0 && type == TYPE_ITEM) {
+ JSONObject mainItem = (JSONObject) result;
+ viceItem = result == null ? null : (JSONObject) mainItem.remove(AbstractSQLExecutor.KEY_VICE_ITEM);
+ }
+
+ response.put(key, result);
index ++;
}
}
@@ -368,7 +388,7 @@ public boolean onParse(@NotNull String key, @NotNull Object value) throws Except
+ JSONRequest.SUBQUERY_RANGE_ALL + ", " + JSONRequest.SUBQUERY_RANGE_ANY + "] 中的一个!");
}
- JSONArray arr = parser.onArrayParse(subquery, path, key, true);
+ JSONArray arr = parser.onArrayParse(subquery, path, key, true, null);
JSONObject obj = arr == null || arr.isEmpty() ? null : arr.getJSONObject(0);
if (obj == null) {
@@ -530,18 +550,19 @@ else if (isTable && key.startsWith("@") && JSONRequest.TABLE_KEY_LIST.contains(k
* @param index
* @param key
* @param value
+ * @param cache
* @return
* @throws Exception
*/
@Override
- public JSON onChildParse(int index, String key, JSONObject value) throws Exception {
+ public JSON onChildParse(int index, String key, JSONObject value, JSON cache) throws Exception {
boolean isFirst = index <= 0;
boolean isMain = isFirst && type == TYPE_ITEM;
JSON child;
boolean isEmpty;
- if (apijson.JSONObject.isArrayKey(key)) {//APIJSON Array
+ if (apijson.JSONObject.isArrayKey(key)) { // APIJSON Array
if (isMain) {
throw new IllegalArgumentException(parentPath + "/" + key + ":{} 不合法!"
+ "数组 []:{} 中第一个 key:{} 必须是主表 TableKey:{} !不能为 arrayKey[]:{} !");
@@ -557,7 +578,7 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
}
String query = value.getString(KEY_QUERY);
- child = parser.onArrayParse(value, path, key, isSubquery);
+ child = parser.onArrayParse(value, path, key, isSubquery, cache instanceof JSONArray ? (JSONArray) cache : null);
isEmpty = child == null || ((JSONArray) child).isEmpty();
if ("2".equals(query) || "ALL".equals(query)) { // 不判断 isEmpty,因为分页数据可能只是某页没有
@@ -594,7 +615,8 @@ public JSON onChildParse(int index, String key, JSONObject value) throws Excepti
}
}
- child = parser.onObjectParse(value, path, key, isMain ? arrayConfig.setType(SQLConfig.TYPE_ITEM_CHILD_0) : null, isSubquery);
+ child = parser.onObjectParse(value, path, key, isMain ? arrayConfig.setType(SQLConfig.TYPE_ITEM_CHILD_0) : null
+ , isSubquery, cache instanceof JSONObject ? (JSONObject) cache : null);
isEmpty = child == null || ((JSONObject) child).isEmpty();
if (isFirst && isEmpty) {
@@ -776,7 +798,7 @@ public void onTableArrayParse(String key, JSONArray valueArray) throws Exception
req = parser.parseCorrectRequest(method, childKey, version, "", req, maxUpdateCount, parser);
}
//parser.getMaxSQLCount() ? 可能恶意调用接口,把数据库拖死
- result = (JSONObject) onChildParse(0, "" + i, req);
+ result = (JSONObject) onChildParse(0, "" + i, req, null);
}
catch (Exception e) {
if (allowPartialFailed == false) {
@@ -1080,7 +1102,7 @@ public void onChildResponse() throws Exception {
if (set != null) {
int index = 0;
for (Entry entry : set) {
- Object child = entry == null ? null : onChildParse(index, entry.getKey(), entry.getValue());
+ Object child = entry == null ? null : onChildParse(index, entry.getKey(), entry.getValue(), null);
if (child == null
|| (child instanceof JSONObject && ((JSONObject) child).isEmpty())
|| (child instanceof JSONArray && ((JSONArray) child).isEmpty())
@@ -1106,8 +1128,11 @@ public Object onReferenceParse(@NotNull String path) {
public JSONObject onSQLExecute() throws Exception {
int position = getPosition();
- JSONObject result;
- if (isArrayMainTable && position > 0) { // 数组主表使用专门的缓存数据
+ JSONObject result = getCache();
+ if (result != null) {
+ parser.putQueryResult(path, result);
+ }
+ else if (isArrayMainTable && position > 0) { // 数组主表使用专门的缓存数据
result = parser.getArrayMainCacheItem(parentPath.substring(0, parentPath.lastIndexOf("[]") + 2), position);
}
else {
@@ -1134,7 +1159,8 @@ public JSONObject onSQLExecute() throws Exception {
JSONObject obj = rawList.get(i);
if (obj != null) {
- parser.putQueryResult(arrayPath + "/" + i + "/" + name, obj); // 解决获取关联数据时requestObject里不存在需要的关联数据
+ // obj.remove(AbstractSQLExecutor.KEY_VICE_ITEM);
+ parser.putQueryResult(arrayPath + "/" + i + "/" + name, obj); // 解决获取关联数据时requestObject里不存在需要的关联数据
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 3a3822d25..22a984fa5 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -537,7 +537,7 @@ public JSONObject parseResponse(JSONObject request) {
queryDepth = 0;
executedSQLDuration = 0;
- requestObject = onObjectParse(request, null, null, null, false);
+ requestObject = onObjectParse(request, null, null, null, false, null);
onCommit();
}
@@ -1081,17 +1081,18 @@ public JSONObject getStructure(@NotNull String table, String method, String tag,
// protected SQLConfig itemConfig;
/**获取单个对象,该对象处于parentObject内
- * @param request parentObject 的 value
- * @param parentPath parentObject 的路径
- * @param name parentObject 的 key
- * @param arrayConfig config for array item
- * @param isSubquery 是否为子查询
- * @return
- * @throws Exception
- */
+ * @param request parentObject 的 value
+ * @param parentPath parentObject 的路径
+ * @param name parentObject 的 key
+ * @param arrayConfig config for array item
+ * @param isSubquery 是否为子查询
+ * @param cache SQL 结果缓存
+ * @return
+ * @throws Exception
+ */
@Override
- public JSONObject onObjectParse(final JSONObject request
- , String parentPath, String name, final SQLConfig arrayConfig, boolean isSubquery) throws Exception {
+ public JSONObject onObjectParse(final JSONObject request, String parentPath, String name
+ , final SQLConfig arrayConfig, boolean isSubquery, JSONObject cache) throws Exception {
if (Log.DEBUG) {
Log.i(TAG, "\ngetObject: parentPath = " + parentPath
@@ -1135,6 +1136,8 @@ public JSONObject onObjectParse(final JSONObject request
}
// 对象 - 设置 method
setOpMethod(request, op, name);
+
+ op.setCache(cache);
op = op.parse(name, isReuse);
JSONObject response = null;
@@ -1251,14 +1254,16 @@ public JSONObject onObjectParse(final JSONObject request
}
/**获取对象数组,该对象数组处于parentObject内
+ * @param request parentObject的value
* @param parentPath parentObject的路径
* @param name parentObject的key
- * @param request parentObject的value
+ * @param isSubquery 是否为子查询
+ * @param cache SQL 结果缓存
* @return
* @throws Exception
*/
@Override
- public JSONArray onArrayParse(JSONObject request, String parentPath, String name, boolean isSubquery) throws Exception {
+ public JSONArray onArrayParse(JSONObject request, String parentPath, String name, boolean isSubquery, JSONArray cache) throws Exception {
if (Log.DEBUG) {
Log.i(TAG, "\n\n\n onArrayParse parentPath = " + parentPath
+ "; name = " + name + "; request = " + JSON.toJSONString(request));
@@ -1355,7 +1360,8 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
//Table<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- response = new JSONArray();
+
+ List joinList = onJoinParse(join, request);
SQLConfig config = createSQLConfig()
.setMethod(requestMethod)
.setCount(size)
@@ -1363,15 +1369,16 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
.setQuery(query2)
.setCompat(compat)
.setTable(arrTableKey)
- .setJoinList(onJoinParse(join, request));
+ .setJoinList(joinList);
JSONObject parent;
boolean isExtract = true;
+ response = new JSONArray();
//生成size个
for (int i = 0; i < (isSubquery ? 1 : size); i++) {
- parent = onObjectParse(request, isSubquery ? parentPath : path, isSubquery ? name : "" + i, config.setType(SQLConfig.TYPE_ITEM).setPosition(i), isSubquery);
+ parent = onObjectParse(request, isSubquery ? parentPath : path, isSubquery ? name : "" + i, config.setType(SQLConfig.TYPE_ITEM).setPosition(i), isSubquery, null);
if (parent == null || parent.isEmpty()) {
break;
}
@@ -1386,7 +1393,7 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
@SuppressWarnings("unchecked")
List list = fo == null ? null : (List) fo.remove(AbstractSQLExecutor.KEY_RAW_LIST);
- if (list != null && list.isEmpty() == false) {
+ if (list != null && list.isEmpty() == false && (joinList == null || joinList.isEmpty())) {
isExtract = false;
list.set(0, fo); // 不知道为啥第 0 项也加了 @RAW@LIST
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 51b76e193..6c46d2059 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4504,7 +4504,7 @@ public String getSetString(RequestMethod method, Map content, bo
if (setString.isEmpty()) {
throw new IllegalArgumentException("PUT 请求必须在Table内设置要修改的 key:value !");
}
- return (isClickHouse()?" ":" SET ") + setString;
+ return (isClickHouse() ? " " : " SET ") + setString;
}
/**SET key = concat(key, 'value')
@@ -6023,7 +6023,7 @@ public static String getRealKey(RequestMethod method, String originKey
return originKey;
}
- String key = new String(originKey);
+ String key = originKey;
if (key.endsWith("$")) {//搜索 LIKE,查询时处理
String k = key.substring(0, key.length() - 1);
// key%$:"a" -> key LIKE '%a%'; key?%$:"a" -> key LIKE 'a%'; key_?$:"a" -> key LIKE '_a'; key_%$:"a" -> key LIKE '_a%'
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 36ef71ca9..fd708f9b8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -32,6 +32,7 @@ public abstract class AbstractSQLExecutor implements SQLExecut
//是否返回 值为null的字段
public static boolean ENABLE_OUTPUT_NULL_COLUMN = false;
public static String KEY_RAW_LIST = "@RAW@LIST"; // 避免和字段命名冲突,不用 $RAW@LIST$ 是因为 $ 会在 fastjson 内部转义,浪费性能
+ public static String KEY_VICE_ITEM = "@VICE@ITEM"; // 避免和字段命名冲突,不用 $VICE@LIST$ 是因为 $ 会在 fastjson 内部转义,浪费性能
private Parser parser;
@Override
@@ -406,6 +407,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n execute while (rs.next()){ index = " + index + "\n\n");
JSONObject item = new JSONObject(true);
+ JSONObject viceItem = null;
JSONObject curItem = item;
boolean isMain = true;
@@ -537,6 +539,9 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
columnIndexAndJoinMap[i - 1] = curJoin;
}
+ //boolean isVice = false;
+ //String viceName = null;
+
// 如果是主表则直接用主表对应的 item,否则缓存副表数据到 childMap
Join prevJoin = columnIndexAndJoinMap == null || i < 2 ? null : columnIndexAndJoinMap[i - 2];
if (curJoin != prevJoin) { // 前后字段不在同一个表对象,即便后面出现 null,也不该是主表数据,而是逻辑 bug 导致
@@ -556,6 +561,7 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
}
}
}
+
String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
if (StringUtil.isEmpty(viceSql, true)) {
@@ -568,10 +574,17 @@ else if (curJoin.isOuterJoin() || curJoin.isAntiJoin()) {
// 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 childMap.put(viceSql, new JSONObject()); // 缓存固定空数据,避免后续多余查询
}
else {
- curItem = childMap.get(viceSql);
+ //isVice = true;
+ String viceName = viceConfig.getTable() + (StringUtil.isEmpty(viceConfig.getAlias()) ? "" : ":" + StringUtil.isEmpty(viceConfig.getAlias()));
+ if (viceItem == null) {
+ viceItem = new JSONObject(true);
+ }
+ curItem = viceItem.getJSONObject(viceName);
+ //curItem = childMap.get(viceSql);
if (curItem == null) {
curItem = new JSONObject(true);
- childMap.put(viceSql, curItem);
+ //childMap.put(viceSql, curItem);
+ viceItem.put(viceName, curItem);
}
}
}
@@ -579,6 +592,10 @@ else if (curJoin.isOuterJoin() || curJoin.isAntiJoin()) {
curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
}
+ if (viceItem != null) {
+ item.put(KEY_VICE_ITEM, viceItem);
+ }
+
resultList = onPutTable(config, rs, rsmd, resultList, index, item);
Log.d(TAG, "execute while (rs.next()) { resultList.put( " + index + ", result); " + "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
@@ -907,7 +924,7 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
// 主表必须 put 至少一个 null 进去,否则全部字段为 null 都不 put 会导致中断后续正常返回值
if (value != null) {
table.put(label, value);
- } else{
+ } else {
if (join == null && table.isEmpty()) {
table.put(label, null);
} else if (ENABLE_OUTPUT_NULL_COLUMN) {
diff --git a/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
index 6df050e0b..205126908 100755
--- a/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
@@ -26,6 +26,10 @@ public interface ObjectParser {
String getParentPath();
ObjectParser setParentPath(String parentPath);
+ ObjectParser setCache(JSONObject cache);
+ JSONObject getCache();
+
+
/**解析成员
* response重新赋值
* @param name
@@ -67,10 +71,11 @@ public interface ObjectParser {
* @param index
* @param key
* @param value
+ * @param cache SQL 结果缓存
* @return
* @throws Exception
*/
- JSON onChildParse(int index, String key, JSONObject value) throws Exception;
+ JSON onChildParse(int index, String key, JSONObject value, JSON cache) throws Exception;
/**解析赋值引用
* @param path
@@ -164,5 +169,4 @@ public interface ObjectParser {
Map> getFunctionMap();
Map getChildMap();
-
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Parser.java b/APIJSONORM/src/main/java/apijson/orm/Parser.java
index 7ed0d1b1c..b2da33477 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Parser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Parser.java
@@ -66,9 +66,9 @@ JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, St
JSONObject getStructure(String table, String method, String tag, int version) throws Exception;
- JSONObject onObjectParse(JSONObject request, String parentPath, String name, SQLConfig arrayConfig, boolean isSubquery) throws Exception;
+ JSONObject onObjectParse(JSONObject request, String parentPath, String name, SQLConfig arrayConfig, boolean isSubquery, JSONObject cache) throws Exception;
- JSONArray onArrayParse(JSONObject request, String parentPath, String name, boolean isSubquery) throws Exception;
+ JSONArray onArrayParse(JSONObject request, String parentPath, String name, boolean isSubquery, JSONArray cache) throws Exception;
/**解析远程函数
* @param key
From e5261ec84c2a6ab1ccf8d666618cc799ebcefb37 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 2 Feb 2025 19:29:14 +0800
Subject: [PATCH 099/184] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20SQL=20JOIN=20?=
=?UTF-8?q?=E5=90=8C=E5=90=8D=E5=89=AF=E8=A1=A8=E8=BF=94=E5=9B=9E=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E9=94=99=E4=B9=B1=EF=BC=8C=E8=A7=A3=E5=86=B3=E6=9C=89?=
=?UTF-8?q?=E6=97=B6=20SELECT=20=E5=92=8C=20ON=20=E4=B8=AD=E7=9A=84?=
=?UTF-8?q?=E8=A1=A8=E5=88=AB=E5=90=8D=E5=AF=B9=E5=BA=94=E9=94=99=E8=AF=AF?=
=?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=20Oracle=20=E6=9F=90=E4=BA=9B?=
=?UTF-8?q?=E6=83=85=E5=86=B5=E4=B8=8B=E7=94=A8=E4=BA=86=E4=B8=8D=E5=85=81?=
=?UTF-8?q?=E8=AE=B8=E7=9A=84=20AS=20=E5=88=AB=E5=90=8D=EF=BC=9B=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=20-=20JOIN=20=E5=89=AF=E8=A1=A8?=
=?UTF-8?q?=E6=9C=AA=E6=8C=87=E5=AE=9A=20@column=20=E4=BC=9A=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E5=86=97=E4=BD=99=E6=9F=A5=E8=AF=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../apijson/orm/AbstractObjectParser.java | 4 +-
.../main/java/apijson/orm/AbstractParser.java | 3 +-
.../java/apijson/orm/AbstractSQLConfig.java | 236 ++++++++----------
.../java/apijson/orm/AbstractSQLExecutor.java | 80 ++++--
.../src/main/java/apijson/orm/Join.java | 9 +
.../src/main/java/apijson/orm/SQLConfig.java | 14 ++
6 files changed, 183 insertions(+), 163 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 8f6494c15..1ed3f1716 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -290,7 +290,7 @@ public AbstractObjectParser parse(String name, boolean isReuse) throws Exception
}
else if (value instanceof JSONObject) { // JSONObject,往下一级提取
if (childMap != null) { // 添加到childMap,最后再解析
- childMap.put(key, (JSONObject)value);
+ childMap.put(key, (JSONObject) value);
}
else { // 直接解析并替换原来的,[]:{} 内必须直接解析,否则会因为丢掉count等属性,并且total@:"/[]/total"必须在[]:{} 后!
JSON cache = index <= 0 || type != TYPE_ITEM || viceItem == null ? null : viceItem.getJSONObject(key);
@@ -457,10 +457,10 @@ else if (value instanceof String) { // //key{}@ getRealKey, 引用赋值路径
if (isTable && (key.startsWith("@") == false || JSONRequest.TABLE_KEY_LIST.contains(key))) {
Log.e(TAG, "onParse isTable && (key.startsWith(@) == false"
+ " || JSONRequest.TABLE_KEY_LIST.contains(key)) >> return null;");
+ // FIXME getCache() != null 时 return true,解决 RIGHT/OUTER/FOREIGN JOIN 主表无数据导致副表数据也不返回
return false; // 获取不到就不用再做无效的 query 了。不考虑 Table:{Table:{}} 嵌套
}
-
Log.d(TAG, "onParse isTable(table) == false >> return true;");
return true; // 舍去,对Table无影响
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 22a984fa5..54eafe97a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1734,7 +1734,7 @@ else if (join != null){
throw new IllegalArgumentException(e.getKey() + ":'/targetTable:targetAlias/targetKey' 中 targetAlias 值 " + targetAlias + " 不合法!必须满足英文单词变量名格式!");
}
- targetTable = targetTableKey; // 主表允许别名
+ //targetTable = targetTableKey; // 主表允许别名
if (StringUtil.isName(targetTable) == false) {
throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中 targetTable 值 " + targetTable + " 不合法!必须满足大写字母开头的表对象英文单词 key 格式!");
}
@@ -1760,6 +1760,7 @@ else if (join != null){
on.setOriginKey(originKey);
on.setOriginValue((String) refEntry.getValue());
+ on.setTargetTableKey(targetTableKey);
on.setTargetTable(targetTable);
on.setTargetAlias(targetAlias);
on.setTargetKey(targetKey);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 6c46d2059..5867b648c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -9,15 +9,8 @@
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.regex.Pattern;
import apijson.JSON;
@@ -1431,10 +1424,6 @@ public String getSQLTable() {
return StringUtil.isEmpty(nt) ? ot : nt;
}
- @JSONField(serialize = false)
- public String getSQLTableWithAlias(String table,String alias) {
- return StringUtil.isEmpty(alias) ? table : table + ":" + alias; // 带上原表名,避免 alias 和其它表名/字段名冲突
- }
@JSONField(serialize = false)
@Override
@@ -1445,7 +1434,7 @@ public String getTablePath() {
String sqlTable = getSQLTable();
return (StringUtil.isEmpty(sch, true) ? "" : q + sch + q + ".") + q + sqlTable + q
- + ( isKeyPrefix() ? " AS " + getAliasWithQuote() : "");
+ + (isKeyPrefix() ? getAs() + q + getSQLAlias() + q : "");
}
@Override
public AbstractSQLConfig setTable(String table) { //Table已经在Parser中校验,所以这里不用防SQL注入
@@ -1453,6 +1442,10 @@ public AbstractSQLConfig setTable(String table) { //Table已经在Parser中校
return this;
}
+ public String getAs() {
+ return isOracle() ? " " : " AS ";
+ }
+
@Override
public String getAlias() {
return alias;
@@ -1462,11 +1455,8 @@ public AbstractSQLConfig setAlias(String alias) {
this.alias = alias;
return this;
}
- public String getAliasWithQuote() {
- String a = getAlias();
- if (StringUtil.isEmpty(a, true)) {
- a = getTable();
- }
+ public String getSQLAliasWithQuote() {
+ String a = getSQLAlias();
String q = getQuote();
// getTable 不能小写,因为Verifier用大小写敏感的名称判断权限
// 如果要强制小写,则可在子类重写这个方法再 toLowerCase
@@ -1502,9 +1492,9 @@ public String getGroupString(boolean hasPrefix) {
if (cfg != null) {
cfg.setMain(false).setKeyPrefix(true);
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ //if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ //}
String c = ((AbstractSQLConfig) cfg).getGroupString(false);
if (StringUtil.isEmpty(c, true) == false) {
@@ -1579,9 +1569,9 @@ public String getHavingString(boolean hasPrefix) throws Exception {
if (cfg != null) {
cfg.setMain(false).setKeyPrefix(true);
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ //if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ //}
String c = ((AbstractSQLConfig) cfg).getHavingString(false);
if (StringUtil.isEmpty(c, true) == false) {
@@ -1610,7 +1600,7 @@ public String getHavingString(boolean hasPrefix) throws Exception {
//fun0(arg0,arg1,...);fun1(arg0,arg1,...)
String havingString = parseCombineExpression(getMethod(), getQuote(), getTable()
- , getAliasWithQuote(), map, getHavingCombine(), true, containRaw, true);
+ , getAlias(), map, getHavingCombine(), true, containRaw, true);
return (hasPrefix ? " HAVING " : "") + StringUtil.concat(havingString, joinHaving, AND);
}
@@ -1693,9 +1683,9 @@ public String getOrderString(boolean hasPrefix) {
if (cfg != null) {
cfg.setMain(false).setKeyPrefix(true);
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ //if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ //}
String c = ((AbstractSQLConfig) cfg).getOrderString(false);
if (StringUtil.isEmpty(c, true) == false) {
@@ -1890,6 +1880,8 @@ public String getColumnString() throws Exception {
@JSONField(serialize = false)
public String getColumnString(boolean inSQLJoin) throws Exception {
List column = getColumn();
+ String as = getAs();
+ String q = getQuote();
switch (getMethod()) {
case HEAD:
@@ -1950,25 +1942,24 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
if (start > 0 && end > start) {
String fun = c0.substring(0, start);
- // Invalid use of group function SELECT count(max(`id`)) AS count FROM `sys`.`Comment`
+ // Invalid use of group function SELECT count(max(`id`)) AS count FROM `sys`.`Comment`
if (SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
String group = getGroup(); // TODO 唯一 100% 兼容的可能只有 SELECT count(*) FROM (原语句) AS table
return StringUtil.isEmpty(group, true) ? "1" : "count(DISTINCT " + group + ")";
}
String[] args = start == end - 1 ? null : StringUtil.split(c0.substring(start + 1, end));
- if (args == null || args.length <= 0) {
- return SQL.count(c0);
+ if (args != null && args.length > 0) {
+ List raw = getRaw();
+ boolean containRaw = raw != null && raw.contains(KEY_COLUMN);
+ c0 = parseSQLExpression(KEY_COLUMN, c0, containRaw, false, null);
}
- List raw = getRaw();
- boolean containRaw = raw != null && raw.contains(KEY_COLUMN);
-
- return SQL.count(parseSQLExpression(KEY_COLUMN, c0, containRaw, false, null));
+ return "count(" + c0 + ")" + as + q + JSONResponse.KEY_COUNT + q;
}
}
- return SQL.count(onlyOne ? getKey(c0) : "*");
+ return "count(" + (onlyOne ? getKey(c0) : "*") + ")" + as + q + JSONResponse.KEY_COUNT + q;
// return SQL.count(onlyOne && StringUtil.isName(column.get(0)) ? getKey(column.get(0)) : "*");
case POST:
if (column == null || column.isEmpty()) {
@@ -1993,30 +1984,28 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
String joinColumn = "";
if (joinList != null) {
boolean first = true;
- for (Join j : joinList) {
- if (j.isAppJoin()) {
+ for (Join join : joinList) {
+ if (join.isAppJoin()) {
continue;
}
- SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig ocfg = join.getOuterConfig();
boolean isEmpty = ocfg == null || ocfg.getColumn() == null;
- boolean isLeftOrRightJoin = j.isLeftOrRightJoin();
+ boolean isLeftOrRightJoin = join.isLeftOrRightJoin();
if (isEmpty && isLeftOrRightJoin) {
// 改为 SELECT ViceTable.* 解决 SELECT sum(ViceTable.id)
// LEFT/RIGHT JOIN (SELECT sum(id) FROM ViceTable...) AS ViceTable
// 不仅导致 SQL 函数重复计算,还有时导致 SQL 报错或对应字段未返回
- String quote = getQuote();
- joinColumn += (first ? "" : ", ") + quote + (StringUtil.isEmpty(j.getAlias(), true)
- ? j.getTable() : j.getAlias()) + quote + ".*";
+ joinColumn += (first ? "" : ", ") + q + SQLConfig.getSQLAlias(join.getTable(), join.getAlias()) + q + ".*";
first = false;
} else {
- SQLConfig cfg = isLeftOrRightJoin == false && isEmpty ? j.getJoinConfig() : ocfg;
+ SQLConfig cfg = isLeftOrRightJoin == false && isEmpty ? join.getJoinConfig() : ocfg;
if (cfg != null) {
cfg.setMain(false).setKeyPrefix(true);
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ //if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ //}
String c = ((AbstractSQLConfig) cfg).getColumnString(true);
if (StringUtil.isEmpty(c, true) == false) {
@@ -2030,9 +2019,8 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
}
}
- String tableAlias = getAliasWithQuote();
-
- // String c = StringUtil.getString(column); //id,name;json_length(contactIdList):contactCount;...
+ String tableAlias = q + getSQLAlias() + q;
+ // String c = StringUtil.getString(column); //id,name;json_length(contactIdList):contactCount;...
String[] keys = column == null ? null : column.toArray(new String[]{}); //StringUtil.split(c, ";");
if (keys == null || keys.length <= 0) {
@@ -2043,7 +2031,6 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
return StringUtil.concat(mc, joinColumn, ", ", true);
}
-
List raw = getRaw();
boolean containRaw = raw != null && raw.contains(KEY_COLUMN);
@@ -2062,7 +2049,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
boolean hasAlias = StringUtil.isName(alias);
String pre = index > 0 && hasAlias ? expression.substring(0, index) : expression;
if (RAW_MAP.containsValue(pre) || "".equals(RAW_MAP.get(pre))) { // newSQLConfig 提前处理好的
- keys[i] = pre + (hasAlias ? " AS " + alias : "");
+ keys[i] = pre + (hasAlias ? getAs() + q + alias + q : "");
continue;
}
}
@@ -2197,7 +2184,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
}
String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "") + StringUtil.getString(ckeys) + ")" + suffix;
- expression = origin + (StringUtil.isEmpty(alias, true) ? "" : " AS " + quote + alias + quote);
+ expression = origin + (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
else {
//是窗口函数 fun(arg0,agr1) OVER (agr0 agr1 ...)
@@ -2255,7 +2242,7 @@ else if (SQL_FUNCTION_MAP.containsKey(fun) == false) {
String argsString2[] = parseArgsSplitWithComma(argString2, false, containRaw, allowAlias);
expression = fun + "(" + StringUtil.getString(agrsString1) + (containOver ? ") OVER (" : ") AGAINST (")
+ StringUtil.getString(argsString2) + ")" + suffix // 传参不传空格,拼接带空格
- + (StringUtil.isEmpty(alias, true) ? "" : " AS " + quote + alias + quote);
+ + (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
}
@@ -2274,7 +2261,7 @@ private String[] parseArgsSplitWithComma(String param, boolean isColumn, boolean
// 以"," 分割参数
String quote = getQuote();
boolean isKeyPrefix = isKeyPrefix();
- String tableAlias = getAliasWithQuote();
+ String tableAlias = quote + getSQLAlias() + quote;
String ckeys[] = StringUtil.split(param); // 以","分割参数
if (ckeys != null && ckeys.length > 0) {
@@ -2385,7 +2372,7 @@ else if ("!=null".equals(ck)) {
}
if (isColumn && StringUtil.isEmpty(alias, true) == false) {
- origin += " AS " + quote + alias + quote;
+ origin += getAs() + quote + alias + quote;
}
}
}
@@ -2408,7 +2395,7 @@ else if ("!=null".equals(ck)) {
private String parseArgsSplitWithSpace(String mkes[]) {
String quote = getQuote();
boolean isKeyPrefix = isKeyPrefix();
- String tableAlias = getAliasWithQuote();
+ String tableAlias = quote + getSQLAlias() + quote;
// 包含空格的参数 肯定不包含别名 不用处理别名
if (mkes != null && mkes.length > 0) {
@@ -2988,8 +2975,7 @@ public String getWhereString(boolean hasPrefix) throws Exception {
@JSONField(serialize = false)
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where
, String combine, List joinList, boolean verifyName) throws Exception {
-
- String whereString = parseCombineExpression(method, getQuote(), getTable(), getAliasWithQuote()
+ String whereString = parseCombineExpression(method, getQuote(), getTable(), getAlias()
, where, combine, verifyName, false, false);
whereString = concatJoinWhereString(whereString);
String result = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
@@ -3680,7 +3666,7 @@ else if (isTest()) {
}
public String getSQLKey(String key) {
String q = getQuote();
- return (isKeyPrefix() ? getAliasWithQuote() + "." : "") + q + key + q;
+ return (isKeyPrefix() ? q + getSQLAlias() + q + "." : "") + q + key + q;
}
/**
@@ -4327,11 +4313,12 @@ private String withAsExpreSubqueryString(SQLConfig cfg, Subquery subquery) throw
}
String quote = getQuote();
+ String as = getAs();
String withAsExpreSql;
if (list != null) {
String withQuoteName = quote + subquery.getKey() + quote;
- list.add(" " + withQuoteName + " AS " + "(" + cfg.getSQL(isPrepared()) + ") ");
+ list.add(" " + withQuoteName + as + "(" + cfg.getSQL(isPrepared()) + ") ");
withAsExpreSql = " SELECT * FROM " + withQuoteName;
// 预编译参数 FIXME 这里重复添加了,导致子查询都报错参数超过 ? 数量 Parameter index out of range (5 > number of parameters, which is 4)
@@ -4350,7 +4337,7 @@ private String withAsExpreSubqueryString(SQLConfig cfg, Subquery subquery) throw
withAsExpreSql = cfg.getSQL(isPrepared());
// mysql 才存在这个问题, 主表和子表是一张表
if (isWithAsEnable && isMySQL() && StringUtil.equals(getTable(), subquery.getFrom())) {
- withAsExpreSql = " SELECT * FROM (" + withAsExpreSql + ") AS " + quote + subquery.getKey() + quote;
+ withAsExpreSql = " SELECT * FROM (" + withAsExpreSql + ")" + as + quote + subquery.getKey() + quote;
}
}
@@ -4629,7 +4616,7 @@ public static String getSQL(AbstractSQLConfig config) throws Exception {
if (config.isTest() && RequestMethod.isGetMethod(config.getMethod(), true)) { // FIXME 为啥是 code 而不是 count ?
String q = config.getQuote(); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0
return explain + "SELECT " + config.getWhereString(false)
- + " AS " + q + JSONResponse.KEY_COUNT + q + config.getLimitString();
+ + config.getAs() + q + JSONResponse.KEY_COUNT + q + config.getLimitString();
}
config.setPreparedValueList(new ArrayList());
@@ -4690,8 +4677,8 @@ protected String getOraclePageSql(String sql) {
return sql;
}
int offset = getOffset(getPage(), count);
- String alias = getAliasWithQuote();
String quote = getQuote();
+ String alias = quote + getSQLAlias() + quote;
return "SELECT * FROM (SELECT " + alias + ".*, ROWNUM "+ quote + "RN" + quote +" FROM (" + sql + ") " + alias
+ " WHERE ROWNUM <= " + (offset + count) + ") WHERE "+ quote + "RN" + quote +" > " + offset;
}
@@ -4705,7 +4692,7 @@ protected String getOraclePageSql(String sql) {
private static String getConditionString(String table, AbstractSQLConfig config) throws Exception {
Subquery from = config.getFrom();
if (from != null) {
- table = config.getSubqueryString(from) + " AS " + config.getAliasWithQuote() + " ";
+ table = config.getSubqueryString(from) + config.getAs() + config.getSQLAliasWithQuote() + " ";
}
String join = config.getJoinString();
@@ -4803,7 +4790,7 @@ public String getJoinString() throws Exception {
jc.setPrepared(isPrepared());
// 将关联表所属数据源配置为主表数据源
jc.setDatasource(this.getDatasource());
- String jt = StringUtil.isEmpty(jc.getAlias(), true) ? jc.getTable() : jc.getAlias();
+ String jt = jc.getSQLAlias();
List onList = j.getOnList();
//如果要强制小写,则可在子类重写这个方法再 toLowerCase
@@ -4824,7 +4811,7 @@ public String getJoinString() throws Exception {
case ">": // RIGHT JOIN
jc.setMain(true).setKeyPrefix(false);
sql = ( "<".equals(type) ? " LEFT" : (">".equals(type) ? " RIGHT" : " CROSS") )
- + " JOIN ( " + jc.getSQL(isPrepared()) + " ) AS " + quote + jt + quote;
+ + " JOIN ( " + jc.getSQL(isPrepared()) + " ) " + getAs() + quote + jt + quote;
sql = concatJoinOn(sql, quote, j, jt, onList);
jc.setMain(false).setKeyPrefix(true);
@@ -4844,9 +4831,10 @@ public String getJoinString() throws Exception {
sql = concatJoinOn(sql, quote, j, jt, onList);
break;
default:
+ String k = jc.getTableKey();
throw new UnsupportedOperationException(
- "join:value 中 value 里的 " + jt + "/" + j.getPath()
- + "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
+ "join:value 中 value 里的 " + k + "/" + j.getPath()
+ + "错误!不支持 " + k + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
+ ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
);
}
@@ -4881,45 +4869,42 @@ public String getJoinString() throws Exception {
}
- protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNull Join j, @NotNull String jt, List onList) {
+ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNull Join join, @NotNull String jt, List onList) {
if (onList != null) {
+ SQLConfig jc = join.getJoinConfig();
+ Map castMap = jc == null ? null : jc.getCast();
+
boolean first = true;
for (On on : onList) {
Logic logic = on.getLogic();
boolean isNot = logic == null ? false : logic.isNot();
if (isNot) {
- onJoinNotRelation(sql, quote, j, jt, onList, on);
+ onJoinNotRelation(sql, quote, join, jt, onList, on);
+ }
+
+ String lk = quote + jt + quote + "." + quote + on.getKey() + quote;
+ Object ct = castMap == null ? null : castMap.get(on.getOriginKey());
+ if (StringUtil.isNotEmpty(ct, false)) {
+ lk = "cast(" + lk + " AS " + ct + ")"; // 解决 JOIN ON 不支持 @cast 问题,CAST(expression AS TYPE) 中 AS 不能省略
}
String rt = on.getRelateType();
+
+ String rk = quote + SQLConfig.getSQLAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+
if (StringUtil.isEmpty(rt, false)) {
- // 解决 JOIN ON 不支持 @cast 问题
- SQLConfig jc = j.getJoinConfig();
- Map castMap = jc == null ? null : jc.getCast();
- if (castMap == null || castMap.isEmpty()) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? " != " : " = ")
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
- } else {
- String leftTableRelationSql = quote + jt + quote + "." + quote + on.getKey() + quote;
- Object castValueType = castMap.get(on.getOriginKey());
- if (castValueType != null) {
- leftTableRelationSql = "CAST(" + leftTableRelationSql + " AS " + castValueType + ")";
- }
- sql += (first ? ON : AND) + leftTableRelationSql + (isNot ? " != " : " = ")
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
- }
+ sql += (first ? ON : AND) + lk + (isNot ? " != " : " = ") + rk;
}
else {
- onJoinComplexRelation(sql, quote, j, jt, onList, on);
+ onJoinComplexRelation(sql, quote, join, jt, onList, on);
if (">=".equals(rt) || "<=".equals(rt) || ">".equals(rt) || "<".equals(rt)) {
if (isNot) {
- throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + join.getPath()
+ " 中 JOIN ON 条件关联逻辑符 " + rt + " 不合法! >, <, >=, <= 不支持与或非逻辑符 & | ! !");
}
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " " + rt + " "
- + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ sql += (first ? ON : AND) + lk + " " + rt + " " + rk;
}
else if (rt.endsWith("$")) {
String t = rt.substring(0, rt.length() - 1);
@@ -4964,52 +4949,38 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
}
if (l <= 0 && r <= 0) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " LIKE " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " LIKE " + rk;
}
else {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote
- + "." + quote + on.getTargetKey() + quote + (r <= 0 ? ")" : ", '" + r + "')");
+ sql += (first ? ON : AND) + lk + (isNot ? NOT : "")
+ + (l <= 0 ? " LIKE concat(" : " LIKE concat('" + l + "', ") + rk + (r <= 0 ? ")" : ", '" + r + "')");
}
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
if (isPostgreSQL() || isInfluxDB()) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote
- + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ")
- + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ") + rk;
}
else if (isOracle() || isDameng() || isKingBase()) {
- sql += (first ? ON : AND) + "regexp_like(" + quote + jt + quote + "." + quote + on.getKey() + quote
- + ", " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote
- + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
+ sql += (first ? ON : AND) + "regexp_like(" + lk + ", " + rk + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
else if (isPresto() || isTrino()) {
- sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + quote
- + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
- + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
+ sql += (first ? ON : AND) + "regexp_like(" + (ignoreCase ? "lower(" : "") + lk + (ignoreCase ? ")" : "")
+ + ", " + (ignoreCase ? "lower(" : "") + rk + (ignoreCase ? ")" : "") + ")";
}
else if (isClickHouse()) {
- sql += (first ? ON : AND) + "match(" + (ignoreCase ? "lower(" : "") + quote + jt
- + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + ", " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
- + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
+ sql += (first ? ON : AND) + "match(" + (ignoreCase ? "lower(" : "") + lk + (ignoreCase ? ")" : "")
+ + ", " + (ignoreCase ? "lower(" : "") + rk + (ignoreCase ? ")" : "") + ")";
}
else if (isElasticsearch()) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " RLIKE " + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " RLIKE " + rk;
}
else if (isHive()) {
- sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
- + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias())
- + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "");
+ sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + lk + (ignoreCase ? ")" : "")
+ + " REGEXP " + (ignoreCase ? "lower(" : "") + rk + (ignoreCase ? ")" : "");
}
else {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " REGEXP " + (ignoreCase ? "" : "BINARY ")
- + quote + getSQLTableWithAlias(on.getTargetTable(), on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
+ sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " REGEXP " + (ignoreCase ? "" : "BINARY ") + rk;
}
}
else if ("{}".equals(rt) || "<>".equals(rt)) {
@@ -5017,13 +4988,13 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String ta = on.getTargetAlias();
Map cast = null;
- if (tt.equals(getTable()) && ((ta == null && getAlias() == null) || ta.equals(getAlias()))) {
+ if (tt.equals(getTable()) && Objects.equals(ta, getAlias())) {
cast = getCast();
}
else {
boolean find = false;
for (Join jn : joinList) {
- if (tt.equals(jn.getTable()) && ((ta == null && jn.getAlias() == null) || ta.equals(jn.getAlias()))) {
+ if (tt.equals(jn.getTable()) && Objects.equals(ta, jn.getAlias())) {
cast = getCast();
find = true;
break;
@@ -5031,23 +5002,16 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
}
if (find == false) {
- throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + join.getPath()
+ " 中 JOIN ON 条件中找不到对应的 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
}
}
boolean isBoolOrNum = SQL.isBooleanOrNumber(cast == null ? null : cast.get(on.getTargetKey()));
- String arrKeyPath;
- String itemKeyPath;
- if ("{}".equals(rt)) {
- arrKeyPath = quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
- itemKeyPath = quote + jt + quote + "." + quote + on.getKey() + quote;
- }
- else {
- arrKeyPath = quote + jt + quote + "." + quote + on.getKey() + quote;
- itemKeyPath = quote + getSQLTableWithAlias(on.getTargetTable(),on.getTargetAlias()) + quote + "." + quote + on.getTargetKey() + quote;
- }
+ boolean isIn = "{}".equals(rt);
+ String arrKeyPath = isIn ? rk : lk;
+ String itemKeyPath = isIn ? lk : rk;
if (isPostgreSQL() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
@@ -5073,12 +5037,12 @@ else if (isClickHouse()) {
+ " IS NOT NULL AND json_contains(" + arrKeyPath
+ (isBoolOrNum ? ", cast(" + itemKeyPath + " AS CHAR), '$')"
: ", concat('\"', " + itemKeyPath + ", '\"'), '$')"
- )
- ) + (isNot ? ") " : "");
+ )
+ ) + (isNot ? ") " : "");
}
}
else {
- throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + join.getPath()
+ " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, >, <, >=, <=, !=, $, ~, {}, <> 这几种!");
}
}
@@ -5953,13 +5917,13 @@ else if (joinConfig.getDatabase().equals(config.getDatabase()) == false) {
joinConfig.setMain(false).setKeyPrefix(true);
if (j.getOuter() != null) {
- SQLConfig outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
- outterConfig.setMain(false)
+ SQLConfig outerConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
+ outerConfig.setMain(false)
.setKeyPrefix(true)
.setDatabase(joinConfig.getDatabase())
.setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
- j.setOuterConfig(outterConfig);
+ j.setOuterConfig(outerConfig);
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index fd708f9b8..84b30437e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -410,6 +410,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
JSONObject viceItem = null;
JSONObject curItem = item;
boolean isMain = true;
+ boolean reseted = false;
for (int i = 1; i <= length; i++) {
@@ -437,20 +438,31 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
if (toFindJoin) { // 在主表字段数量内的都归属主表
long startTime3 = System.currentTimeMillis();
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
- sqlResultDuration += System.currentTimeMillis() - startTime3;
- if (StringUtil.isEmpty(sqlTable, true)) {
- boolean isEmpty = curItem == null || curItem.isEmpty();
- String label = isEmpty ? null : getKey(config, rs, rsmd, index, curItem, i, childMap);
- if (isEmpty || curItem.containsKey(label) == false) { // 重复字段几乎肯定不是一张表的,尤其是主副表同名主键 id
- sqlTable = i <= 1 ? config.getSQLTable() : lastTableName; // Presto 等引擎 JDBC 返回 rsmd.getTableName(i) 为空,主表如果一个字段都没有会导致 APISJON 主副表所有字段都不返回
- }
- }
+ //if (StringUtil.isEmpty(sqlTable, true)) {
+ // boolean isEmpty = curItem == null || curItem.isEmpty();
+ String label = getKey(config, rs, rsmd, index, curItem, i, childMap);
+ if (i > 1 && ( (curItem != null && curItem.containsKey(label))
+ || (StringUtil.isNotEmpty(label) && StringUtil.equals(label, curConfig == null ? null : curConfig.getIdKey())))
+ ) { // Presto 等引擎 JDBC 返回 rsmd.getTableName(i) 为空,主表如果一个字段都没有会导致 APISJON 主副表所有字段都不返回
+ sqlTable = null;
+ if (reseted) {
+ lastViceTableStart ++;
+
+ SQLConfig lastCfg = lastJoin == null ? null : lastJoin.getCacheConfig();
+ List lastColumn = lastCfg == null ? null : lastCfg.getColumn();
+ lastViceColumnStart += lastColumn == null ? 1 : lastColumn.size();
+ }
+ reseted = true;
+ }
+ //}
+ sqlResultDuration += System.currentTimeMillis() - startTime3;
if (StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
- for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
+ int joinCount = joinList.size();
+ for (int j = lastViceTableStart; j < joinCount; j++) { // 查找副表 @column,定位字段所在表
Join join = joinList.get(j);
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
List c = cfg == null ? null : cfg.getColumn();
@@ -461,7 +473,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
&& StringUtil.equals(sqlAlias, lastAliasName) ? 1 : 0
)
);
- if (i < nextViceColumnStart) {
+ if (i < nextViceColumnStart || j >= joinCount - 1) {
sqlTable = cfg.getSQLTable();
sqlAlias = cfg.getAlias();
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
@@ -539,17 +551,17 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
columnIndexAndJoinMap[i - 1] = curJoin;
}
- //boolean isVice = false;
- //String viceName = null;
-
// 如果是主表则直接用主表对应的 item,否则缓存副表数据到 childMap
Join prevJoin = columnIndexAndJoinMap == null || i < 2 ? null : columnIndexAndJoinMap[i - 2];
if (curJoin != prevJoin) { // 前后字段不在同一个表对象,即便后面出现 null,也不该是主表数据,而是逻辑 bug 导致
SQLConfig viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;
+ boolean hasPK = false;
if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
List onList = curJoin.getOnList();
int size = onList == null ? 0 : onList.size();
if (size > 0) {
+ String idKey = viceConfig.getIdKey();
+ String tblKey = config.getTableKey();
for (int j = size - 1; j >= 0; j--) {
On on = onList.get(j);
String ok = on == null ? null : on.getOriginKey();
@@ -557,34 +569,54 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
throw new NullPointerException("服务器内部错误,List 中 Join.onList[" + j + (on == null ? "] = null!" : ".getOriginKey() = null!"));
}
- viceConfig.putWhere(ok.substring(0, ok.length() - 1), item.get(on.getTargetKey()), true);
+ String k = ok.substring(0, ok.length() - 1);
+ String ttk = on.getTargetTableKey();
+
+ JSONObject target = StringUtil.equals(ttk, tblKey) ? item : (viceItem == null ? null : viceItem.getJSONObject(ttk));
+ Object v = target == null ? null : target.get(on.getTargetKey());
+ hasPK = hasPK || (k.equals(idKey) && v != null);
+
+ viceConfig.putWhere(k, v, true);
}
}
}
- String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
-
- if (StringUtil.isEmpty(viceSql, true)) {
- Log.i(TAG, "execute StringUtil.isEmpty(viceSql, true) >> item = null; >> ");
+ if (viceConfig == null) { // StringUtil.isEmpty(viceSql, true)) {
+ Log.i(TAG, "execute viceConfig == null >> item = null; >> ");
curItem = null;
}
else if (curJoin.isOuterJoin() || curJoin.isAntiJoin()) {
Log.i(TAG, "execute curJoin.isOuterJoin() || curJoin.isAntiJoin() >> item = null; >> ");
curItem = null; // 肯定没有数据,缓存也无意义
- // 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 childMap.put(viceSql, new JSONObject()); // 缓存固定空数据,避免后续多余查询
+ // 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 childMap.put(viceSql, new JSONObject()); // 缓存固定空数据,避免后续多余查询
}
else {
- //isVice = true;
- String viceName = viceConfig.getTable() + (StringUtil.isEmpty(viceConfig.getAlias()) ? "" : ":" + StringUtil.isEmpty(viceConfig.getAlias()));
+ String viceName = viceConfig.getTableKey();
if (viceItem == null) {
viceItem = new JSONObject(true);
}
curItem = viceItem.getJSONObject(viceName);
- //curItem = childMap.get(viceSql);
- if (curItem == null) {
+
+ String viceSql = hasPK ? viceConfig.getSQL(false) : null; // TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
+ JSONObject curCache = hasPK ? childMap.get(viceSql) : null;
+
+ if (curItem == null || curItem.isEmpty()) {
+ // 导致前面判断重复 key 出错 curItem = curCache != null ? curCache : new JSONObject(true);
curItem = new JSONObject(true);
- //childMap.put(viceSql, curItem);
viceItem.put(viceName, curItem);
+ if (hasPK && curCache == null) {
+ childMap.put(viceSql, curItem);
+ }
+ }
+ else if (hasPK) {
+ if (curCache == null || curCache.isEmpty()) {
+ childMap.put(viceSql, curItem);
+ }
+ else {
+ curCache.putAll(curItem);
+ // 导致前面判断重复 key 出错 curItem = curCache;
+ // viceItem.put(viceName, curItem);
+ }
}
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index 68d7e8344..f648ff8bf 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -181,6 +181,7 @@ public static class On {
private Logic logic; // & | !
private String relateType; // "" - 一对一, "{}" - 一对多, "<>" - 多对一, > , <= , !=
private String key; // id
+ private String targetTableKey; // Moment:main
private String targetTable; // Moment
private String targetAlias; // main
private String targetKey; // userId
@@ -218,6 +219,14 @@ public String getKey() {
public void setKey(String key) {
this.key = key;
}
+
+ public void setTargetTableKey(String targetTableKey) {
+ this.targetTableKey = targetTableKey;
+ }
+ public String getTargetTableKey() {
+ return targetTableKey;
+ }
+
public void setTargetTable(String targetTable) {
this.targetTable = targetTable;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 8e5755f89..641212708 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -345,6 +345,19 @@ default int[] getDBVersionNums() {
SQLConfig setAlias(String alias);
+ default String getTableKey() {
+ String alias = getAlias();
+ return getTable() + (StringUtil.isEmpty(alias) ? "" : ":" + alias);
+ }
+
+ default String getSQLAlias() {
+ return getSQLAlias(getTable(), getAlias());
+ }
+ static String getSQLAlias(@NotNull String table, String alias) {
+ return StringUtil.isEmpty(alias) ? table : table + "_" + alias; // 带上原表名,避免 alias 和其它表名/字段名冲突
+ }
+
+
String getWhereString(boolean hasPrefix) throws Exception;
String getRawSQL(String key, Object value) throws Exception;
@@ -374,4 +387,5 @@ default int[] getDBVersionNums() {
boolean isFakeDelete();
Map onFakeDelete(Map map);
+
}
From 324fe328631077018078fa53c4934cfbd39d3ac5 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 2 Feb 2025 19:57:51 +0800
Subject: [PATCH 100/184] =?UTF-8?q?=E4=BC=98=E5=8C=96=20JOIN=20=E4=B8=AD?=
=?UTF-8?q?=E7=9A=84=E5=89=AF=E8=A1=A8=E5=88=AB=E5=90=8D=EF=BC=8C=E7=94=A8?=
=?UTF-8?q?=20=5F=5F=20=E5=8F=8C=E4=B8=8B=E5=88=92=E7=BA=BF=E6=9B=BF?=
=?UTF-8?q?=E4=BB=A3=E5=8E=9F=E6=9D=A5=E7=9A=84=20=5F=20=E5=8D=95=E4=B8=8B?=
=?UTF-8?q?=E5=88=92=E7=BA=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 641212708..60f2ddcdc 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -354,7 +354,8 @@ default String getSQLAlias() {
return getSQLAlias(getTable(), getAlias());
}
static String getSQLAlias(@NotNull String table, String alias) {
- return StringUtil.isEmpty(alias) ? table : table + "_" + alias; // 带上原表名,避免 alias 和其它表名/字段名冲突
+ // 这里不用 : $ 等符号,因为部分数据库/引擎似乎不支持 `key`, "key", [key] 等避免关键词冲突的方式,只能使用符合变量命名的表别名
+ return StringUtil.isEmpty(alias) ? table : table + "__" + alias; // 带上原表名,避免 alias 和其它表名/字段名冲突
}
From 84edf9372bc3316a100d916146f819377d7ede23 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 2 Feb 2025 20:02:44 +0800
Subject: [PATCH 101/184] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 3484638cb..1c0b97a5d 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.4.0";
+ public static final String VERSION = "7.4.2";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From 448f31ddf77061ce62c984d5cca3d031648ad2bf Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 9 Feb 2025 16:54:49 +0800
Subject: [PATCH 102/184] =?UTF-8?q?Maven=20pom.xml=20=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E6=94=B9=E4=B8=BA=207.4.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index b301c6d95..e19551a17 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.4.0
+ 7.4.2
jar
APIJSONORM
From 31e8d7290378e6257fd48eb861a6126dff4d99dd Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 9 Feb 2025 16:55:23 +0800
Subject: [PATCH 103/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=8D=8E=E4=B8=BA=20openGauss-=E9=AB=98=E6=96=AF=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=BC=80=E6=BA=90=E7=89=88=EF=BC=8C=E9=AB=98?=
=?UTF-8?q?=E5=8F=AF=E7=94=A8=E3=80=81=E9=AB=98=E6=80=A7=E8=83=BD=E3=80=81?=
=?UTF-8?q?=E9=AB=98=E5=AE=89=E5=85=A8=E3=80=81=E9=AB=98=E5=BC=B9=E6=80=A7?=
=?UTF-8?q?=E3=80=81=E9=AB=98=E6=99=BA=E8=83=BD=E3=80=81=E6=98=93=E9=83=A8?=
=?UTF-8?q?=E7=BD=B2=E3=80=81=E6=98=93=E8=BF=81=E7=A7=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/issues/795
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 9 +++++++++
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
4 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index e19551a17..2f71df253 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.4.2
+ 7.5.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 1c0b97a5d..e9315e224 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "7.4.2";
+ public static final String VERSION = "7.5.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 5867b648c..573ee0b1e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -200,6 +200,7 @@ public abstract class AbstractSQLConfig implements SQLConfig(); // 保证顺序,避免配置冲突等意外情况
@@ -1322,6 +1323,14 @@ public static boolean isSurrealDB(String db) {
return DATABASE_SURREALDB.equals(db);
}
+ @Override
+ public boolean isOpenGauss() {
+ return isOpenGauss(getSQLDatabase());
+ }
+ public static boolean isOpenGauss(String db) {
+ return DATABASE_OPENGAUSS.equals(db);
+ }
+
@Override
public String getQuote() { // MongoDB 同时支持 `tbl` 反引号 和 "col" 双引号
if(isElasticsearch() || isIoTDB() || isSurrealDB()) {
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 60f2ddcdc..61233313d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -45,6 +45,7 @@ public interface SQLConfig {
String DATABASE_SQLITE = "SQLITE"; // https://www.sqlite.org
String DATABASE_DUCKDB = "DUCKDB"; // https://duckdb.org
String DATABASE_SURREALDB = "SURREALDB"; // https://surrealdb.com
+ String DATABASE_OPENGAUSS = "OPENGAUSS"; // https://surrealdb.com
String DATABASE_MQ = "MQ"; //
@@ -101,6 +102,7 @@ public interface SQLConfig {
boolean isSQLite();
boolean isDuckDB();
boolean isSurrealDB();
+ boolean isOpenGauss();
// 暂时只兼容以上几种
From a1e4085407939ffb2872ff5ebdb8704ddf48aaf9 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 9 Feb 2025 17:32:48 +0800
Subject: [PATCH 104/184] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8D=8E=E4=B8=BA=20?=
=?UTF-8?q?openGauss,=20=E6=B8=85=E5=8D=8E=20IoTDB,=20DuckDB,=20SurrealDB,?=
=?UTF-8?q?=20Kingbase=20=E7=9A=84=E6=94=AF=E6=8C=81=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=8A=E5=BF=AB=E6=8D=B7=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON?tab=readme-ov-file#--apijson
---
README.md | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 5ccf60161..e373f91b5 100644
--- a/README.md
+++ b/README.md
@@ -20,25 +20,31 @@ This source code is licensed under the Apache License Version 2.0
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
From 7fe5c0701bd21ded3133a8c55cfa1b67e7dee64e Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Feb 2025 22:41:25 +0800
Subject: [PATCH 105/184] =?UTF-8?q?=E6=89=93=E5=8C=85=E7=94=A8=E7=9A=84=20?=
=?UTF-8?q?JDK=2017=20=E6=94=B9=E4=B8=BA=201.8=EF=BC=8C=E5=85=BC=E5=AE=B9?=
=?UTF-8?q?=E4=BD=8E=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
APIJSONORM/jitpack.yml | 6 ------
APIJSONORM/pom.xml | 12 ++++++------
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../src/main/java/apijson/orm/AbstractParser.java | 2 +-
5 files changed, 9 insertions(+), 14 deletions(-)
delete mode 100644 APIJSONORM/jitpack.yml
diff --git a/.gitignore b/.gitignore
index f4b1dd878..a06bbd0a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ build/
### VS Code ###
.vscode/
APIJSONORM/bin
+*.DS_Store
diff --git a/APIJSONORM/jitpack.yml b/APIJSONORM/jitpack.yml
deleted file mode 100644
index 9e42c425a..000000000
--- a/APIJSONORM/jitpack.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-jdk:
- - openjdk17
-
-before_install:
- - sdk install java 17.0.6-open
- - sdk use java 17.0.6-open
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 2f71df253..30a3f9444 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.5.0
+ 7.5.5
jar
APIJSONORM
@@ -14,10 +14,10 @@
UTF-8
UTF-8
- 17
+ 1.8
UTF-8
- 17
- 17
+ 1.8
+ 1.8
@@ -35,8 +35,8 @@
maven-compiler-plugin
3.12.1
- 17
- 17
+ 1.8
+ 1.8