From c34ee0fc366746de70fbbb2a83a27bfbb0af9a10 Mon Sep 17 00:00:00 2001
From: Bill <1594805355@qq.com>
Date: Tue, 21 Nov 2023 15:40:53 +0800
Subject: [PATCH 001/145] =?UTF-8?q?=E5=8D=87=E7=BA=A7jdk?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index b29887e04..22a4bb627 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.github.Tencent
- APIJSON
+ APIJSON-spring-boot3
6.2.0
jar
@@ -14,7 +14,7 @@
UTF-8
UTF-8
- 1.8
+ 17
@@ -32,8 +32,8 @@
maven-compiler-plugin
3.8.1
- 1.8
- 1.8
+ 17
+ 17
From 8f6b8293fc74c219159b2cc2900600d3589591c4 Mon Sep 17 00:00:00 2001
From: Bill <1594805355@qq.com>
Date: Wed, 22 Nov 2023 15:35:11 +0800
Subject: [PATCH 002/145] =?UTF-8?q?maven=20=E7=BC=96=E8=AF=91jdk=20?=
=?UTF-8?q?=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 22a4bb627..f656d471b 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -15,6 +15,8 @@
UTF-8
UTF-8
17
+ 17
+ 17
From 984450646a65e88f4f08bfe8fd565970525c2416 Mon Sep 17 00:00:00 2001
From: kom <102006886+komiblog@users.noreply.github.com>
Date: Tue, 23 Jan 2024 00:15:26 +0800
Subject: [PATCH 003/145] Update pom.xml
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
修改pom文件,以使其编译成功
---
APIJSONORM/pom.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index f3d633760..489e98f55 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -15,6 +15,7 @@
UTF-8
UTF-8
17
+ UTF-8
17
17
From 6b9c6f3f4a21a6276b367fd908bbe181b79e7bd6 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 23 Jan 2024 22:38:19 +0800
Subject: [PATCH 004/145] Create jitpack.yml
---
jitpack.yml | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 jitpack.yml
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 000000000..9e42c425a
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,6 @@
+jdk:
+ - openjdk17
+
+before_install:
+ - sdk install java 17.0.6-open
+ - sdk use java 17.0.6-open
From 29516bd02b1c4acf51e789da32155de56b746402 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 23 Jan 2024 22:38:43 +0800
Subject: [PATCH 005/145] Create jitpack.yml
---
APIJSONORM/jitpack.yml | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 APIJSONORM/jitpack.yml
diff --git a/APIJSONORM/jitpack.yml b/APIJSONORM/jitpack.yml
new file mode 100644
index 000000000..9e42c425a
--- /dev/null
+++ b/APIJSONORM/jitpack.yml
@@ -0,0 +1,6 @@
+jdk:
+ - openjdk17
+
+before_install:
+ - sdk install java 17.0.6-open
+ - sdk use java 17.0.6-open
From 268535cf04607679db8e8c5d876de8aa2e267592 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 24 Jan 2024 22:47:58 +0800
Subject: [PATCH 006/145] Update pom.xml
---
APIJSONORM/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 489e98f55..1a2f7f00b 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON-spring-boot3
- 6.4.0
+ 6.4.3
jar
APIJSONORM
From c0c4af34af3bb93c96d1854d82e2a2dcbee64b36 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 24 Jan 2024 22:48:15 +0800
Subject: [PATCH 007/145] Update Log.java
---
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 bc1275947..a02013311 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.0";
+ public static final String VERSION = "6.4.3";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From cae91c4f0e150312f6bcfbbabd2acb68e69e8023 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 30 Jan 2024 18:36:13 +0800
Subject: [PATCH 008/145] =?UTF-8?q?GitHub=2016K+=20Star=20=E5=9C=A8=20400W?=
=?UTF-8?q?=C2=A0Java=20=E9=A1=B9=E7=9B=AE=E6=8E=92=E5=90=8D=E5=89=8D=2010?=
=?UTF-8?q?0=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?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 fc9b199a2..1809e4b06 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 15.6K Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **社区影响力大** (GitHub 16K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前八、腾讯后端 Star 第一、GitHub Java 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
From 6fa69a3c362804acfb09415be30ef2b49fb80beb Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 4 Feb 2024 20:45:17 +0800
Subject: [PATCH 009/145] =?UTF-8?q?=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=E8=BF=91=207=20?=
=?UTF-8?q?=E5=B9=B4=EF=BC=8C60+=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 1809e4b06..13c180ddb 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 年,50+ 个贡献者、90+ 次发版、3000+ 次提交,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年起已连续维护近 7 年,60+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From 11874be5e72db010125c0b1654af1de4aa660f84 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 4 Feb 2024 20:46:09 +0800
Subject: [PATCH 010/145] =?UTF-8?q?=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=8C60+=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 13c180ddb..2de84b5db 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 年多,60+ 贡献者、90+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
From fefc4cb9e36b69c819dbc2a2763149d2108a4318 Mon Sep 17 00:00:00 2001
From: ostrichManX <159116925+ostrichManX@users.noreply.github.com>
Date: Wed, 7 Feb 2024 10:28:14 +0800
Subject: [PATCH 011/145] Update AbstractSQLConfig.java
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
解决oracle in中的子查询会带上分页的bug
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index bfb3b1e1d..f5ac342fc 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4587,6 +4587,9 @@ public boolean isWithAsEnable() {
*/
protected String getOraclePageSql(String sql) {
int count = getCount();
+ if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
+ return sql;
+ }
int offset = getOffset(getPage(), count);
String alias = getAliasWithQuote();
From f0b16a1280bcc5e149abfdbc030e358a109cfe08 Mon Sep 17 00:00:00 2001
From: jia199807 <44639807+jia199807@users.noreply.github.com>
Date: Sun, 18 Feb 2024 11:01:46 +0800
Subject: [PATCH 012/145] Update AbstractSQLConfig.java
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
这里 Log.DEBUG == false 应该去掉,毕竟上线前要 Log.DEBUG = true 减少日志打印等,会导致上线后初始化加载 Access, Request 表记录仍然限制最大 Parser.getMaxQueryCount(默认 100)。
(摘自 https://github.com/Tencent/APIJSON/issues/640)
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index f5ac342fc..278b5e15d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -883,7 +883,7 @@ public int[] getDBVersionNums() {
@Override
public boolean limitSQLCount() {
- return Log.DEBUG == false || AbstractVerifier.SYSTEM_ACCESS_MAP.containsKey(getTable()) == false;
+ return AbstractVerifier.SYSTEM_ACCESS_MAP.containsKey(getTable()) == false;
}
@Override
public boolean allowPartialUpdateFailed() {
From 9a1f7dbe7fcb06166428e1ac03cc8817659765d2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 21 Feb 2024 19:41:27 +0800
Subject: [PATCH 013/145] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20UIGO=20-=20=F0=9F=93=B1=20=E9=9B=B6?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=BF=AB=E5=87=86=E7=A8=B3=20UI=20=E6=99=BA?=
=?UTF-8?q?=E8=83=BD=E5=BD=95=E5=88=B6=E5=9B=9E=E6=94=BE=E5=B9=B3=E5=8F=B0?=
=?UTF-8?q?=20=F0=9F=9A=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
自动兼容任意宽高比分辨率屏幕,自动精准等待网络请求,录制回放快、准、稳!
创作不易,右上角点 ⭐Star 来支持/收藏下吧,谢谢 ^_^
https://github.com/TommyLemon/unitauto-go/edit/main/README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 2de84b5db..faf915252 100644
--- a/README.md
+++ b/README.md
@@ -608,6 +608,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,一键批量生成参数组合、快速构造大量测试数据
+[UIGO](https://github.com/TommyLemon/UIGO) 📱 零代码快准稳 UI 智能录制回放平台 🚀 自动兼容任意宽高比分辨率屏幕,自动精准等待网络请求,录制回放快、准、稳!
+
[apijson-doc](https://github.com/vincentCheng/apijson-doc) APIJSON 官方文档,提供排版清晰、搜索方便的文档内容展示,包括设计规范、图文教程等
[APIJSONdocs](https://github.com/ruoranw/APIJSONdocs) APIJSON 英文文档,提供排版清晰的文档内容展示,包括详细介绍、设计规范、使用方式等
From c2589ef4839539138a658b93e7c3a075bbf9d519 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 21 Feb 2024 19:44:03 +0800
Subject: [PATCH 014/145] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20UIGO=20-=20=F0=9F=93=B1=20=E9=9B=B6?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=BF=AB=E5=87=86=E7=A8=B3=20UI=20=E6=99=BA?=
=?UTF-8?q?=E8=83=BD=E5=BD=95=E5=88=B6=E5=9B=9E=E6=94=BE=E5=B9=B3=E5=8F=B0?=
=?UTF-8?q?=20=F0=9F=9A=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
自动兼容任意宽高比分辨率屏幕、自动精准等待网络请求,录制回放快、准、稳!
创作不易,右上角点 ⭐Star 来支持/收藏下吧,谢谢 ^_^
https://github.com/TommyLemon/UIGO
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index faf915252..0f70ad519 100644
--- a/README.md
+++ b/README.md
@@ -608,7 +608,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,一键批量生成参数组合、快速构造大量测试数据
-[UIGO](https://github.com/TommyLemon/UIGO) 📱 零代码快准稳 UI 智能录制回放平台 🚀 自动兼容任意宽高比分辨率屏幕,自动精准等待网络请求,录制回放快、准、稳!
+[UIGO](https://github.com/TommyLemon/UIGO) 📱 零代码快准稳 UI 智能录制回放平台 🚀 自动兼容任意宽高比分辨率屏幕、自动精准等待网络请求,录制回放快、准、稳!
[apijson-doc](https://github.com/vincentCheng/apijson-doc) APIJSON 官方文档,提供排版清晰、搜索方便的文档内容展示,包括设计规范、图文教程等
From d079cb4080039627c7fec2523266ec19eb11df33 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 5 Mar 2024 20:39:24 +0800
Subject: [PATCH 015/145] =?UTF-8?q?=E8=B4=A1=E7=8C=AE=E8=80=85=E5=90=8D?=
=?UTF-8?q?=E5=8D=95=E6=96=B0=E5=A2=9E=208=20=E4=BA=BA=EF=BC=8C=E6=84=9F?=
=?UTF-8?q?=E8=B0=A2=E5=A4=A7=E5=AE=B6=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://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
---
CONTRIBUTING.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f9c33a7a9..06596081b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -56,6 +56,14 @@
- [cnscoo](https://github.com/cnscoo)(阿里云工程师)
- [aninZz](https://github.com/aninZz)
- [leomiaomiao](https://github.com/leomiaomiao)
+- [YqxLzx](https://github.com/YqxLzx)
+- [hiteshbedre](https://github.com/hiteshbedre)
+- [wahowaho](https://github.com/wahowaho)
+- [jarrodquan](https://github.com/jarrodquan)
+- [gemufeng](https://github.com/gemufeng)(上海麦市工程师)
+- [komiblog](https://github.com/komiblog)
+- [ostrichManX](https://github.com/ostrichManX)
+- [jia199807](https://github.com/jia199807)
#### 其中特别致谢:
cloudAndMonkey 提交的 11 个 Commits, 对 APIJSON 做出了 1,496 增加和 845 处删减(截止 2022/12/15 日);
From 83d606aad173273aecbce1b22e46dcfcc4202bd3 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 12 Mar 2024 20:32:23 +0800
Subject: [PATCH 016/145] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20maven-compiler-plu?=
=?UTF-8?q?gin=203.12.1?=
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 489e98f55..17c03f5b9 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -33,7 +33,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ 3.12.1
17
17
From c03d0d31eca5470c86b74462411691627d6cd928 Mon Sep 17 00:00:00 2001
From: guanlinc <437663002@qq.com>
Date: Thu, 14 Mar 2024 21:16:51 +0800
Subject: [PATCH 017/145] =?UTF-8?q?RN=E5=88=97=E6=95=B0=E5=AD=97=E6=AE=B5?=
=?UTF-8?q?=E5=8A=A0=E4=B8=8A=E5=8F=8C=E5=BC=95=E5=8F=B7=EF=BC=8C=E9=81=B5?=
=?UTF-8?q?=E5=BE=AA=E6=95=B0=E6=8D=AE=E5=BA=93=E8=A7=84=E8=8C=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 278b5e15d..c39fd9d43 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4592,9 +4592,9 @@ protected String getOraclePageSql(String sql) {
}
int offset = getOffset(getPage(), count);
String alias = getAliasWithQuote();
-
- return "SELECT * FROM (SELECT " + alias + ".*, ROWNUM RN FROM (" + sql + ") " + alias
- + " WHERE ROWNUM <= " + (offset + count) + ") WHERE RN > " + offset;
+ String quote = getQuote();
+ return "SELECT * FROM (SELECT " + alias + ".*, ROWNUM "+ quote + "RN" + quote +" FROM (" + sql + ") " + alias
+ + " WHERE ROWNUM <= " + (offset + count) + ") WHERE "+ quote + "RN" + quote +" > " + offset;
}
/**获取条件SQL字符串
From ac186678dc1bc4a4a2f399034aebebd641d188b8 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 18 Mar 2024 22:20:20 +0800
Subject: [PATCH 018/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?Milvus-AI=20=E5=90=91=E9=87=8F=E6=95=B0=E6=8D=AE=E5=BA=93?=
=?UTF-8?q?=E3=80=81InfluxDB-=E7=89=A9=E8=81=94=E7=BD=91=E6=97=B6=E5=BA=8F?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E3=80=81MongoDB&Cassandra-NoSQL=20?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=20=E7=9A=84=204=20=E4=B8=AA=20APIJS?=
=?UTF-8?q?ON=20=E6=8F=92=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#%E7%94%9F%E6%80%81%E9%A1%B9%E7%9B%AE
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 0f70ad519..e64f3c66d 100644
--- a/README.md
+++ b/README.md
@@ -602,6 +602,14 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-column](https://github.com/APIJSON/apijson-column) APIJSON 的字段插件,支持 字段名映射 和 !key 反选字段
+[apijson-milvus](https://github.com/APIJSON/apijson-milvus) APIJSON 的 Milvus AI 向量数据库插件
+
+[apijson-influxdb](https://github.com/APIJSON/apijson-influxdb) APIJSON 的 InfluxDB 物联网时序数据库插件
+
+[apijson-mongodb](https://github.com/APIJSON/apijson-mongodb) APIJSON 的 MongoDB NoSQL 数据库插件
+
+[apijson-cassandra](https://github.com/APIJSON/apijson-cassandra) APIJSON 的 Cassandra NoSQL 数据库插件
+
[APIAuto](https://github.com/TommyLemon/APIAuto) 敏捷开发最强大易用的接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释
[UnitAuto](https://github.com/TommyLemon/UnitAuto) 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能
From a763bf6afa6c3a56dac60bd55bc2848f5ef3ce2e Mon Sep 17 00:00:00 2001
From: jia199807 <44639807+jia199807@users.noreply.github.com>
Date: Tue, 2 Apr 2024 14:04:57 +0800
Subject: [PATCH 019/145] Update README.md
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 e64f3c66d..da55a1d50 100644
--- a/README.md
+++ b/README.md
@@ -354,6 +354,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [邻盛科技(武汉)有限公司](http://www.linksame.com)
* [上海麦市信息科技有限公司](https://www.masscms.com)
* [上海翊丞互联网科技有限公司](http://www.renrencjl.com/home)
+ * [上海直真君智科技有限公司](http://www.zzjunzhi.com)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、1 个美国加州大学学生、3 个 SUSTech 学生等):
From 7403f0a7471819ae8897c2fa56ea7eea4ab9c4c7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 21 Apr 2024 11:36:21 +0800
Subject: [PATCH 020/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8C=85=E6=8B=AC?=
=?UTF-8?q?=E8=80=81=E5=A4=96=E5=9C=A8=E5=86=85=E7=9A=84=2011=20=E4=B8=AA?=
=?UTF-8?q?=E8=B4=A1=E7=8C=AE=E8=80=85=EF=BC=8C=E6=84=9F=E8=B0=A2=E5=A4=A7?=
=?UTF-8?q?=E5=AE=B6=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://github.com/Tencent/APIJSON?tab=readme-ov-file#%E8%B4%A1%E7%8C%AE%E8%80%85%E4%BB%AC
---
README.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/README.md b/README.md
index da55a1d50..86399e402 100644
--- a/README.md
+++ b/README.md
@@ -407,6 +407,17 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
+
+
+
+
+
+
+
+
+
+
+
From 9f6272478ac5db11d7389225fc47eae91aa72974 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 19 May 2024 17:27:56 +0800
Subject: [PATCH 021/145] =?UTF-8?q?=E5=A4=A7=E6=95=B0=E8=BD=AC=20String=20?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=EF=BC=8C=E9=81=BF=E5=85=8D=E5=89=8D=E7=AB=AF?=
=?UTF-8?q?/=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=8B=BF=E5=88=B0=E7=B2=BE?=
=?UTF-8?q?=E5=BA=A6=E4=B8=A2=E5=A4=B1=E7=94=9A=E8=87=B3=E4=B8=A5=E9=87=8D?=
=?UTF-8?q?=E5=A4=B1=E7=9C=9F=E7=9A=84=E5=80=BC=EF=BC=8C=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E9=87=8D=E5=86=99=20getNumVal=20=E6=9D=A5=E8=87=AA=E5=AE=9A?=
=?UTF-8?q?=E4=B9=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLExecutor.java | 28 ++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index e4b38d0b1..bb476783a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -6,6 +6,8 @@
package apijson.orm;
import java.io.BufferedReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
@@ -995,9 +997,12 @@ protected Object getValue(@NotNull SQLConfig config, @NotNull ResultSet rs, @
boolean castToJson = false;
//数据库查出来的null和empty值都有意义,去掉会导致 Moment:{ @column:"content" } 部分无结果及中断数组查询!
- if (value instanceof Boolean || value instanceof Number) {
+ if (value instanceof Boolean) {
//加快判断速度
}
+ else if (value instanceof Number) {
+ value = getNumVal((Number) value);
+ }
else if (value instanceof Timestamp) {
value = ((Timestamp) value).toString();
}
@@ -1058,6 +1063,27 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
return value;
}
+ public Object getNumVal(Number value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof BigInteger) {
+ return ((BigInteger) value).toString();
+ }
+
+ if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).toString();
+ }
+
+ double v = value.doubleValue();
+ if (v > Integer.MAX_VALUE || v < Integer.MIN_VALUE) { // 避免前端/客户端拿到精度丢失甚至严重失真的值
+ return value.toString();
+ }
+
+ return value;
+ }
+
/**判断是否为JSON类型
* @param config
From 4a2e7f590de7db7c3133736edbe584a229bfc1e6 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 19 May 2024 17:38:44 +0800
Subject: [PATCH 022/145] =?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 023/145] =?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 024/145] 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 025/145] 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 026/145] =?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 027/145] =?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 028/145] =?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 029/145] =?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 030/145] =?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 031/145] =?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 032/145] =?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 033/145] =?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 034/145] =?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 035/145] =?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 036/145] =?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 037/145] =?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 038/145] =?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 039/145] 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 040/145] 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 041/145] 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 042/145] =?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 043/145] =?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 044/145] 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 045/145] 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 046/145] =?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 047/145] =?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 048/145] =?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 049/145] =?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 050/145] =?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 051/145] =?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 052/145] =?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 053/145] =?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 054/145] =?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 055/145] =?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 056/145] =?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 057/145] =?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 058/145] =?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 059/145] =?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 060/145] =?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 061/145] =?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 062/145] =?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 063/145] =?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 064/145] =?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 065/145] =?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 066/145] =?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 067/145] =?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 068/145] =?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 069/145] =?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 070/145] =?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 071/145] =?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 072/145] =?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 073/145] =?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 074/145] =?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 075/145] =?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 076/145] =?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 077/145] =?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 078/145] =?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 079/145] =?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 080/145] =?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 081/145] =?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 082/145] =?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 083/145] =?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 084/145] =?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 085/145] =?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 086/145] =?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 087/145] =?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
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index e9315e224..6183afea8 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.5.0";
+ public static final String VERSION = "7.5.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/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 54eafe97a..277893eb9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1981,7 +1981,7 @@ public Object getValueByPath(String valuePath) {
*/
public static String getDecodedKey(String key) {
try {
- return URLDecoder.decode(key, StandardCharsets.UTF_8);
+ return URLDecoder.decode(key, StringUtil.UTF_8);
} catch (Throwable e) {
return key;
}
From f82c1de346e8da030d9f7a5cee262a20f0a81992 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Feb 2025 22:54:03 +0800
Subject: [PATCH 088/145] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=8C=87=E5=AE=9A=20?=
=?UTF-8?q?JDK=2017=20=E6=89=93=E5=8C=85=E7=9A=84=20jitpack.yml?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
jitpack.yml | 6 ------
1 file changed, 6 deletions(-)
delete mode 100644 jitpack.yml
diff --git a/jitpack.yml b/jitpack.yml
deleted file mode 100644
index 9e42c425a..000000000
--- a/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
From a4108de1a865765d2f4a5a269212c554bf6547d5 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 22 Feb 2025 01:19:36 +0800
Subject: [PATCH 089/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20@catalog=20?=
=?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=95=B0=E6=8D=AE=E5=BA=93=E7=9B=AE=E5=BD=95?=
=?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=20@namespace=20=E5=92=8C=20@catalog?=
=?UTF-8?q?=20=E5=85=A8=E5=B1=80=E5=85=B3=E9=94=AE=E8=AF=8D=E5=8F=8A=20JOI?=
=?UTF-8?q?N=20=E5=A4=84=E7=90=86=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81?=
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 | 13 +-
.../apijson/orm/AbstractObjectParser.java | 40 +++--
.../main/java/apijson/orm/AbstractParser.java | 56 +++++--
.../java/apijson/orm/AbstractSQLConfig.java | 139 ++++++++++--------
.../java/apijson/orm/AbstractSQLExecutor.java | 9 +-
.../src/main/java/apijson/orm/Parser.java | 4 +-
.../src/main/java/apijson/orm/SQLConfig.java | 102 +++++++------
8 files changed, 230 insertions(+), 135 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 30a3f9444..475e6ba91 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.5.5
+ 7.5.6
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 68fb30c57..6f4359019 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -140,8 +140,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_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_NAMESPACE = "@namespace"; //命名空间,Table 在非默认 namespace 内时需要声明
+ public static final String KEY_CATALOG = "@catalog"; //目录,Table 在非默认 catalog 内时需要声明
+ 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函数
@@ -172,6 +173,7 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST.add(KEY_DATABASE);
TABLE_KEY_LIST.add(KEY_DATASOURCE);
TABLE_KEY_LIST.add(KEY_NAMESPACE);
+ TABLE_KEY_LIST.add(KEY_CATALOG);
TABLE_KEY_LIST.add(KEY_SCHEMA);
TABLE_KEY_LIST.add(KEY_EXPLAIN);
TABLE_KEY_LIST.add(KEY_CACHE);
@@ -269,6 +271,13 @@ public JSONObject setDatasource(String datasource) {
public JSONObject setNamespace(String namespace) {
return puts(KEY_NAMESPACE, namespace);
}
+ /**set catalog where table was puts
+ * @param catalog
+ * @return this
+ */
+ public JSONObject setCatalog(String catalog) {
+ return puts(KEY_CATALOG, catalog);
+ }
/**set schema where table was puts
* @param schema
* @return this
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 1ed3f1716..0a35761cb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -328,22 +328,42 @@ else if (_method == PUT && value instanceof JSONArray && (whereList == null || w
}
if (isTable) {
- if (parser.getGlobalDatabase() != null && sqlRequest.get(JSONRequest.KEY_DATABASE) == null) {
- sqlRequest.put(JSONRequest.KEY_DATABASE, parser.getGlobalDatabase());
+ // parser.onVerifyRole 已处理 globalRole
+
+ String db = parser.getGlobalDatabase();
+ if (db != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_DATABASE, db);
+ }
+
+ String ds = parser.getGlobalDatasource();
+ if (ds != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_DATASOURCE, ds);
+ }
+
+ String ns = parser.getGlobalNamespace();
+ if (ns != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_NAMESPACE, ns);
}
- if (parser.getGlobalSchema() != null && sqlRequest.get(JSONRequest.KEY_SCHEMA) == null) {
- sqlRequest.put(JSONRequest.KEY_SCHEMA, parser.getGlobalSchema());
+
+ String cl = parser.getGlobalCatalog();
+ if (cl != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_CATALOG, cl);
}
- if (parser.getGlobalDatasource() != null && sqlRequest.get(JSONRequest.KEY_DATASOURCE) == null) {
- sqlRequest.put(JSONRequest.KEY_DATASOURCE, parser.getGlobalDatasource());
+
+ String sch = parser.getGlobalSchema();
+ if (sch != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_SCHEMA, sch);
}
if (isSubquery == false) { // 解决 SQL 语法报错,子查询不能 EXPLAIN
- if (parser.getGlobalExplain() != null && sqlRequest.get(JSONRequest.KEY_EXPLAIN) == null) {
- sqlRequest.put(JSONRequest.KEY_EXPLAIN, parser.getGlobalExplain());
+ Boolean exp = parser.getGlobalExplain();
+ if (sch != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_EXPLAIN, exp);
}
- if (parser.getGlobalCache() != null && sqlRequest.get(JSONRequest.KEY_CACHE) == null) {
- sqlRequest.put(JSONRequest.KEY_CACHE, parser.getGlobalCache());
+
+ String cache = parser.getGlobalCache();
+ if (cache != null) {
+ sqlRequest.putIfAbsent(JSONRequest.KEY_CACHE, cache);
}
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 277893eb9..624830374 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -320,15 +320,7 @@ public AbstractParser setGlobalDatabase(String globalDatabase) {
public String getGlobalDatabase() {
return globalDatabase;
}
- protected String globalSchema;
- public AbstractParser setGlobalSchema(String globalSchema) {
- this.globalSchema = globalSchema;
- return this;
- }
- @Override
- public String getGlobalSchema() {
- return globalSchema;
- }
+
protected String globalDatasource;
@Override
public String getGlobalDatasource() {
@@ -339,6 +331,36 @@ public AbstractParser setGlobalDatasource(String globalDatasource) {
return this;
}
+ protected String globalNamespace;
+ public AbstractParser setGlobalNamespace(String globalNamespace) {
+ this.globalNamespace = globalNamespace;
+ return this;
+ }
+ @Override
+ public String getGlobalNamespace() {
+ return globalNamespace;
+ }
+
+ protected String globalCatalog;
+ public AbstractParser setGlobalCatalog(String globalCatalog) {
+ this.globalCatalog = globalCatalog;
+ return this;
+ }
+ @Override
+ public String getGlobalCatalog() {
+ return globalCatalog;
+ }
+
+ protected String globalSchema;
+ public AbstractParser setGlobalSchema(String globalSchema) {
+ this.globalSchema = globalSchema;
+ return this;
+ }
+ @Override
+ public String getGlobalSchema() {
+ return globalSchema;
+ }
+
protected Boolean globalExplain;
public AbstractParser setGlobalExplain(Boolean globalExplain) {
this.globalExplain = globalExplain;
@@ -508,19 +530,25 @@ public JSONObject parseResponse(JSONObject request) {
}
try {
- setGlobalFormat(requestObject.getBoolean(JSONRequest.KEY_FORMAT));
setGlobalDatabase(requestObject.getString(JSONRequest.KEY_DATABASE));
- setGlobalSchema(requestObject.getString(JSONRequest.KEY_SCHEMA));
setGlobalDatasource(requestObject.getString(JSONRequest.KEY_DATASOURCE));
+ setGlobalNamespace(requestObject.getString(JSONRequest.KEY_NAMESPACE));
+ setGlobalCatalog(requestObject.getString(JSONRequest.KEY_CATALOG));
+ setGlobalSchema(requestObject.getString(JSONRequest.KEY_SCHEMA));
+
setGlobalExplain(requestObject.getBoolean(JSONRequest.KEY_EXPLAIN));
setGlobalCache(requestObject.getString(JSONRequest.KEY_CACHE));
+ setGlobalFormat(requestObject.getBoolean(JSONRequest.KEY_FORMAT));
- requestObject.remove(JSONRequest.KEY_FORMAT);
requestObject.remove(JSONRequest.KEY_DATABASE);
- requestObject.remove(JSONRequest.KEY_SCHEMA);
requestObject.remove(JSONRequest.KEY_DATASOURCE);
+ requestObject.remove(JSONRequest.KEY_NAMESPACE);
+ requestObject.remove(JSONRequest.KEY_CATALOG);
+ requestObject.remove(JSONRequest.KEY_SCHEMA);
+
requestObject.remove(JSONRequest.KEY_EXPLAIN);
requestObject.remove(JSONRequest.KEY_CACHE);
+ requestObject.remove(JSONRequest.KEY_FORMAT);
} catch (Exception e) {
return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
}
@@ -1462,6 +1490,8 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
JOIN_COPY_KEY_LIST = new ArrayList();
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_ROLE);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_DATABASE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_NAMESPACE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_CATALOG);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_SCHEMA);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_DATASOURCE);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COLUMN);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 573ee0b1e..aed2efb62 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -96,6 +96,7 @@ public abstract class AbstractSQLConfig implements SQLConfig setVersion(int version) {
this.version = version;
return this;
}
@@ -870,7 +871,7 @@ public String getTag() {
return tag;
}
@Override
- public AbstractSQLConfig setTag(String tag) {
+ public AbstractSQLConfig setTag(String tag) {
this.tag = tag;
return this;
}
@@ -927,6 +928,7 @@ public String getUserIdKey() {
private boolean distinct = false;
private String database; //表所在的数据库类型
private String namespace; //表所在的命名空间
+ private String catalog; //表所在的目录
private String schema; //表所在的数据库名
private String datasource; //数据源
private String table; //表名
@@ -964,7 +966,7 @@ public String getUserIdKey() {
private String procedure;
- public SQLConfig setProcedure(String procedure) {
+ public AbstractSQLConfig setProcedure(String procedure) {
this.procedure = procedure;
return this;
}
@@ -994,7 +996,7 @@ public RequestMethod getMethod() {
return method;
}
@Override
- public AbstractSQLConfig setMethod(RequestMethod method) {
+ public AbstractSQLConfig setMethod(RequestMethod method) {
this.method = method;
return this;
}
@@ -1003,7 +1005,7 @@ public boolean isPrepared() {
return prepared && ! isMongoDB(); // MongoDB JDBC 还不支持预编译;
}
@Override
- public AbstractSQLConfig setPrepared(boolean prepared) {
+ public AbstractSQLConfig setPrepared(boolean prepared) {
this.prepared = prepared;
return this;
}
@@ -1012,7 +1014,7 @@ public boolean isMain() {
return main;
}
@Override
- public AbstractSQLConfig setMain(boolean main) {
+ public AbstractSQLConfig setMain(boolean main) {
this.main = main;
return this;
}
@@ -1023,7 +1025,7 @@ public Object getId() {
return id;
}
@Override
- public AbstractSQLConfig setId(Object id) {
+ public AbstractSQLConfig setId(Object id) {
this.id = id;
return this;
}
@@ -1033,7 +1035,7 @@ public Object getIdIn() {
return idIn;
}
@Override
- public AbstractSQLConfig setIdIn(Object idIn) {
+ public AbstractSQLConfig setIdIn(Object idIn) {
this.idIn = idIn;
return this;
}
@@ -1044,7 +1046,7 @@ public Object getUserId() {
return userId;
}
@Override
- public AbstractSQLConfig setUserId(Object userId) {
+ public AbstractSQLConfig setUserId(Object userId) {
this.userId = userId;
return this;
}
@@ -1054,7 +1056,7 @@ public Object getUserIdIn() {
return userIdIn;
}
@Override
- public AbstractSQLConfig setUserIdIn(Object userIdIn) {
+ public AbstractSQLConfig setUserIdIn(Object userIdIn) {
this.userIdIn = userIdIn;
return this;
}
@@ -1065,7 +1067,7 @@ public String getRole() {
return role;
}
@Override
- public AbstractSQLConfig setRole(String role) {
+ public AbstractSQLConfig setRole(String role) {
this.role = role;
return this;
}
@@ -1075,7 +1077,7 @@ public boolean isDistinct() {
return distinct;
}
@Override
- public SQLConfig setDistinct(boolean distinct) {
+ public AbstractSQLConfig setDistinct(boolean distinct) {
this.distinct = distinct;
return this;
}
@@ -1085,7 +1087,7 @@ public String getDatabase() {
return database;
}
@Override
- public SQLConfig setDatabase(String database) {
+ public AbstractSQLConfig setDatabase(String database) {
this.database = database;
return this;
}
@@ -1344,26 +1346,39 @@ public String quote(String s) {
return q + s + q;
}
- @Override
- public String getNamespace() {
- return namespace;
- }
-
@Override
public String getSQLNamespace() {
String sch = getNamespace(); // 前端传参 @namespace 优先
return sch == null ? DEFAULT_NAMESPACE : sch; // 最后代码默认兜底配置
}
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
@Override
public AbstractSQLConfig setNamespace(String namespace) {
this.namespace = namespace;
return this;
}
+
@Override
- public String getSchema() {
- return schema;
+ public String getSQLCatalog() {
+ String catalog = getCatalog(); // 前端传参 @catalog 优先
+ return catalog == null ? DEFAULT_CATALOG : catalog; // 最后代码默认兜底配置
+ }
+
+ @Override
+ public String getCatalog() {
+ return catalog;
+ }
+
+ @Override
+ public AbstractSQLConfig setCatalog(String catalog) {
+ this.catalog = catalog;
+ return this;
}
@NotNull
@@ -1393,7 +1408,12 @@ public String getSQLSchema() {
}
@Override
- public AbstractSQLConfig setSchema(String schema) {
+ public String getSchema() {
+ return schema;
+ }
+
+ @Override
+ public AbstractSQLConfig setSchema(String schema) {
if (schema != null) {
AbstractFunctionParser.verifySchema(schema, getTable());
}
@@ -1406,7 +1426,7 @@ public String getDatasource() {
return datasource;
}
@Override
- public SQLConfig setDatasource(String datasource) {
+ public AbstractSQLConfig setDatasource(String datasource) {
this.datasource = datasource;
return this;
}
@@ -1446,7 +1466,7 @@ public String getTablePath() {
+ (isKeyPrefix() ? getAs() + q + getSQLAlias() + q : "");
}
@Override
- public AbstractSQLConfig setTable(String table) { //Table已经在Parser中校验,所以这里不用防SQL注入
+ public AbstractSQLConfig setTable(String table) { //Table已经在Parser中校验,所以这里不用防SQL注入
this.table = table;
return this;
}
@@ -1460,7 +1480,7 @@ public String getAlias() {
return alias;
}
@Override
- public AbstractSQLConfig setAlias(String alias) {
+ public AbstractSQLConfig setAlias(String alias) {
this.alias = alias;
return this;
}
@@ -1477,11 +1497,11 @@ public String getSQLAliasWithQuote() {
public String getGroup() {
return group;
}
- public AbstractSQLConfig setGroup(String... keys) {
+ public AbstractSQLConfig setGroup(String... keys) {
return setGroup(StringUtil.getString(keys));
}
@Override
- public AbstractSQLConfig setGroup(String group) {
+ public AbstractSQLConfig setGroup(String group) {
this.group = group;
return this;
}
@@ -1540,7 +1560,7 @@ public String getHavingCombine() {
return havingCombine;
}
@Override
- public SQLConfig setHavingCombine(String havingCombine) {
+ public AbstractSQLConfig setHavingCombine(String havingCombine) {
this.havingCombine = havingCombine;
return this;
}
@@ -1550,11 +1570,11 @@ public Map getHaving() {
return having;
}
@Override
- public SQLConfig setHaving(Map having) {
+ public AbstractSQLConfig setHaving(Map having) {
this.having = having;
return this;
}
- public AbstractSQLConfig setHaving(String... conditions) {
+ public AbstractSQLConfig setHaving(String... conditions) {
return setHaving(StringUtil.getString(conditions));
}
@@ -1668,11 +1688,11 @@ else if (SQL_FUNCTION_MAP.containsKey(method) == false) {
public String getOrder() {
return order;
}
- public AbstractSQLConfig setOrder(String... conditions) {
+ public AbstractSQLConfig setOrder(String... conditions) {
return setOrder(StringUtil.getString(conditions));
}
@Override
- public AbstractSQLConfig setOrder(String order) {
+ public AbstractSQLConfig setOrder(String order) {
this.order = order;
return this;
}
@@ -1787,7 +1807,7 @@ public Map getKeyMap() {
return keyMap;
}
@Override
- public AbstractSQLConfig setKeyMap(Map keyMap) {
+ public AbstractSQLConfig setKeyMap(Map keyMap) {
this.keyMap = keyMap;
return this;
}
@@ -1797,7 +1817,7 @@ public List getRaw() {
return raw;
}
@Override
- public AbstractSQLConfig setRaw(List raw) {
+ public AbstractSQLConfig setRaw(List raw) {
this.raw = raw;
return this;
}
@@ -1857,7 +1877,7 @@ public List getJson() {
return json;
}
@Override
- public AbstractSQLConfig setJson(List json) {
+ public AbstractSQLConfig setJson(List json) {
this.json = json;
return this;
}
@@ -1868,7 +1888,7 @@ public Subquery getFrom() {
return from;
}
@Override
- public AbstractSQLConfig setFrom(Subquery from) {
+ public AbstractSQLConfig setFrom(Subquery from) {
this.from = from;
return this;
}
@@ -1878,7 +1898,7 @@ public List getColumn() {
return column;
}
@Override
- public AbstractSQLConfig setColumn(List column) {
+ public AbstractSQLConfig setColumn(List column) {
this.column = column;
return this;
}
@@ -2517,7 +2537,7 @@ public String getValuesString() {
return s;
}
@Override
- public AbstractSQLConfig setValues(List> valuess) {
+ public AbstractSQLConfig setValues(List> valuess) {
this.values = valuess;
return this;
}
@@ -2527,7 +2547,7 @@ public Map getContent() {
return content;
}
@Override
- public AbstractSQLConfig setContent(Map content) {
+ public AbstractSQLConfig setContent(Map content) {
this.content = content;
return this;
}
@@ -2537,7 +2557,7 @@ public int getCount() {
return count;
}
@Override
- public AbstractSQLConfig setCount(int count) {
+ public AbstractSQLConfig setCount(int count) {
this.count = count;
return this;
}
@@ -2546,7 +2566,7 @@ public int getPage() {
return page;
}
@Override
- public AbstractSQLConfig setPage(int page) {
+ public AbstractSQLConfig setPage(int page) {
this.page = page;
return this;
}
@@ -2555,7 +2575,7 @@ public int getPosition() {
return position;
}
@Override
- public AbstractSQLConfig setPosition(int position) {
+ public AbstractSQLConfig setPosition(int position) {
this.position = position;
return this;
}
@@ -2565,7 +2585,7 @@ public int getQuery() {
return query;
}
@Override
- public AbstractSQLConfig setQuery(int query) {
+ public AbstractSQLConfig setQuery(int query) {
this.query = query;
return this;
}
@@ -2574,7 +2594,7 @@ public Boolean getCompat() {
return compat;
}
@Override
- public AbstractSQLConfig setCompat(Boolean compat) {
+ public AbstractSQLConfig setCompat(Boolean compat) {
this.compat = compat;
return this;
}
@@ -2584,7 +2604,7 @@ public int getType() {
return type;
}
@Override
- public AbstractSQLConfig setType(int type) {
+ public AbstractSQLConfig setType(int type) {
this.type = type;
return this;
}
@@ -2594,12 +2614,12 @@ public int getCache() {
return cache;
}
@Override
- public AbstractSQLConfig setCache(int cache) {
+ public AbstractSQLConfig setCache(int cache) {
this.cache = cache;
return this;
}
- public AbstractSQLConfig setCache(String cache) {
+ public AbstractSQLConfig setCache(String cache) {
return setCache(getCache(cache));
}
public static int getCache(String cache) {
@@ -2638,7 +2658,7 @@ public boolean isExplain() {
return explain;
}
@Override
- public AbstractSQLConfig setExplain(boolean explain) {
+ public AbstractSQLConfig setExplain(boolean explain) {
this.explain = explain;
return this;
}
@@ -2648,7 +2668,7 @@ public List getJoinList() {
return joinList;
}
@Override
- public SQLConfig setJoinList(List joinList) {
+ public AbstractSQLConfig setJoinList(List joinList) {
this.joinList = joinList;
return this;
}
@@ -2663,7 +2683,7 @@ public boolean isTest() {
return test;
}
@Override
- public AbstractSQLConfig setTest(boolean test) {
+ public AbstractSQLConfig setTest(boolean test) {
this.test = test;
return this;
}
@@ -2761,7 +2781,7 @@ public List getNull() {
return nulls;
}
@Override
- public SQLConfig setNull(List nulls) {
+ public AbstractSQLConfig setNull(List nulls) {
this.nulls = nulls;
return this;
}
@@ -2771,7 +2791,7 @@ public Map getCast() {
return cast;
}
@Override
- public SQLConfig setCast(Map cast) {
+ public AbstractSQLConfig setCast(Map cast) {
this.cast = cast;
return this;
}
@@ -2803,7 +2823,7 @@ public String getCombine() {
return combine;
}
@Override
- public AbstractSQLConfig setCombine(String combine) {
+ public AbstractSQLConfig setCombine(String combine) {
this.combine = combine;
return this;
}
@@ -2822,7 +2842,7 @@ public Map> getCombineMap() {
return combineMap;
}
@Override
- public AbstractSQLConfig setCombineMap(Map> combineMap) {
+ public AbstractSQLConfig setCombineMap(Map> combineMap) {
this.combineMap = combineMap;
return this;
}
@@ -2832,7 +2852,7 @@ public Map getWhere() {
return where;
}
@Override
- public AbstractSQLConfig setWhere(Map where) {
+ public AbstractSQLConfig setWhere(Map where) {
this.where = where;
return this;
}
@@ -2877,7 +2897,7 @@ public Object getWhere(String key, boolean exactMatch) {
return null;
}
@Override
- public AbstractSQLConfig putWhere(String key, Object value, boolean prior) {
+ public AbstractSQLConfig putWhere(String key, Object value, boolean prior) {
if (key != null) {
if (where == null) {
where = new LinkedHashMap();
@@ -3740,7 +3760,7 @@ public List getPreparedValueList() {
return preparedValueList;
}
@Override
- public AbstractSQLConfig setPreparedValueList(List preparedValueList) {
+ public AbstractSQLConfig setPreparedValueList(List preparedValueList) {
this.preparedValueList = preparedValueList;
return this;
}
@@ -4770,7 +4790,7 @@ public boolean isKeyPrefix() {
return keyPrefix;
}
@Override
- public AbstractSQLConfig setKeyPrefix(boolean keyPrefix) {
+ public AbstractSQLConfig setKeyPrefix(boolean keyPrefix) {
this.keyPrefix = keyPrefix;
return this;
}
@@ -5105,6 +5125,7 @@ public static SQLConfig newSQLConfig(RequestMethod method,
String datasource = request.getString(KEY_DATASOURCE);
String namespace = request.getString(KEY_NAMESPACE);
+ String catalog = request.getString(KEY_CATALOG);
String schema = request.getString(KEY_SCHEMA);
SQLConfig config = callback.getSQLConfig(method, database, schema, datasource, table);
@@ -5113,6 +5134,7 @@ public static SQLConfig newSQLConfig(RequestMethod method,
config.setDatabase(database); // 不删,后面表对象还要用的,必须放在 parseJoin 前
config.setDatasource(datasource); // 不删,后面表对象还要用的
config.setNamespace(namespace); // 不删,后面表对象还要用的
+ config.setCatalog(catalog); // 不删,后面表对象还要用的
config.setSchema(schema); // 不删,后面表对象还要用的
if (isProcedure) {
@@ -5274,6 +5296,7 @@ else if (userId instanceof Subquery) {}
request.remove(KEY_DATABASE);
request.remove(KEY_DATASOURCE);
request.remove(KEY_NAMESPACE);
+ request.remove(KEY_CATALOG);
request.remove(KEY_SCHEMA);
request.remove(KEY_FROM);
request.remove(KEY_COLUMN);
@@ -6233,7 +6256,7 @@ public List getWithAsExprPreparedValueList() {
}
@Override
- public AbstractSQLConfig setWithAsExprPreparedValueList(List list) {
+ public AbstractSQLConfig setWithAsExprPreparedValueList(List list) {
this.withAsExprPreparedValueList = list;
return this;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 84b30437e..9cddeb4e1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1237,7 +1237,7 @@ public PreparedStatement setArgument(@NotNull SQLConfig config, @NotNull Prep
@NotNull
@Override
public Connection getConnection(@NotNull SQLConfig config) throws Exception {
- String connectionKey = config.getDatasource() + "-" + config.getDatabase();
+ String connectionKey = getConnectionKey(config);
connection = connectionMap.get(connectionKey);
if (connection == null || connection.isClosed()) {
Log.i(TAG, "select connection " + (connection == null ? " = null" : ("isClosed = " + connection.isClosed()))) ;
@@ -1255,6 +1255,13 @@ public Connection getConnection(@NotNull SQLConfig config) throws Exception {
return connection;
}
+ public String getConnectionKey(@NotNull SQLConfig config) {
+ return getConnectionKey(config.getNamespace(), config.getCatalog(), config.getDatasource(), config.getDatabase());
+ }
+ public String getConnectionKey(String database, String datasource, String namespace, String catalog) {
+ return database + "-" + datasource + "-" + namespace + "-" + catalog;
+ }
+
//事务处理 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
private int transactionIsolation;
@Override
diff --git a/APIJSONORM/src/main/java/apijson/orm/Parser.java b/APIJSONORM/src/main/java/apijson/orm/Parser.java
index b2da33477..a272fc27a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Parser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Parser.java
@@ -112,8 +112,10 @@ JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, St
Boolean getGlobalFormat();
String getGlobalRole();
String getGlobalDatabase();
- String getGlobalSchema();
String getGlobalDatasource();
+ String getGlobalNamespace();
+ String getGlobalCatalog();
+ String getGlobalSchema();
Boolean getGlobalExplain();
String getGlobalCache();
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 61233313d..6252bc68a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -60,19 +60,19 @@ public interface SQLConfig {
Parser getParser();
- SQLConfig setParser(Parser parser);
+ SQLConfig setParser(Parser parser);
ObjectParser getObjectParser();
- SQLConfig setObjectParser(ObjectParser objectParser);
+ SQLConfig setObjectParser(ObjectParser objectParser);
int getVersion();
- SQLConfig setVersion(int version);
+ SQLConfig setVersion(int version);
String getTag();
- SQLConfig setTag(String tag);
+ SQLConfig setTag(String tag);
boolean isMySQL();
boolean isPostgreSQL();
@@ -182,72 +182,76 @@ default int[] getDBVersionNums() {
boolean isTest();
- SQLConfig setTest(boolean test);
+ SQLConfig setTest(boolean test);
int getType();
- SQLConfig setType(int type);
+ SQLConfig setType(int type);
int getCount();
- SQLConfig setCount(int count);
+ SQLConfig setCount(int count);
int getPage();
- SQLConfig setPage(int page);
+ SQLConfig setPage(int page);
int getQuery();
- SQLConfig setQuery(int query);
+ SQLConfig setQuery(int query);
Boolean getCompat();
- SQLConfig setCompat(Boolean compat);
+ SQLConfig setCompat(Boolean compat);
int getPosition();
- SQLConfig setPosition(int position);
+ SQLConfig setPosition(int position);
int getCache();
- SQLConfig setCache(int cache);
+ SQLConfig setCache(int cache);
boolean isExplain();
- SQLConfig setExplain(boolean explain);
+ SQLConfig setExplain(boolean explain);
RequestMethod getMethod();
- SQLConfig setMethod(RequestMethod method);
+ SQLConfig setMethod(RequestMethod method);
Object getId();
- SQLConfig setId(Object id);
+ SQLConfig setId(Object id);
Object getIdIn();
- SQLConfig setIdIn(Object idIn);
+ SQLConfig setIdIn(Object idIn);
Object getUserId();
- SQLConfig setUserId(Object userId);
+ SQLConfig setUserId(Object userId);
Object getUserIdIn();
- SQLConfig setUserIdIn(Object userIdIn);
+ SQLConfig setUserIdIn(Object userIdIn);
String getRole();
- SQLConfig setRole(String role);
+ SQLConfig setRole(String role);
public boolean isDistinct();
- public SQLConfig setDistinct(boolean distinct);
+ public SQLConfig setDistinct(boolean distinct);
String getDatabase();
- SQLConfig setDatabase(String database);
+ SQLConfig setDatabase(String database);
String getSQLNamespace();
String getNamespace();
SQLConfig setNamespace(String namespace);
+ String getSQLCatalog();
+ String getCatalog();
+ SQLConfig setCatalog(String catalog);
+
String getSQLSchema();
String getSchema();
- SQLConfig setSchema(String schema);
+ SQLConfig setSchema(String schema);
String getDatasource();
- SQLConfig setDatasource(String datasource);
+ SQLConfig setDatasource(String datasource);
String getQuote();
List getJson();
- SQLConfig setJson(List json);
+ SQLConfig setJson(List json);
/**请求传进来的Table名
* @return
@@ -255,7 +259,7 @@ default int[] getDBVersionNums() {
*/
String getTable();
- SQLConfig setTable(String table);
+ SQLConfig setTable(String table);
/**数据库里的真实Table名
* 通过 {@link AbstractSQLConfig.TABLE_KEY_MAP} 映射
@@ -266,49 +270,49 @@ default int[] getDBVersionNums() {
String getTablePath();
Map getKeyMap();
- SQLConfig setKeyMap(Map keyMap);
+ SQLConfig setKeyMap(Map keyMap);
List getRaw();
- SQLConfig setRaw(List raw);
+ SQLConfig setRaw(List raw);
Subquery getFrom();
- SQLConfig setFrom(Subquery from);
+ SQLConfig setFrom(Subquery from);
List getColumn();
- SQLConfig setColumn(List column);
+ SQLConfig setColumn(List column);
List> getValues();
- SQLConfig setValues(List> values);
+ SQLConfig setValues(List> values);
Map getContent();
- SQLConfig setContent(Map content);
+ SQLConfig setContent(Map content);
Map> getCombineMap();
- SQLConfig setCombineMap(Map> combineMap);
+ SQLConfig setCombineMap(Map> combineMap);
String getCombine();
- SQLConfig setCombine(String combine);
+ SQLConfig setCombine(String combine);
Map getCast();
- SQLConfig setCast(Map cast);
+ SQLConfig setCast(Map cast);
List getNull();
- SQLConfig setNull(List nulls);
+ SQLConfig setNull(List nulls);
Map getWhere();
- SQLConfig setWhere(Map where);
+ SQLConfig setWhere(Map where);
String getGroup();
- SQLConfig setGroup(String group);
+ SQLConfig setGroup(String group);
Map getHaving();
- SQLConfig setHaving(Map having);
+ SQLConfig setHaving(Map having);
String getHavingCombine();
- SQLConfig setHavingCombine(String havingCombine);
+ SQLConfig setHavingCombine(String havingCombine);
String getOrder();
- SQLConfig setOrder(String order);
+ SQLConfig setOrder(String order);
/**
* exactMatch = false
@@ -327,25 +331,25 @@ default int[] getDBVersionNums() {
* @param value
* @return
*/
- SQLConfig putWhere(String key, Object value, boolean prior);
+ SQLConfig putWhere(String key, Object value, boolean prior);
boolean isPrepared();
- SQLConfig setPrepared(boolean prepared);
+ SQLConfig setPrepared(boolean prepared);
boolean isMain();
- SQLConfig setMain(boolean main);
+ SQLConfig setMain(boolean main);
List getPreparedValueList();
- SQLConfig setPreparedValueList(List preparedValueList);
+ SQLConfig setPreparedValueList(List preparedValueList);
String getAlias();
- SQLConfig setAlias(String alias);
+ SQLConfig setAlias(String alias);
default String getTableKey() {
String alias = getAlias();
@@ -368,24 +372,24 @@ static String getSQLAlias(@NotNull String table, String alias) {
boolean isKeyPrefix();
- SQLConfig setKeyPrefix(boolean keyPrefix);
+ SQLConfig setKeyPrefix(boolean keyPrefix);
List getJoinList();
- SQLConfig setJoinList(List joinList);
+ SQLConfig setJoinList(List joinList);
boolean hasJoin();
String getSubqueryString(Subquery subquery) throws Exception;
- SQLConfig setProcedure(String procedure);
+ SQLConfig setProcedure(String procedure);
List getWithAsExprPreparedValueList();
- SQLConfig setWithAsExprPreparedValueList(List withAsExprePreparedValueList);
+ SQLConfig setWithAsExprPreparedValueList(List withAsExprePreparedValueList);
boolean isFakeDelete();
From 9b9086af8f3f5a9fc0288c015c357abbce4c617b Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 1 Mar 2025 16:28:22 +0800
Subject: [PATCH 090/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?CockroachDB-=E4=BA=91=E5=8E=9F=E7=94=9F=E5=88=86=E5=B8=83?=
=?UTF-8?q?=E5=BC=8F=E9=AB=98=E5=8F=AF=E7=94=A8=E8=BD=BB=E6=9D=BE=E6=89=A9?=
=?UTF-8?q?=E5=B1=95=E6=95=B0=E6=8D=AE=E5=BA=93?=
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 +++++---
.../java/apijson/orm/AbstractSQLExecutor.java | 2 +-
.../src/main/java/apijson/orm/SQLConfig.java | 16 +++--
.../orm/exception/CommonException.java | 71 +++++++++++++++++--
6 files changed, 100 insertions(+), 22 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 475e6ba91..f3c7bb589 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.5.6
+ 7.6.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 6183afea8..12fe35cc4 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.5.5";
+ public static final String VERSION = "7.6.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 aed2efb62..53d8633ab 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -95,8 +95,8 @@ public abstract class AbstractSQLConfig implements SQLConfig implements SQLConfig setTable(String table) { //Table已经在Parser中校验,所以这里不用防SQL注入
@@ -3955,7 +3968,7 @@ public String getRegExpString(String key, String column, Object[] values, int ty
*/
@JSONField(serialize = false)
public String getRegExpString(String key, String column, String value, boolean ignoreCase) {
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
return getKey(column) + " ~" + (ignoreCase ? "* " : " ") + getValue(key, column, value);
}
if (isOracle() || isDameng() || isKingBase() || (isMySQL() && getDBVersionNums()[0] >= 8)) {
@@ -4281,7 +4294,7 @@ public String getContainString(String key, String column, Object[] childs, int t
}
condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
condition += (getKey(column) + " @> " + getValue(key, column, newJSONArray(c)));
// operator does not exist: jsonb @> character varying "[" + c + "]");
}
@@ -4987,7 +5000,7 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
- if (isPostgreSQL() || isInfluxDB()) {
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ") + rk;
}
else if (isOracle() || isDameng() || isKingBase()) {
@@ -5042,7 +5055,7 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String arrKeyPath = isIn ? rk : lk;
String itemKeyPath = isIn ? lk : rk;
- if (isPostgreSQL() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
+ if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ " IS NOT NULL AND " + arrKeyPath + " @> " + itemKeyPath) + (isNot ? ") " : "");
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 9cddeb4e1..9393f43be 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1190,7 +1190,7 @@ else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
//} else {
// statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
//}
- if (config.isMySQL() || config.isPostgreSQL() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
+ if (config.isMySQL() || config.isPostgreSQL() || config.isCockroachDB() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
statement = getConnection(config).prepareStatement(sql);
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 6252bc68a..d70de4b0e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -22,12 +22,13 @@ public interface SQLConfig {
String DATABASE_SQLSERVER = "SQLSERVER"; // https://www.microsoft.com/en-us/sql-server
String DATABASE_ORACLE = "ORACLE"; // https://www.oracle.com/database
String DATABASE_DB2 = "DB2"; // https://www.ibm.com/products/db2
- String DATABASE_MARIADB = "MARIADB"; // https://mariadb.org
- String DATABASE_TIDB = "TIDB"; // https://www.pingcap.com/tidb
- String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
- String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
- String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
- String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
+ String DATABASE_MARIADB = "MARIADB"; // https://mariadb.org
+ String DATABASE_TIDB = "TIDB"; // https://www.pingcap.com/tidb
+ String DATABASE_COCKROACHDB = "COCKROACHDB"; // https://www.cockroachlabs.com
+ String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
+ String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
+ String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
+ String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
String DATABASE_HIVE = "HIVE"; // https://hive.apache.org
String DATABASE_PRESTO = "PRESTO"; // Facebook PrestoDB https://prestodb.io
String DATABASE_TRINO = "TRINO"; // PrestoSQL https://trino.io
@@ -45,7 +46,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_OPENGAUSS = "OPENGAUSS"; // https://opengauss.org
String DATABASE_MQ = "MQ"; //
@@ -81,6 +82,7 @@ public interface SQLConfig {
boolean isDb2();
boolean isMariaDB();
boolean isTiDB();
+ boolean isCockroachDB();
boolean isDameng();
boolean isKingBase();
boolean isElasticsearch();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index fe748b7a1..6dc15129c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -147,7 +147,7 @@ public CommonException(Throwable t, String environment) {
}
- public static Exception wrap(Exception e, SQLConfig config) {
+ public static Exception wrap(Exception e, SQLConfig> config) {
if (Log.DEBUG == false && e instanceof SQLException) {
return new SQLException("数据库驱动执行异常SQLException,非 Log.DEBUG 模式下不显示详情,避免泄漏真实模式名、表名等隐私信息", e);
}
@@ -158,23 +158,32 @@ public static Exception wrap(Exception e, SQLConfig config) {
// msg != null && msg.contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
try {
String db = config == null ? AbstractSQLConfig.DEFAULT_DATABASE : (config instanceof AbstractSQLConfig
- ? ((AbstractSQLConfig) config).getSQLDatabase() : config.getDatabase()
+ ? ((AbstractSQLConfig>) config).getSQLDatabase() : config.getDatabase()
);
- String dbVersion = config.getDBVersion();
+ String dbVersion = config == null ? null : config.getDBVersion();
if (StringUtil.isEmpty(dbVersion)) {
dbVersion = "";
}
- if (db != null) {
+ if (db != null || config == null) {
db += " " + dbVersion;
}
else if (config.isMySQL()) {
db = SQLConfig.DATABASE_MYSQL + " " + dbVersion;
}
+ else if (config.isMariaDB()) {
+ db = SQLConfig.DATABASE_MARIADB + " " + dbVersion;
+ }
+ else if (config.isTiDB()) {
+ db = SQLConfig.DATABASE_TIDB + " " + dbVersion;
+ }
else if (config.isPostgreSQL()) {
db = SQLConfig.DATABASE_POSTGRESQL + " " + dbVersion;
}
+ else if (config.isCockroachDB()) {
+ db = SQLConfig.DATABASE_COCKROACHDB + " " + dbVersion;
+ }
else if (config.isSQLServer()) {
db = SQLConfig.DATABASE_SQLSERVER + " " + dbVersion;
}
@@ -184,15 +193,69 @@ else if (config.isOracle()) {
else if (config.isDb2()) {
db = SQLConfig.DATABASE_DB2 + " " + dbVersion;
}
+ else if (config.isDuckDB()) {
+ db = SQLConfig.DATABASE_DUCKDB + " " + dbVersion;
+ }
+ else if (config.isSurrealDB()) {
+ db = SQLConfig.DATABASE_SURREALDB + " " + dbVersion;
+ }
+ else if (config.isOpenGauss()) {
+ db = SQLConfig.DATABASE_OPENGAUSS + " " + dbVersion;
+ }
else if (config.isDameng()) {
db = SQLConfig.DATABASE_DAMENG + " " + dbVersion;
}
+ else if (config.isKingBase()) {
+ db = SQLConfig.DATABASE_KINGBASE + " " + dbVersion;
+ }
+ else if (config.isElasticsearch()) {
+ db = SQLConfig.DATABASE_ELASTICSEARCH + " " + dbVersion;
+ }
else if (config.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE + " " + dbVersion;
}
+ else if (config.isMilvus()) {
+ db = SQLConfig.DATABASE_MILVUS + " " + dbVersion;
+ }
+ else if (config.isInfluxDB()) {
+ db = SQLConfig.DATABASE_INFLUXDB + " " + dbVersion;
+ }
else if (config.isTDengine()) {
db = SQLConfig.DATABASE_TDENGINE + " " + dbVersion;
}
+ else if (config.isIoTDB()) {
+ db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
+ }
+ else if (config.isSQLite()) {
+ db = SQLConfig.DATABASE_SQLITE + " " + dbVersion;
+ }
+ else if (config.isHive()) {
+ db = SQLConfig.DATABASE_HIVE + " " + dbVersion;
+ }
+ else if (config.isPresto()) {
+ db = SQLConfig.DATABASE_PRESTO + " " + dbVersion;
+ }
+ else if (config.isTrino()) {
+ db = SQLConfig.DATABASE_TRINO + " " + dbVersion;
+ }
+ else if (config.isSnowflake()) {
+ db = SQLConfig.DATABASE_SNOWFLAKE + " " + dbVersion;
+ }
+ else if (config.isDatabricks()) {
+ db = SQLConfig.DATABASE_DATABRICKS + " " + dbVersion;
+ }
+ else if (config.isMongoDB()) {
+ db = SQLConfig.DATABASE_MONGODB + " " + dbVersion;
+ }
+ else if (config.isCassandra()) {
+ db = SQLConfig.DATABASE_CASSANDRA + " " + dbVersion;
+ }
+ else if (config.isRedis()) {
+ db = SQLConfig.DATABASE_REDIS + " " + dbVersion;
+ }
+ else if (config.isKafka()) {
+ db = SQLConfig.DATABASE_KAFKA + " " + dbVersion;
+ }
else {
db = "";
}
From bc1035fec2356feb6f0cd81a6bd8f2b98d6a63c8 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 1 Mar 2025 17:46:11 +0800
Subject: [PATCH 091/145] =?UTF-8?q?=E4=BC=98=E5=8C=96=20SQLConfig=20?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BC=A9=E8=BF=9B=EF=BC=8C=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E5=9C=A8=20GitHub=20=E4=B8=8A=E5=9B=A0=E4=B8=BA=20tab=20?=
=?UTF-8?q?=E7=BC=A9=E8=BF=9B=E5=92=8C=20IDEA=20=E4=B8=8D=E4=B8=80?=
=?UTF-8?q?=E8=87=B4=E5=AF=BC=E8=87=B4=E4=B8=8A=E4=B8=8B=E6=98=BE=E7=A4=BA?=
=?UTF-8?q?=E6=B2=A1=E5=AF=B9=E9=BD=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/SQLConfig.java | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index d70de4b0e..e63687976 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -113,9 +113,9 @@ public interface SQLConfig {
// boolean isPLSQL();
// boolean isAnsiSQL();
- /**用来给 Table, Column 等系统属性表来绕过 MAX_SQL_COUNT 等限制
- * @return
- */
+ /**用来给 Table, Column 等系统属性表来绕过 MAX_SQL_COUNT 等限制
+ * @return
+ */
boolean limitSQLCount();
/**是否开启 WITH AS 表达式来简化 SQL 和提升性能
@@ -123,11 +123,11 @@ public interface SQLConfig {
*/
boolean isWithAsEnable();
/**允许增删改部分失败
- * @return
- */
- boolean allowPartialUpdateFailed();
+ * @return
+ */
+ boolean allowPartialUpdateFailed();
- @NotNull
+ @NotNull
String getIdKey();
@NotNull
String getUserIdKey();
From de742e729b38a47bfc35b5e811ce53a33b9e7fa0 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 2 Mar 2025 12:00:24 +0800
Subject: [PATCH 092/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?ManticoreSearch-=E6=9B=BF=E4=BB=A3=20Elasticsearch=20=E7=9A=84?=
=?UTF-8?q?=E8=BD=BB=E9=87=8F=E7=BA=A7=E6=90=9C=E7=B4=A2=E5=BC=95=E6=93=8E?=
=?UTF-8?q?=EF=BC=8C=E5=85=BC=E5=AE=B9=20MySQL=20=E5=8D=8F=E8=AE=AE?=
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 +-
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 11 ++++++++++-
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
.../java/apijson/orm/exception/CommonException.java | 3 +++
5 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index f3c7bb589..179373256 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.6.0
+ 7.7.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 12fe35cc4..6f9aa894c 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.6.0";
+ public static final String VERSION = "7.7.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 53d8633ab..9b901e6cb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -185,6 +185,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com
String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn
String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html
+ String DATABASE_MANTICORE = "MANTICORE"; // https://manticoresearch.com
String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com
String DATABASE_HIVE = "HIVE"; // https://hive.apache.org
String DATABASE_PRESTO = "PRESTO"; // Facebook PrestoDB https://prestodb.io
@@ -86,6 +87,7 @@ public interface SQLConfig {
boolean isDameng();
boolean isKingBase();
boolean isElasticsearch();
+ boolean isManticore();
boolean isClickHouse();
boolean isHive();
boolean isPresto();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 6dc15129c..23055198c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -211,6 +211,9 @@ else if (config.isKingBase()) {
else if (config.isElasticsearch()) {
db = SQLConfig.DATABASE_ELASTICSEARCH + " " + dbVersion;
}
+ else if (config.isManticore()) {
+ db = SQLConfig.DATABASE_MANTICORE + " " + dbVersion;
+ }
else if (config.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE + " " + dbVersion;
}
From 013441a045b5935bde59f6a54edc73340515f480 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Mon, 3 Mar 2025 00:35:19 +0800
Subject: [PATCH 093/145] =?UTF-8?q?=E5=8E=BB=E6=8E=89=20ManticoreSearch=20?=
=?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=E7=9A=84=20SQL=20=E5=85=B3=E9=94=AE?=
=?UTF-8?q?=E8=AF=8D=20AS?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 9b901e6cb..1512a5529 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1494,7 +1494,7 @@ public AbstractSQLConfig setTable(String table) { //Table已经在Parser中
}
public String getAs() {
- return isOracle() ? " " : " AS ";
+ return isOracle() || isManticore() ? " " : " AS ";
}
@Override
From 7ff081260e018f2217ba8f84693d4fad3b06601e Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:54:57 +0800
Subject: [PATCH 094/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20CockroachDB,=20Man?=
=?UTF-8?q?ticoreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e373f91b5..1f71bde90 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -30,6 +31,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -43,7 +45,8 @@ This source code is licensed under the Apache License Version 2.0
-
+
+
From 54ecd56fc71412a8cf3e1742cbb0b97e122e5fb7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:57:12 +0800
Subject: [PATCH 095/145] =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=20CockroachDB,=20?=
=?UTF-8?q?ManticoreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 1f71bde90..721e0adf6 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ This source code is licensed under the Apache License Version 2.0
+
From 69573d1d6dfd6101ccfc993b5e4ceb6d1e61de8f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Mar 2025 00:59:27 +0800
Subject: [PATCH 096/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20CockroachDB,=20Man?=
=?UTF-8?q?ticoreSearch,=20PosgGIS=20=E7=9A=84=E6=94=AF=E6=8C=81=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#--apijson
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 721e0adf6..57bed4104 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -47,8 +48,6 @@ This source code is licensed under the Apache License Version 2.0
-
-
From 86fd75a05ea581afac88d8a0bf20ef9453ce591b Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 9 Mar 2025 22:12:51 +0800
Subject: [PATCH 097/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?TimescaleDB-=E9=AB=98=E6=80=A7=E8=83=BD=E5=AE=9E=E6=97=B6?=
=?UTF-8?q?=E5=88=86=E6=9E=90=E6=97=B6=E5=BA=8F=E6=95=B0=E6=8D=AE=E5=BA=93?=
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 +-
.../src/main/java/apijson/orm/AbstractSQLConfig.java | 9 +++++++++
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
.../main/java/apijson/orm/exception/CommonException.java | 3 +++
5 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 179373256..2d0768fff 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.7.0
+ 7.8.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 6f9aa894c..86c2e61cc 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.7.0";
+ public static final String VERSION = "7.8.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 1512a5529..19446e69e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -193,6 +193,7 @@ public abstract class AbstractSQLConfig implements 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_TIMESCALEDB = "TIMESCALEDB"; // https://www.timescale.com
String DATABASE_IOTDB = "IOTDB"; // https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-JDBC.html
String DATABASE_REDIS = "REDIS"; // https://redisql.com
@@ -98,6 +99,7 @@ public interface SQLConfig {
boolean isMilvus();
boolean isInfluxDB();
boolean isTDengine();
+ boolean isTimescaleDB();
boolean isIoTDB();
boolean isRedis();
boolean isMongoDB();
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 23055198c..9dab4ea16 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -226,6 +226,9 @@ else if (config.isInfluxDB()) {
else if (config.isTDengine()) {
db = SQLConfig.DATABASE_TDENGINE + " " + dbVersion;
}
+ else if (config.isTimescaleDB()) {
+ db = SQLConfig.DATABASE_TIMESCALEDB + " " + dbVersion;
+ }
else if (config.isIoTDB()) {
db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
}
From f2f7fe660980b35b854dc196d0551547121b456b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 9 Mar 2025 22:36:36 +0800
Subject: [PATCH 098/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20TimescaleDB-?=
=?UTF-8?q?=E9=AB=98=E6=80=A7=E8=83=BD=E5=AE=9E=E6=97=B6=E5=88=86=E6=9E=90?=
=?UTF-8?q?=E6=97=B6=E5=BA=8F=E6=95=B0=E6=8D=AE=E5=BA=93=20=E7=9A=84?=
=?UTF-8?q?=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#--apijson
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 57bed4104..a5f474067 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ This source code is licensed under the Apache License Version 2.0
+
From ecb9bb729683e390332c45bd056a2a364451370c Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 20:52:55 +0800
Subject: [PATCH 099/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E5=AE=89=E5=85=A8=E6=89=AB=E6=8F=8F=E9=85=8D=E7=BD=AE=20yaml?=
=?UTF-8?q?=20=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
github/Tencent/APIJSON.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 github/Tencent/APIJSON.yml
diff --git a/github/Tencent/APIJSON.yml b/github/Tencent/APIJSON.yml
new file mode 100644
index 000000000..e9c40e3e0
--- /dev/null
+++ b/github/Tencent/APIJSON.yml
@@ -0,0 +1,13 @@
+tosr_no:
+
+account_mappings:
+ caohao-go: smallhowcao
+
+opensource_repository_information:
+ tencentopen_url: https://github.com/Tencent/APIJSON
+ tencentopen_name: APIJSON
+ status: ongoing
+ owner: smallhowcao
+ follower: smallhowcao
+ internal_id:
+ internal_url:
From 476f6e2a181fc3f110c92deb9d9f1bef6c6cbf3a Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 21:26:58 +0800
Subject: [PATCH 100/145] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Document-English.md | 28 ++++++++++++++--------------
Document.md | 30 +++++++++++++++---------------
2 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/Document-English.md b/Document-English.md
index 8d1fab8a4..c9a4f2948 100644
--- a/Document-English.md
+++ b/Document-English.md
@@ -4,7 +4,7 @@
Request:
{
"User":{
- }
+ }
}
@@ -44,10 +44,10 @@ Response:
Request:
{
"[]":{
- "count":3, //just get 3 results
- "User":{
- "@column":"id,name" //just get ids and names
- }
+ "count":3, //just get 3 results
+ "User":{
+ "@column":"id,name" //just get ids and names
+ }
}
}
@@ -134,13 +134,13 @@ Response:
Request:
{
"[]":{ //get an array
- "page":0, //pagination
+ "page":0, //pagination
"count":2,
- "Moment":{ //get a Moment
- "content$":"%a%" //filter condition: content contains 'a'
+ "Moment":{ //get a Moment
+ "content$":"%a%" //filter condition: content contains 'a'
},
"User":{
- "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
+ "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
"@column":"id,name,head" //get specified keys with the written order
},
"Comment[]":{ //get a Comment array, and unwrap Comment object
@@ -163,7 +163,7 @@ Response:
"id":15,
"userId":70793,
"date":1486541171000,
- "content":"APIJSON is a JSON Transmission Structure Protocol…",
+ "content":"APIJSON is a JSON Transmission Protocol…",
"praiseUserIdList":[
82055,
82002,
@@ -288,14 +288,14 @@ Response:
### 1. Methods and API endpoints
- Methods | URL | Request | Response
+ Methods | URL | Request | Response
------------ | ------------ | ------------ | ------------
-**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } } Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "code":200, "msg":"success" }
+**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } } Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
**HEAD**: A general way to get counts. You can use dev tools to make edits in a web browser. | base_url/head/ | { TableName:{ … } } {…} are conditions. Eg. Get the number of Moments posted by the user with `id = 38710`: { "Moment":{ "userId":38710 } } | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
**GETS**: Get data with high security and confidentiality. Eg. bank accounts, birth date. | base_url/gets/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **GET**. | Same as **GET**.
**HEADS**: Get counts of confidential data(eg. bank account).| base_url/heads/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **HEAD**. | Same as **HEAD**.
-**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user. Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
-**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`. Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" } | Same as **POST**.
+**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user. Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
+**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`. Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | Same as **POST**.
**DELETE**: Delete data. | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } You can also add multiple id as `id{}`. Or Delete contents with multiple id: { "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" } | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } Eg. { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
**Note**:
diff --git a/Document.md b/Document.md
index e20bfbb27..a97f7b823 100644
--- a/Document.md
+++ b/Document.md
@@ -30,7 +30,7 @@ https://github.com/Tencent/APIJSON
{
"User":{
"id":38710
- }
+ }
}
@@ -67,10 +67,10 @@ https://github.com/Tencent/APIJSON
请求:
{
"[]":{
- "count":3, //只要3个
- "User":{
- "@column":"id,name" //只要id,name这两个字段
- }
+ "count":3, //只要3个
+ "User":{
+ "@column":"id,name" //只要id,name这两个字段
+ }
}
}
@@ -156,14 +156,14 @@ https://github.com/Tencent/APIJSON
请求:
{
"[]":{ //请求一个数组
- "page":0, //数组条件
+ "page":0, //数组条件
"count":2,
- "Moment":{ //请求一个名为Moment的对象
- "content$":"%a%" //对象条件,搜索content中包含a的动态
+ "Moment":{ //请求一个名为Moment的对象
+ "content$":"%a%" //对象条件,搜索content中包含a的动态
},
"User":{
- "id@":"/Moment/userId", //User.id = Moment.userId 缺省引用赋值路径,从所处容器的父容器路径开始
- "@column":"id,name,head" //指定返回字段
+ "id@":"/Moment/userId", //User.id = Moment.userId 缺省引用赋值路径,从所处容器的父容器路径开始
+ "@column":"id,name,head" //指定返回字段
},
"Comment[]":{ //请求一个名为Comment的数组,并去除Comment包装
"count":2,
@@ -185,7 +185,7 @@ https://github.com/Tencent/APIJSON
"id":15,
"userId":70793,
"date":1486541171000,
- "content":"APIJSON is a JSON Transmission Structure Protocol…",
+ "content":"APIJSON is a JSON Transmission Protocol…",
"praiseUserIdList":[
82055,
82002,
@@ -378,14 +378,14 @@ https://github.com/Tencent/APIJSON
### 3.1 操作方法
- 方法及说明 | URL | Request | Response
+ 方法及说明 | URL | Request | Response
------------ | ------------ | ------------ | ------------
-GET: 普通获取数据, 可用浏览器调试 | base_url/get/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 235 的 Moment: [{ "Moment":{ "id":235 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&type=JSON&json={"Moment"%3A{"id"%3A235}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT * FROM Moment WHERE id=235 LIMIT 1` | { TableName:{ ... }, "code":200, "msg":"success" } 例如 { "Moment":{ "id":235, "userId":38710, "content":"APIJSON,let interfaces and documents go to hell !" }, "code":200, "msg":"success" }
+GET: 普通获取数据, 可用浏览器调试 | base_url/get/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 235 的 Moment: [{ "Moment":{ "id":235 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&type=JSON&json={"Moment"%3A{"id"%3A235}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT * FROM Moment WHERE id=235 LIMIT 1` | { TableName:{ ... }, "code":200, "msg":"success" } 例如 { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
HEAD: 普通获取数量, 可用浏览器调试 | base_url/head/ | { TableName:{ … } } {…}内为限制条件 例如获取一个 id = 38710 的 User 所发布的 Moment 总数: [{ "Moment":{ "userId":38710 } }](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fhead&type=JSON&json={"Moment"%3A{"userId"%3A38710}}) 后端校验通过后自动解析为 SQL 并执行: `SELECT count(*) FROM Moment WHERE userId=38710 LIMIT 1` | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } 例如 { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
GETS: 安全/私密获取数据, 用于获取钱包等 对安全性要求高的数据 | base_url/gets/ | 最外层加一个 "tag":tag,例如 ["tag":"Privacy"](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={"tag"%3A"Privacy","Privacy"%3A{"id"%3A82001}}),其它同GET | 同GET
HEADS: 安全/私密获取数量, 用于获取银行卡数量等 对安全性要求高的数据总数 | base_url/heads/ | 最外层加一个 "tag":tag,例如 ["tag":"Verify"](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fheads&type=JSON&json={"tag"%3A"Verify","Verify"%3A{"phone"%3A13000082001}}),其它同HEAD | 同HEAD
-POST: 新增数据 | base_url/post/ | 单个: { TableName:{ … }, "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON,let interfaces and documents go to hell !')` 批量: { TableName\[]:\[{ … }, { … } … ], "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON,let interfaces and documents go to hell !" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON,let interfaces and documents go to hell !');` `INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" } 批量: { TableName:{ "code":200, "msg":"success", "count":5, "id[]":[1, 2, 3, 4, 5] }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "count":2, "id[]":\[1, 2] }, "code":200, "msg":"success" }
-PUT: 修改数据, 只修改所传的字段 | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } {…} 中 id 或 id{} 至少传一个 例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON,let interfaces and documents go to hell !" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON,let%20interfaces%20and%20documents%20go%20to%20hell%20!"},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON,let interfaces and documents go to hell !' WHERE id=235 AND userId=82001 LIMIT 1` 批量除了 id{}:\[] 也可类似批量 POST,只是每个 {...} 里面都必须有 id。 "tag":"Comment[]" 对应对象 "Comment":{"id{}":[1,2,3]},表示指定记录全部统一设置; "tag":"Comment:[]" 多了冒号,对应数组 "Comment[]":[{"id":1},{"id":2},{"id":3}],表示每项单独设置 | 同POST
+POST: 新增数据 | base_url/post/ | 单个: { TableName:{ … }, "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON is the real-time coding-free, powerful and secure ORM')` 批量: { TableName\[]:\[{ … }, { … } … ], "tag":tag } {…}中id由后端生成,不能传 例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON is the real-time coding-free, powerful and secure ORM');` `INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" } 批量: { TableName:{ "code":200, "msg":"success", "count":5, "id[]":[1, 2, 3, 4, 5] }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "count":2, "id[]":\[1, 2] }, "code":200, "msg":"success" }
+PUT: 修改数据, 只修改所传的字段 | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } {…} 中 id 或 id{} 至少传一个 例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON is the real-time coding-free, powerful and secure ORM' WHERE id=235 AND userId=82001 LIMIT 1` 批量除了 id{}:\[] 也可类似批量 POST,只是每个 {...} 里面都必须有 id。 "tag":"Comment[]" 对应对象 "Comment":{"id{}":[1,2,3]},表示指定记录全部统一设置; "tag":"Comment:[]" 多了冒号,对应数组 "Comment[]":[{"id":1},{"id":2},{"id":3}],表示每项单独设置 | 同POST
DELETE: 删除数据 | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } {…} 中 id 或 id{} 至少传一个,一般只传 id 或 id{} 例如当前登录用户 82001 批量删除 id = 100,110,120 的 Comment: [{ "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fdelete&type=JSON&json={"Comment":{"id{}":[100,110,120]},"tag":"Comment[]"}) 后端校验通过后自动解析为 SQL 并执行: `DELETE FROM Comment WHERE id IN(100,110,120) AND userId=82001 LIMIT 3` | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
以上接口的简单形式: base_url/{method}/{tag} | GET: 普通获取数据 base_url/get/{tag} HEAD: 普通获取数量 base_url/head/{tag} GETS: 安全/私密获取数据 base_url/gets/{tag} HEADS: 安全/私密获取数量 base_url/heads/{tag} POST: 新增数据 base_url/post/{tag} PUT: 修改数据 base_url/put/{tag} DELETE: 删除数据 base_url/delete/{tag} | 例如安全/私密获取一个 id = 82001 的 Privacy: [base_url/gets/Privacy/ {"id":82001}](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets%2FPrivacy&type=JSON&json={"id"%3A82001}) 相当于 [base_url/gets/ {"tag":"Privacy", "Privacy":{"id":82001}}](http://apijson.cn/api/?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={"tag"%3A"Privacy","Privacy"%3A{"id"%3A82001}}) 例如批量修改 id = 114, 124 的 Comment 的 content: [base_url/put/Comemnt[]/ { "id{}":[114,124], "content":"test multi put" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput%2FComment[]&type=JSON&json={"id{}"%3A[114,124],"content"%3A"test%20multi%20put"}) 相当于 [base_url/put/ { "tag":"Comment[]", "Comment":{ "id{}":[114,124], "content":"test multi put" } }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"tag"%3A"Comment[]","Comment"%3A{"id{}"%3A[114,124],"content"%3A"test%20multi%20put"}}) | 同以上对应的方法
From 41be0a8ddc4fee5f61bb6b0ccfe43341f23046e6 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 21:38:11 +0800
Subject: [PATCH 101/145] Delete APIJSON.yml
---
github/Tencent/APIJSON.yml | 13 -------------
1 file changed, 13 deletions(-)
delete mode 100644 github/Tencent/APIJSON.yml
diff --git a/github/Tencent/APIJSON.yml b/github/Tencent/APIJSON.yml
deleted file mode 100644
index e9c40e3e0..000000000
--- a/github/Tencent/APIJSON.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-tosr_no:
-
-account_mappings:
- caohao-go: smallhowcao
-
-opensource_repository_information:
- tencentopen_url: https://github.com/Tencent/APIJSON
- tencentopen_name: APIJSON
- status: ongoing
- owner: smallhowcao
- follower: smallhowcao
- internal_id:
- internal_url:
From 632c806d630b481a8c33b703b32fa64b652f2c81 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 15 Mar 2025 22:00:26 +0800
Subject: [PATCH 102/145] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README-English.md | 20 ++++++++++----------
README.md | 34 +++++++++++++++++-----------------
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/README-English.md b/README-English.md
index 9329faf0e..beea69d6c 100644
--- a/README-English.md
+++ b/README-English.md
@@ -17,8 +17,8 @@ This source code is licensed under the Apache License Version 2.0
-
-
+
+
@@ -42,21 +42,21 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
+
+
+
-
-
+
+
-
-
+
+
@@ -179,7 +179,7 @@ See https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/READ
##
3. Frontend usage
You can skip this step and use [APIAuto](https://github.com/TommyLemon/APIAuto) or download App.
-See [Android](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Android/README-English.md), [iOS](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-iOS/README-English.md) or [JavaScript](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-JavaScript/README-English.md)
+See [Android](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Android/README-English.md), [iOS](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-iOS/README-English.md) or [JavaScript](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-JavaScript/README-English.md)
### Download App
diff --git a/README.md b/README.md
index a5f474067..67610d168 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,14 @@ This source code is licensed under the Apache License Version 2.0
English
- 通用文档
+ 通用文档
视频教程
测试用例
-
-
+
+
@@ -54,22 +54,22 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
@@ -189,16 +189,16 @@ https://github.com/Tencent/APIJSON/wiki
* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽)
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
-* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
+* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前六、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
-* **文档视频齐全** (项目介绍、快速上手、安装部署 等后端、前端、客户端的 图文解说、视频教程、代码注释 等)
+* **文档视频齐全** (项目介绍、快速上手、安装部署 等后端、前端、客户端的 图文解说、视频教程、代码注释 等)
* **功能丰富强大** (增删改查、分页排序、分组聚合、各种条件、各种 JOIN、各种子查询、跨库连表 等零代码实现)
* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防 SQL 注入)
-* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
+* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,蚂蚁集团源伞 Pinpoint 代码扫描分析报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
@@ -276,7 +276,7 @@ https://github.com/Tencent/APIJSON/issues/36
#### 2.前端上手
可以跳过这个步骤,直接使用 [APIAuto-机器学习HTTP接口工具](https://github.com/TommyLemon/APIAuto) 或 下载客户端App。
-见 [Android](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Android) 或 [iOS](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-iOS) 或 [JavaScript](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-JavaScript)
+见 [Android](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Android) 或 [iOS](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-iOS) 或 [JavaScript](https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-JavaScript)
### 下载客户端 App
@@ -546,7 +546,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
### 相关推荐
-[APIJSON, 让接口和文档见鬼去吧!](https://my.oschina.net/tommylemon/blog/805459)
+[APIJSON, 接口和文档的终结者!](https://my.oschina.net/tommylemon/blog/805459)
[腾讯业务百万数据 6s 响应,APIJSON 性能优化背后的故事](https://my.oschina.net/tommylemon/blog/5375645)
From a1caa259157632b981d7c274da7ab6859a53652f Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 00:31:08 +0800
Subject: [PATCH 103/145] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?QuestDB=20=E5=B9=B6=E6=94=AF=E6=8C=81=20~=20ASOF=20JOIN?=
=?UTF-8?q?=EF=BC=8CSAMPLE=20BY,=20LATEST=20ON,=20PARTITION=20BY,=20FILL(L?=
=?UTF-8?q?INEAR)=20=E7=AD=89=E5=85=B3=E9=94=AE=E8=AF=8D=E5=8F=8A=E4=B8=8E?=
=?UTF-8?q?=E8=AF=AD=E6=B3=95?=
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 | 68 ++++
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../main/java/apijson/orm/AbstractParser.java | 4 +
.../java/apijson/orm/AbstractSQLConfig.java | 377 ++++++++++++++++--
.../java/apijson/orm/AbstractSQLExecutor.java | 12 +-
.../src/main/java/apijson/orm/Join.java | 5 +-
.../src/main/java/apijson/orm/SQLConfig.java | 18 +
.../orm/exception/CommonException.java | 3 +
9 files changed, 461 insertions(+), 30 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 2d0768fff..2cf436bd5 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 7.8.0
+ 7.9.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 6f4359019..c69a03569 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -151,6 +151,10 @@ public JSONObject setUserIdIn(List list) {
public static final String KEY_GROUP = "@group"; //分组方式
public static final String KEY_HAVING = "@having"; //聚合函数条件,一般和@group一起用
public static final String KEY_HAVING_AND = "@having&"; //聚合函数条件,一般和@group一起用
+ public static final String KEY_SAMPLE = "@sample"; //取样方式
+ public static final String KEY_LATEST = "@latest"; //最近方式
+ public static final String KEY_PARTITION = "@partition"; //分区方式
+ public static final String KEY_FILL = "@fill"; //填充方式
public static final String KEY_ORDER = "@order"; //排序方式
public static final String KEY_KEY = "@key"; // key 映射,year:left(date,4);name_tag:(name,tag)
public static final String KEY_RAW = "@raw"; // 自定义原始 SQL 片段
@@ -185,6 +189,10 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST.add(KEY_GROUP);
TABLE_KEY_LIST.add(KEY_HAVING);
TABLE_KEY_LIST.add(KEY_HAVING_AND);
+ TABLE_KEY_LIST.add(KEY_SAMPLE);
+ TABLE_KEY_LIST.add(KEY_LATEST);
+ TABLE_KEY_LIST.add(KEY_PARTITION);
+ TABLE_KEY_LIST.add(KEY_FILL);
TABLE_KEY_LIST.add(KEY_ORDER);
TABLE_KEY_LIST.add(KEY_KEY);
TABLE_KEY_LIST.add(KEY_RAW);
@@ -410,6 +418,66 @@ public JSONObject setHaving(String keys, boolean isAnd) {
return puts(isAnd ? KEY_HAVING_AND : KEY_HAVING, keys);
}
+ /**set keys for sample by
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setSample(String)}
+ */
+ public JSONObject setSample(String... keys) {
+ return setSample(StringUtil.getString(keys, true));
+ }
+ /**set keys for sample by
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setSample(String keys) {
+ return puts(KEY_SAMPLE, keys);
+ }
+
+ /**set keys for latest on
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setLatest(String)}
+ */
+ public JSONObject setLatest(String... keys) {
+ return setLatest(StringUtil.getString(keys, true));
+ }
+ /**set keys for latest on
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setLatest(String keys) {
+ return puts(KEY_LATEST, keys);
+ }
+
+ /**set keys for partition by
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setPartition(String)}
+ */
+ public JSONObject setPartition(String... keys) {
+ return setPartition(StringUtil.getString(keys, true));
+ }
+ /**set keys for partition by
+ * @param keys key0, key1, key2 ...
+ * @return
+ */
+ public JSONObject setPartition(String keys) {
+ return puts(KEY_PARTITION, keys);
+ }
+
+ /**set keys for fill(key): fill(null), fill(linear), fill(prev)
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setFill(String)}
+ */
+ public JSONObject setFill(String... keys) {
+ return setFill(StringUtil.getString(keys, true));
+ }
+ /**set keys for fill(key): fill(null), fill(linear), fill(prev)
+ * @param keys key0, key1, key2 ...
+ * @return
+ */
+ public JSONObject setFill(String keys) {
+ return puts(KEY_FILL, keys);
+ }
+
/**set keys for order by
* @param keys key0, key1+, key2- ...
* @return {@link #setOrder(String)}
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 86c2e61cc..0916af410 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.8.0";
+ public static final String VERSION = "7.9.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/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 624830374..fd56b71b9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1501,6 +1501,10 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_GROUP);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING_AND);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_SAMPLE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_LATEST);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_PARTITION);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_FILL);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_ORDER);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_KEY);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_RAW);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 19446e69e..e9b6c0a02 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -194,6 +194,7 @@ public abstract class AbstractSQLConfig implements SQLConfig having; //聚合函数的字符串数组,','分隔
+ private String sample; //取样方式的字符串数组,','分隔
+ private String latest; //最近方式的字符串数组,','分隔
+ private String partition; //分区方式的字符串数组,','分隔
+ private String fill; //填充方式的字符串数组,','分隔
private String order; //排序方式的字符串数组,','分隔
+
private Map keyMap; //字段名映射,支持 name_tag:(name,tag) 多字段 IN,year:left(date,4) 截取日期年份等
private List raw; //需要保留原始 SQL 的字段,','分隔
private List json; //需要转为 JSON 的字段,','分隔
@@ -1103,6 +1109,19 @@ public String getSQLDatabase() {
return db == null ? DEFAULT_DATABASE : db; // "" 表示已设置,不需要用全局默认的 StringUtil.isEmpty(db, false)) {
}
+ @Override
+ public boolean isTSQL() { // 兼容 TSQL 语法
+ return isOracle() || isSQLServer() || isDb2();
+ }
+ @Override
+ public boolean isMSQL() { // 兼容 MySQL 语法,但不一定可以使用它的 JDBC/ODBC
+ return isMySQL() || isTiDB() || isMariaDB() || isSQLite() || isTDengine();
+ }
+ @Override
+ public boolean isPSQL() { // 兼容 PostgreSQL 语法,但不一定可以使用它的 JDBC/ODBC
+ return isPostgreSQL() || isCockroachDB() || isOpenGauss() || isInfluxDB() || isTimescaleDB() || isQuestDB() || isDuckDB();
+ }
+
@Override
public boolean isMySQL() {
return isMySQL(getSQLDatabase());
@@ -1287,6 +1306,14 @@ public static boolean isTimescaleDB(String db) {
return DATABASE_TIMESCALEDB.equals(db);
}
+ @Override
+ public boolean isQuestDB() {
+ return isQuestDB(getSQLDatabase());
+ }
+ public static boolean isQuestDB(String db) {
+ return DATABASE_QUESTDB.equals(db);
+ }
+
public boolean isIoTDB() {
return isIoTDB(getDatabase());
@@ -1487,7 +1514,7 @@ public String getTablePath() {
String q = getQuote();
String ns = isSurrealDB() ? getSQLNamespace() : null;
- String cl = isPostgreSQL() || isCockroachDB() || isOpenGauss() || isDuckDB() ? getSQLCatalog() : null;
+ String cl = isPSQL() ? getSQLCatalog() : null;
String sch = getSQLSchema();
String sqlTable = getSQLTable();
@@ -1715,6 +1742,274 @@ else if (SQL_FUNCTION_MAP.containsKey(method) == false) {
return method + parseSQLExpression(KEY_HAVING, expression.substring(start), containRaw, false, null);
}
+ @Override
+ public String getSample() {
+ return sample;
+ }
+ public AbstractSQLConfig setSample(String... conditions) {
+ return setSample(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setSample(String sample) {
+ this.sample = sample;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getSampleString(boolean hasPrefix) {
+ //加上子表的 sample
+ String joinSample = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getSample() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getSampleString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinSample += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String sample = StringUtil.getTrimedString(getSample());
+
+ String[] keys = StringUtil.split(sample);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinSample, true) ? "" : (hasPrefix ? " SAMPLE BY " : "") + joinSample;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ //if ("fill(null)".equals(item) || "fill(linear)".equals(item) || "fill(prev)".equals(item) || "fill(previous)".equals(item)) {
+ // continue;
+ //}
+
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isNumberOrAlpha(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @sample:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 字母或数字组合!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinSample, ", ");
+ }
+
+ @Override
+ public String getLatest() {
+ return latest;
+ }
+ public AbstractSQLConfig setLatest(String... conditions) {
+ return setLatest(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setLatest(String latest) {
+ this.latest = latest;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getLatestString(boolean hasPrefix) {
+ //加上子表的 latest
+ String joinLatest = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getLatest() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getLatestString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinLatest += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String latest = StringUtil.getTrimedString(getLatest());
+
+ String[] keys = StringUtil.split(latest);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinLatest, true) ? "" : (hasPrefix ? " LATEST ON " : "") + joinLatest;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @latest:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.getString(keys), joinLatest, ", ");
+ }
+
+ @Override
+ public String getPartition() {
+ return partition;
+ }
+ public AbstractSQLConfig setPartition(String... conditions) {
+ return setPartition(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setPartition(String partition) {
+ this.partition = partition;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getPartitionString(boolean hasPrefix) {
+ //加上子表的 partition
+ String joinPartition = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getPartition() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getPartitionString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinPartition += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String partition = StringUtil.getTrimedString(getPartition());
+
+ String[] keys = StringUtil.split(partition);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinPartition, true) ? "" : (hasPrefix ? " PARTITION BY " : "") + joinPartition;
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @partition:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinPartition, ", ");
+ }
+
+ @Override
+ public String getFill() {
+ return fill;
+ }
+ public AbstractSQLConfig setFill(String... conditions) {
+ return setFill(StringUtil.getString(conditions));
+ }
+ @Override
+ public AbstractSQLConfig setFill(String fill) {
+ this.fill = fill;
+ return this;
+ }
+ @JSONField(serialize = false)
+ public String getFillString(boolean hasPrefix) {
+ //加上子表的 fill
+ String joinFill = "";
+ if (joinList != null) {
+ boolean first = true;
+ for (Join j : joinList) {
+ if (j.isAppJoin()) {
+ continue;
+ }
+
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getFill() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
+
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ // if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // cfg.setAlias(cfg.getTable());
+ // }
+ String c = ((AbstractSQLConfig) cfg).getFillString(false);
+
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinFill += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
+ }
+ }
+
+ String fill = StringUtil.getTrimedString(getFill());
+
+ String[] keys = StringUtil.split(fill);
+ if (keys == null || keys.length <= 0) {
+ return StringUtil.isEmpty(joinFill, true) ? "" : (hasPrefix ? " FILL(" : "") + joinFill + ")";
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String item = keys[i];
+ String origin = item;
+
+ if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
+ //这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
+ if (StringUtil.isName(origin) == false) {
+ throw new IllegalArgumentException("预编译模式下 @fill:value 中 " + item + " 不合法! value 里面用 , 分割的"
+ + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ }
+ }
+
+ keys[i] = getKey(origin);
+ }
+
+ return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.getString(keys), joinFill, ", ") + ")";
+ }
+
@Override
public String getOrder() {
return order;
@@ -2741,35 +3036,38 @@ public static int getOffset(int page, int count) {
public String getLimitString() {
int count = getCount();
- if (isMilvus()) {
+ boolean isSurrealDB = isSurrealDB();
+ boolean isQuestDB = isQuestDB();
+ if (isSurrealDB || isQuestDB || isMilvus()) {
if (count == 0) {
Parser parser = getParser();
count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
}
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();
+ if (isQuestDB()) {
+ return " LIMIT " + offset + ", " + (offset + count);
+ }
+ else if (isSurrealDB()) {
+ return " START " + offset + " LIMIT " + count;
+ }
+ else {
+ return " LIMIT " + offset + ", " + count; // 目前 moql-transx 的限制
}
-
- int offset = getOffset(getPage(), count);
- return " START " + offset + " LIMIT " + count;
}
if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
return "";
}
+ boolean isOracle = isOracle();
return getLimitString(
- getPage()
- , getCount()
- , isOracle() || isSQLServer() || isDb2()
- , isOracle() || isDameng() || isKingBase()
- , isPresto() || isTrino()
- );
+ getPage()
+ , count
+ , isTSQL()
+ , isOracle || isDameng() || isKingBase()
+ , isPresto() || isTrino()
+ );
}
/**获取限制数量及偏移量
* @param page
@@ -3462,6 +3760,7 @@ protected String concatJoinWhereString(String whereString) throws Exception {
case "^": // SIDE JOIN: ! (A & B)
case "(": // ANTI JOIN: A & ! B
case ")": // FOREIGN JOIN: B & ! A
+ case "~": // ASOF JOIN: B ~= A
jc = j.getJoinConfig();
boolean isMain = jc.isMain();
jc.setMain(false).setPrepared(isPrepared()).setPreparedValueList(new ArrayList());
@@ -3547,7 +3846,7 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
throw new UnsupportedOperationException(
"join:value 中 value 里的 " + jt + "/" + j.getPath()
+ "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
- + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
+ + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN, ~ ASOF ] 之外的 JOIN 类型 !"
);
}
}
@@ -3986,7 +4285,7 @@ public String getRegExpString(String key, String column, Object[] values, int ty
*/
@JSONField(serialize = false)
public String getRegExpString(String key, String column, String value, boolean ignoreCase) {
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
return getKey(column) + " ~" + (ignoreCase ? "* " : " ") + getValue(key, column, value);
}
if (isOracle() || isDameng() || isKingBase() || (isMySQL() && getDBVersionNums()[0] >= 8)) {
@@ -4312,7 +4611,7 @@ public String getContainString(String key, String column, Object[] childs, int t
}
condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
condition += (getKey(column) + " @> " + getValue(key, column, newJSONArray(c)));
// operator does not exist: jsonb @> character varying "[" + c + "]");
}
@@ -4763,11 +5062,15 @@ private static String getConditionString(String table, AbstractSQLConfig config)
String aggregation;
if (RequestMethod.isGetMethod(config.getMethod(), true)) {
aggregation = config.getGroupString(true) + config.getHavingString(true)
+ + config.getSampleString(true) + config.getLatestString(true)
+ + config.getPartitionString(true) + config.getFillString(true)
+ config.getOrderString(true);
}
else if (RequestMethod.isHeadMethod(config.getMethod(), true)) {
// TODO 加参数 isPagenation 判断是 GET 内分页 query:2 查总数,不用加这些条件
- aggregation = config.getGroupString(true) + config.getHavingString(true) ;
+ aggregation = config.getGroupString(true) + config.getHavingString(true)
+ + config.getSampleString(true) + config.getLatestString(true)
+ + config.getPartitionString(true) + config.getFillString(true);
}
else if (config.getMethod() == PUT || config.getMethod() == DELETE) {
aggregation = config.getHavingString(true) ;
@@ -4890,12 +5193,16 @@ public String getJoinString() throws Exception {
sql = " INNER JOIN " + jc.getTablePath();
sql = concatJoinOn(sql, quote, j, jt, onList);
break;
+ case "~": // ASOF JOIN: B ~= A
+ sql = " ASOF JOIN " + jc.getTablePath();
+ sql = concatJoinOn(sql, quote, j, jt, onList);
+ break;
default:
String k = jc.getTableKey();
throw new UnsupportedOperationException(
"join:value 中 value 里的 " + k + "/" + j.getPath()
+ "错误!不支持 " + k + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
- + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
+ + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN, ~ ASOF ] 之外的 JOIN 类型 !"
);
}
@@ -5018,7 +5325,7 @@ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) {
+ if (isPSQL()) {
sql += (first ? ON : AND) + lk + (isNot ? NOT : "") + " ~" + (ignoreCase ? "* " : " ") + rk;
}
else if (isOracle() || isDameng() || isKingBase()) {
@@ -5073,7 +5380,7 @@ else if ("{}".equals(rt) || "<>".equals(rt)) {
String arrKeyPath = isIn ? rk : lk;
String itemKeyPath = isIn ? lk : rk;
- if (isPostgreSQL() || isCockroachDB() || isInfluxDB()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
+ if (isPSQL()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ " IS NOT NULL AND " + arrKeyPath + " @> " + itemKeyPath) + (isNot ? ") " : "");
}
@@ -5308,6 +5615,10 @@ else if (userId instanceof Subquery) {}
String group = request.getString(KEY_GROUP);
Object having = request.get(KEY_HAVING);
String havingAnd = request.getString(KEY_HAVING_AND);
+ String sample = request.getString(KEY_SAMPLE);
+ String latest = request.getString(KEY_LATEST);
+ String partition = request.getString(KEY_PARTITION);
+ String fill = request.getString(KEY_FILL);
String order = request.getString(KEY_ORDER);
Object keyMap = request.get(KEY_KEY);
String raw = request.getString(KEY_RAW);
@@ -5337,6 +5648,10 @@ else if (userId instanceof Subquery) {}
request.remove(KEY_GROUP);
request.remove(KEY_HAVING);
request.remove(KEY_HAVING_AND);
+ request.remove(KEY_SAMPLE);
+ request.remove(KEY_LATEST);
+ request.remove(KEY_PARTITION);
+ request.remove(KEY_FILL);
request.remove(KEY_ORDER);
request.remove(KEY_KEY);
request.remove(KEY_RAW);
@@ -5841,6 +6156,10 @@ else if (keyMap != null) {
config.setGroup(group);
config.setHaving(havingMap);
config.setHavingCombine(havingCombine);
+ config.setSample(sample);
+ config.setLatest(latest);
+ config.setPartition(partition);
+ config.setFill(fill);
config.setOrder(order);
String[] jsons = StringUtil.split(json);
@@ -5905,6 +6224,18 @@ else if (keyMap != null) {
if (havingAnd != null) {
request.put(KEY_HAVING_AND, havingAnd);
}
+ if (sample != null) {
+ request.put(KEY_SAMPLE, sample);
+ }
+ if (latest != null) {
+ request.put(KEY_LATEST, latest);
+ }
+ if (partition != null) {
+ request.put(KEY_PARTITION, partition);
+ }
+ if (fill != null) {
+ request.put(KEY_FILL, fill);
+ }
if (order != null) {
request.put(KEY_ORDER, order);
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 9393f43be..2f0482091 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1185,12 +1185,16 @@ public PreparedStatement getStatement(@NotNull SQLConfig config, String sql)
}
}
else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
- //if (config.isPresto() || config.isTrino()) {
+ // if (config.isPresto() || config.isTrino()) {
// statement = getConnection(config).prepareStatement(sql); // , ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
- //} else {
+ // } else {
// statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
- //}
- if (config.isMySQL() || config.isPostgreSQL() || config.isCockroachDB() || config.isOracle() || config.isSQLServer() || config.isDb2()) {
+ // }
+
+ // TODO 补充各种支持 TYPE_SCROLL_SENSITIVE 和 CONCUR_UPDATABLE 的数据库
+ if (config.isMySQL() || config.isTiDB() || config.isMariaDB() || config.isOracle() || config.isSQLServer() || config.isDb2()
+ || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
+ ) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
statement = getConnection(config).prepareStatement(sql);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index f648ff8bf..449208bd9 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -19,7 +19,7 @@ public class Join {
private String path; // /User/id@
- private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN
+ private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN, "~" ASOF
private String table; // User
private String alias; // owner
private int count = 1; // 当app join子表,需要返回子表的行数,默认1行;
@@ -143,6 +143,9 @@ public boolean isAntiJoin() {
public boolean isForeignJoin() {
return ")".equals(getJoinType());
}
+ public boolean isAsofJoin() {
+ return "~".equals(getJoinType());
+ }
public boolean isLeftOrRightJoin() {
String jt = getJoinType();
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 64e6273ee..ac541c8da 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -40,6 +40,7 @@ public interface SQLConfig {
String DATABASE_INFLUXDB = "INFLUXDB"; // https://www.influxdata.com/products/influxdb-overview
String DATABASE_TDENGINE = "TDENGINE"; // https://tdengine.com
String DATABASE_TIMESCALEDB = "TIMESCALEDB"; // https://www.timescale.com
+ String DATABASE_QUESTDB = "QUESTDB"; // https://questdb.com
String DATABASE_IOTDB = "IOTDB"; // https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-JDBC.html
String DATABASE_REDIS = "REDIS"; // https://redisql.com
@@ -77,6 +78,10 @@ public interface SQLConfig {
SQLConfig setTag(String tag);
+ boolean isTSQL();
+ boolean isMSQL();
+ boolean isPSQL();
+
boolean isMySQL();
boolean isPostgreSQL();
boolean isSQLServer();
@@ -100,6 +105,7 @@ public interface SQLConfig {
boolean isInfluxDB();
boolean isTDengine();
boolean isTimescaleDB();
+ boolean isQuestDB();
boolean isIoTDB();
boolean isRedis();
boolean isMongoDB();
@@ -317,6 +323,18 @@ default int[] getDBVersionNums() {
String getHavingCombine();
SQLConfig setHavingCombine(String havingCombine);
+ String getSample();
+ SQLConfig setSample(String order);
+
+ String getLatest();
+ SQLConfig setLatest(String latest);
+
+ String getPartition();
+ SQLConfig setPartition(String partition);
+
+ String getFill();
+ SQLConfig setFill(String fill);
+
String getOrder();
SQLConfig setOrder(String order);
diff --git a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
index 9dab4ea16..0acced3be 100755
--- a/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
+++ b/APIJSONORM/src/main/java/apijson/orm/exception/CommonException.java
@@ -229,6 +229,9 @@ else if (config.isTDengine()) {
else if (config.isTimescaleDB()) {
db = SQLConfig.DATABASE_TIMESCALEDB + " " + dbVersion;
}
+ else if (config.isQuestDB()) {
+ db = SQLConfig.DATABASE_QUESTDB + " " + dbVersion;
+ }
else if (config.isIoTDB()) {
db = SQLConfig.DATABASE_IOTDB + " " + dbVersion;
}
From d8b8e5783fc632043b25d89b818aaf25f24c4d24 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 15:45:08 +0800
Subject: [PATCH 104/145] =?UTF-8?q?QuestDB:=20=E8=A7=A3=E5=86=B3=20JOIN=20?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BB=E8=A1=A8=E7=A9=BA=E5=AF=B9=E8=B1=A1?=
=?UTF-8?q?=E5=B9=B6=E6=9C=AA=E8=BF=94=E5=9B=9E=E5=89=AF=E8=A1=A8=EF=BC=8C?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20QuestDB=20=E8=87=AA=E5=8A=A8=E6=8A=8A?=
=?UTF-8?q?=E5=89=AF=E8=A1=A8=20id=20=E7=AD=89=E4=B8=8E=E4=B8=BB=E8=A1=A8?=
=?UTF-8?q?=E9=87=8D=E5=90=8D=E5=AD=97=E6=AE=B5=E6=94=B9=E6=88=90=20id1=20?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLExecutor.java | 235 ++++++++++--------
1 file changed, 137 insertions(+), 98 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 2f0482091..49f51438e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -198,6 +198,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
ResultSet rs = null;
List resultList = null;
Map childMap = null;
+ Map keyMap = null;
try {
if (unknownType) {
@@ -393,6 +394,8 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
// 直接用数组存取更快 Map columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new HashMap<>(length);
Join[] columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new Join[length];
+ Map repeatMap = columnIndexAndJoinMap == null || ! config.isQuestDB() ? null : new HashMap<>();
+ keyMap = repeatMap == null ? null : new HashMap<>();
// int viceColumnStart = length + 1; //第一个副表字段的index
@@ -431,34 +434,69 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
List column = config.getColumn();
int mainColumnSize = column == null ? 0 : column.size();
- // FIXME 主副表同名导致主表数据当成副表数据 { "[]": { "join": "": 0 }, "Comment:to": { "@column": "id,content", "id@": "/Comment/toId" } }, "@explain": true }
boolean toFindJoin = mainColumnSize <= 0 || i > mainColumnSize; // 主表就不用找 JOIN 配置
if (StringUtil.isEmpty(sqlTable, true)) {
+ //sqlTable = null;
+
if (toFindJoin) { // 在主表字段数量内的都归属主表
long startTime3 = System.currentTimeMillis();
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
//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())))
+ // boolean isEmpty = curItem == null || curItem.isEmpty();
+ String key = getKey(config, rs, rsmd, index, curItem, i, childMap, keyMap);
+ char last = repeatMap == null ? 0 : key.charAt(key.length() - 1);
+ String repeatKey = last < '0' || last > '9' ? null : key.substring(0, key.length() - 1);
+ Integer repeatCount = repeatKey == null ? null : repeatMap.get(repeatKey);
+ int nc = repeatCount == null ? 1 : repeatCount + 1;
+ if (last == nc + '0') {
+ keyMap.put(key, repeatKey);
+ repeatMap.put(repeatKey, nc);
+ key = repeatKey; // QuestDB 会自动把副表与主表同名的字段重命名,例如 id 改为 id1, date 改为 date1
+ }
+
+ if (i > 1 && ( (curItem != null && curItem.containsKey(key))
+ || (StringUtil.isNotEmpty(key) && StringUtil.equals(key, 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();
+
+ lastViceTableStart ++;
lastViceColumnStart += lastColumn == null ? 1 : lastColumn.size();
}
+ else if (isMain) {
+ for (int j = 0; j < joinList.size(); j++) {
+ Join join = joinList.get(j);
+ SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
+ List c = cfg == null ? null : cfg.getColumn();
+
+ if (cfg != null) {
+ sqlTable = cfg.getSQLTable();
+ sqlAlias = cfg.getAlias();
+ lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
+ lastViceColumnStart = i + 1;
+
+ curJoin = join;
+ curConfig = cfg;
+ curColumn = c;
+
+ toFindJoin = false;
+ isMain = false;
+ break;
+ }
+ }
+ }
+
reseted = true;
}
//}
sqlResultDuration += System.currentTimeMillis() - startTime3;
- if (StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
+ if (toFindJoin && StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
+ //sqlTable = null; // QuestDB 等 rsmd.getTableName(i) 返回 "" 导致以下 StringUtil.equalsIgnoreCase 对比失败
int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
int joinCount = joinList.size();
@@ -469,11 +507,11 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
nextViceColumnStart += (c != null && ! c.isEmpty() ?
c.size() : (
- StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
+ StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
&& StringUtil.equals(sqlAlias, lastAliasName) ? 1 : 0
- )
- );
- if (i < nextViceColumnStart || j >= joinCount - 1) {
+ )
+ );
+ if (i < nextViceColumnStart) { // 导致只 JOIN 一张副表时主表数据放到副表 || j >= joinCount - 1) {
sqlTable = cfg.getSQLTable();
sqlAlias = cfg.getAlias();
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
@@ -496,8 +534,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
toFindJoin = false;
}
}
- }
- else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWith("\""))){
+ } else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWith("\""))){
sqlTable = sqlTable.substring(1, sqlTable.length() - 1);
}
@@ -512,7 +549,7 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
if (cfg != null && StringUtil.equalsIgnoreCase(sqlTable, cfg.getSQLTable())
- ) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
+ ) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
curJoin = join;
@@ -621,7 +658,7 @@ else if (hasPK) {
}
}
- curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
+ curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap, keyMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
}
if (viceItem != null) {
@@ -671,7 +708,7 @@ else if (hasPK) {
// @ APP JOIN 查询副表并缓存到 childMap <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Map> appJoinChildMap = new HashMap<>();
childMap.forEach((viceSql, item) -> appJoinChildMap.put(viceSql, Arrays.asList(item)));
- executeAppJoin(config, resultList, appJoinChildMap);
+ executeAppJoin(config, resultList, appJoinChildMap, keyMap);
// @ APP JOIN 查询副表并缓存到 childMap >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -713,7 +750,7 @@ else if (hasPK) {
* @param childMap
* @throws Exception
*/
- protected void executeAppJoin(SQLConfig config, List resultList, Map> childMap) throws Exception {
+ protected void executeAppJoin(SQLConfig config, List resultList, Map> childMap, Map keyMap) throws Exception {
List joinList = config.getJoinList();
if (joinList != null) {
@@ -737,27 +774,27 @@ protected void executeAppJoin(SQLConfig config, List resultList,
On on = onList == null || onList.isEmpty() ? null : onList.get(0); // APP JOIN 应该有且只有一个 ON 条件
String originKey = on == null ? null : on.getOriginKey();
if (originKey == null) {
- throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getOriginKey() = null!"));
+ throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getOriginKey() = null!"));
}
String key = on.getKey();
if (key == null) {
- throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getKey() = null!"));
+ throw new NullPointerException("服务器内部错误,List 中 Join.onList[0" + (on == null ? "] = null!" : ".getKey() = null!"));
}
// 取出 "id@": "@/User/userId" 中所有 userId 的值
List targetValueList = new ArrayList<>();
for (int i = 0; i < resultList.size(); i++) {
- JSONObject mainTable = resultList.get(i);
- Object targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());
+ JSONObject mainTable = resultList.get(i);
+ Object targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());
- if (targetValue != null && targetValueList.contains(targetValue) == false) {
- targetValueList.add(targetValue);
- }
+ if (targetValue != null && targetValueList.contains(targetValue) == false) {
+ targetValueList.add(targetValue);
+ }
}
if (targetValueList.isEmpty() && config.isExplain() == false) {
- throw new NotExistException("targetValueList.isEmpty() && config.isExplain() == false");
+ throw new NotExistException("targetValueList.isEmpty() && config.isExplain() == false");
}
// 替换为 "id{}": [userId1, userId2, userId3...]
@@ -796,38 +833,38 @@ protected void executeAppJoin(SQLConfig config, List resultList,
String sql2 = null;
if (childCount > 0 && isOne2Many && (jc.isMySQL() == false || jc.getDBVersionNums()[0] >= 8)) {
- // 加 row_number 字段并不会导致 count 等聚合函数统计出错,结果偏大,SQL JOIN 才会,之前没发现是因为缓存失效 bug
- // boolean noAggrFun = true;
- // List column = jc.getColumn();
- // if (column != null) {
- // for (String c : column) {
- // int start = c == null ? -1 : c.indexOf("(");
- // int end = start <= 0 ? -1 : c.lastIndexOf(")");
- // if (start > 0 && end > start) {
- // String fun = c.substring(0, start);
- // if (AbstractSQLConfig.SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
- // noAggrFun = false;
- // break;
- // }
- // }
- // }
- // }
- //
- // if (noAggrFun) { // 加 row_number 字段会导致 count 等聚合函数统计出错,结果偏大?
- String q = jc.getQuote();
- sql2 = prepared && jc.isTDengine() == false ? jc.getSQL(true) : sql;
-
- String prefix = "SELECT * FROM(";
- String rnStr = ", row_number() OVER (PARTITION BY " + q + key + q + ((AbstractSQLConfig) jc).getOrderString(true) + ") _row_num_ FROM ";
- String suffix = ") _t WHERE ( (_row_num_ <= " + childCount + ") )" + (allChildCount > 0 ? " LIMIT " + allChildCount : "");
-
- sql2 = prefix
- // 放一块逻辑更清晰,也避免解析 * 等不支持或性能开销 + sql
- + sql2.replaceFirst(" FROM ", rnStr) // * 居然只能放在 row_number() 前面,放后面就报错 "SELECT ", rnStr)
- + suffix;
-
- sql = prepared ? (prefix + sql.replaceFirst(" FROM ", rnStr) + suffix) : sql2;
- // }
+ // 加 row_number 字段并不会导致 count 等聚合函数统计出错,结果偏大,SQL JOIN 才会,之前没发现是因为缓存失效 bug
+ // boolean noAggrFun = true;
+ // List column = jc.getColumn();
+ // if (column != null) {
+ // for (String c : column) {
+ // int start = c == null ? -1 : c.indexOf("(");
+ // int end = start <= 0 ? -1 : c.lastIndexOf(")");
+ // if (start > 0 && end > start) {
+ // String fun = c.substring(0, start);
+ // if (AbstractSQLConfig.SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
+ // noAggrFun = false;
+ // break;
+ // }
+ // }
+ // }
+ // }
+ //
+ // if (noAggrFun) { // 加 row_number 字段会导致 count 等聚合函数统计出错,结果偏大?
+ String q = jc.getQuote();
+ sql2 = prepared && jc.isTDengine() == false ? jc.getSQL(true) : sql;
+
+ String prefix = "SELECT * FROM(";
+ String rnStr = ", row_number() OVER (PARTITION BY " + q + key + q + ((AbstractSQLConfig) jc).getOrderString(true) + ") _row_num_ FROM ";
+ String suffix = ") _t WHERE ( (_row_num_ <= " + childCount + ") )" + (allChildCount > 0 ? " LIMIT " + allChildCount : "");
+
+ sql2 = prefix
+ // 放一块逻辑更清晰,也避免解析 * 等不支持或性能开销 + sql
+ + sql2.replaceFirst(" FROM ", rnStr) // * 居然只能放在 row_number() 前面,放后面就报错 "SELECT ", rnStr)
+ + suffix;
+
+ sql = prepared ? (prefix + sql.replaceFirst(" FROM ", rnStr) + suffix) : sql2;
+ // }
}
boolean isExplain = jc.isExplain();
@@ -846,12 +883,12 @@ protected void executeAppJoin(SQLConfig config, List resultList,
try {
long executedSQLStartTime = 0;
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
- executedSQLCount ++;
- executedSQLStartTime = System.currentTimeMillis();
+ executedSQLCount ++;
+ executedSQLStartTime = System.currentTimeMillis();
}
rs = executeQuery(jc, sql2);
if (isExplain == false) {
- executedSQLDuration += System.currentTimeMillis() - executedSQLStartTime;
+ executedSQLDuration += System.currentTimeMillis() - executedSQLStartTime;
}
int count = 0;
@@ -876,7 +913,7 @@ protected void executeAppJoin(SQLConfig config, List resultList,
JSONObject result = new JSONObject(true);
for (int i = 1; i <= length; i++) {
- result = onPutColumn(jc, rs, rsmd, index, result, i, null, null);
+ result = onPutColumn(jc, rs, rsmd, index, result, i, null, null, keyMap);
}
//每个 result 都要用新的 SQL 来存 childResultMap = onPutTable(config, rs, rsmd, childResultMap, index, result);
@@ -890,21 +927,19 @@ protected void executeAppJoin(SQLConfig config, List resultList,
List results = childMap.get(cacheSql);
if (results == null || skipMap.get(cacheSql) == null) { // 避免添加重复数据
- results = new ArrayList<>(childCount);
- childMap.put(cacheSql, results);
- skipMap.put(cacheSql, Boolean.TRUE);
+ results = new ArrayList<>(childCount);
+ childMap.put(cacheSql, results);
+ skipMap.put(cacheSql, Boolean.TRUE);
}
if (childCount <= 0 || results.size() < childCount) { // 避免超过子数组每页数量
- // if (count == 1 && results.isEmpty() == false) { // 避免添加重复数据
- // results.clear();
- // }
- results.add(result); //缓存到 childMap
- count ++;
- Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
+ // if (count == 1 && results.isEmpty() == false) { // 避免添加重复数据
+ // results.clear();
+ // }
+ results.add(result); //缓存到 childMap
+ count ++;
+ Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
}
- }
- }
finally {
if (rs != null) {
try {
@@ -939,29 +974,24 @@ protected void executeAppJoin(SQLConfig config, List resultList,
* @throws Exception
*/
protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Join join, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Join join, Map childMap
+ , Map keyMap) throws Exception {
if (table == null) { // 对应副表 viceSql 不能生成正常 SQL, 或者是 ! - Outer, ( - ANTI JOIN 的副表这种不需要缓存及返回的数据
Log.i(TAG, "onPutColumn table == null >> return table;");
return table;
}
- if (isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap)) {
- Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap) >> return table;");
+ if (isHideColumn(config, rs, rsmd, row, table, columnIndex, childMap, keyMap)) {
+ Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, row, table, columnIndex, childMap) >> return table;");
return table;
}
- String label = getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap);
- Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, label, childMap);
+ String label = getKey(config, rs, rsmd, row, table, columnIndex, childMap, keyMap);
+ Object value = getValue(config, rs, rsmd, row, table, columnIndex, label, childMap, keyMap);
// 主表必须 put 至少一个 null 进去,否则全部字段为 null 都不 put 会导致中断后续正常返回值
- if (value != null) {
+ if (value != null || ENABLE_OUTPUT_NULL_COLUMN || (join == null && table.isEmpty())) {
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;
@@ -971,7 +1001,7 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
* @param config
* @param rs
* @param rsmd
- * @param tablePosition
+ * @param row
* @param table
* @param columnIndex
* @param childMap
@@ -979,7 +1009,8 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSe
* @throws SQLException
*/
protected boolean isHideColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws SQLException {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Map childMap
+ , Map keyMap) throws SQLException {
return rsmd.getColumnName(columnIndex).startsWith("_");
}
@@ -999,12 +1030,11 @@ protected List onPutTable(@NotNull SQLConfig config, @NotNull Res
return resultList;
}
-
-
protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, Map childMap
+ , Map keyMap) throws Exception {
long startTime = System.currentTimeMillis();
- String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? label : label.substring(dotIndex + 1);
sqlResultDuration += System.currentTimeMillis() - startTime;
if (config.isHive()) {
@@ -1018,18 +1048,26 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @No
}
}
+ if (keyMap != null && ! keyMap.isEmpty()) {
+ String nk = keyMap.get(key);
+ if (StringUtil.isNotEmpty(nk, true)) {
+ key = nk; // QuestDB 会自动把副表与主表同名的字段重命名,例如 id 改为 id1, date 改为 date1
+ }
+ }
+
return key;
}
protected Object getValue(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, String lable, Map childMap) throws Exception {
+ , final int row, @NotNull JSONObject table, final int columnIndex, String label
+ , Map childMap, Map keyMap) throws Exception {
long startTime = System.currentTimeMillis();
Object value = rs.getObject(columnIndex);
sqlResultDuration += System.currentTimeMillis() - startTime;
// Log.d(TAG, "name:" + rsmd.getColumnName(i));
- // Log.d(TAG, "lable:" + rsmd.getColumnLabel(i));
+ // Log.d(TAG, "label:" + rsmd.getColumnLabel(i));
// Log.d(TAG, "type:" + rsmd.getColumnType(i));
// Log.d(TAG, "typeName:" + rsmd.getColumnTypeName(i));
@@ -1093,6 +1131,7 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
castToJson = json != null && json.contains(lable);
+ castToJson = json != null && json.contains(label);
}
if (castToJson) {
try {
@@ -1133,13 +1172,13 @@ public Object getNumVal(Number value) {
/**判断是否为JSON类型
* @param config
- * @param lable
+ * @param label
* @param rsmd
* @param position
* @return
*/
@Override
- public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable) {
+ public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String label) {
try {
long startTime = System.currentTimeMillis();
String column = rsmd.getColumnTypeName(position);
@@ -1158,16 +1197,16 @@ public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd,
e.printStackTrace();
}
// List json = config.getJson();
- // return json != null && json.contains(lable);
+ // return json != null && json.contains(label);
return false;
}
-
@Override // 重写是为了返回类型从 Statement 改为 PreparedStatement,避免其它方法出错
public PreparedStatement getStatement(@NotNull SQLConfig config) throws Exception {
return getStatement(config, null);
}
+
@Override
public PreparedStatement getStatement(@NotNull SQLConfig config, String sql) throws Exception {
if (StringUtil.isEmpty(sql)) {
@@ -1193,7 +1232,7 @@ else if (RequestMethod.isGetMethod(config.getMethod(), true)) {
// TODO 补充各种支持 TYPE_SCROLL_SENSITIVE 和 CONCUR_UPDATABLE 的数据库
if (config.isMySQL() || config.isTiDB() || config.isMariaDB() || config.isOracle() || config.isSQLServer() || config.isDb2()
- || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
+ || config.isPostgreSQL() || config.isCockroachDB() || config.isOpenGauss() || config.isTimescaleDB() || config.isQuestDB()
) {
statement = getConnection(config).prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else {
From 9a45d3381e9e27095baf6367078283d7f7d98a85 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 15:46:50 +0800
Subject: [PATCH 105/145] =?UTF-8?q?QuestDB:=20=E8=A7=A3=E5=86=B3=20JOIN=20?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BB=E8=A1=A8=E7=A9=BA=E5=AF=B9=E8=B1=A1?=
=?UTF-8?q?=E5=B9=B6=E6=9C=AA=E8=BF=94=E5=9B=9E=E5=89=AF=E8=A1=A8=EF=BC=8C?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20QuestDB=20=E8=87=AA=E5=8A=A8=E6=8A=8A?=
=?UTF-8?q?=E5=89=AF=E8=A1=A8=20id=20=E7=AD=89=E4=B8=8E=E4=B8=BB=E8=A1=A8?=
=?UTF-8?q?=E9=87=8D=E5=90=8D=E5=AD=97=E6=AE=B5=E6=94=B9=E6=88=90=20id1=20?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractSQLExecutor.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 49f51438e..35d1efda2 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -940,6 +940,8 @@ protected void executeAppJoin(SQLConfig config, List resultList,
count ++;
Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result); childMap.size() = " + childMap.size());
}
+ }
+ }
finally {
if (rs != null) {
try {
@@ -1101,7 +1103,7 @@ else if (value instanceof Month) {
else if (value instanceof DayOfWeek) {
value = ((DayOfWeek) value).getValue();
}
- else if (value instanceof String && isJSONType(config, rsmd, columnIndex, lable)) { //json String
+ else if (value instanceof String && isJSONType(config, rsmd, columnIndex, label)) { //json String
castToJson = true;
}
else if (value instanceof Blob) { //FIXME 存的是 abcde,取出来直接就是 [97, 98, 99, 100, 101] 这种 byte[] 类型,没有经过以下处理,但最终序列化后又变成了字符串 YWJjZGU=
@@ -1130,7 +1132,7 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
- castToJson = json != null && json.contains(lable);
+ castToJson = json != null && json.contains(label);
castToJson = json != null && json.contains(label);
}
if (castToJson) {
From 58404703b09c8a123a5018d31d66b2b49256cb35 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:18:39 +0800
Subject: [PATCH 106/145] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=AF=AF=E5=9C=A8?=
=?UTF-8?q?=E5=AD=90=E6=9F=A5=E8=AF=A2=E6=8B=BC=E6=8E=A5=20LIMIT=EF=BC=9B?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20@sample=20SAMPLE=20BY=20@fill=20FILL=20?=
=?UTF-8?q?=E7=9A=84=20SQL=20=E6=8B=BC=E6=8E=A5=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/StringUtil.java | 15 ++++++
.../java/apijson/orm/AbstractSQLConfig.java | 47 ++++++++++---------
2 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index c6caf21e7..49c72677a 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -343,6 +343,7 @@ public static boolean isNotEmpty(String s, boolean trim) {
public static final Pattern PATTERN_PHONE;
public static final Pattern PATTERN_EMAIL;
public static final Pattern PATTERN_ID_CARD;
+ public static final Pattern PATTERN_NUM_OR_ALPHA;
public static final Pattern PATTERN_ALPHA;
public static final Pattern PATTERN_PASSWORD; //TODO
public static final Pattern PATTERN_NAME;
@@ -351,6 +352,7 @@ public static boolean isNotEmpty(String s, boolean trim) {
public static final Pattern PATTERN_BRANCH_URL;
static {
PATTERN_NUMBER = Pattern.compile("^[0-9]+$");
+ PATTERN_NUM_OR_ALPHA = Pattern.compile("^[0-9a-zA-Z_.:]+$");
PATTERN_ALPHA = Pattern.compile("^[a-zA-Z]+$");
PATTERN_ALPHA_BIG = Pattern.compile("^[A-Z]+$");
PATTERN_ALPHA_SMALL = Pattern.compile("^[a-z]+$");
@@ -442,6 +444,19 @@ public static boolean isNumberOrAlpha(String s) {
return isNumer(s) || isAlpha(s);
}
+ /**判断是否全是数字或字母
+ * @param s
+ * @return
+ */
+ public static boolean isCombineOfNumOrAlpha(String s) {
+ if (isEmpty(s, true)) {
+ return false;
+ }
+
+ currentString = s;
+ return PATTERN_NUM_OR_ALPHA.matcher(s).matches();
+ }
+
/**判断是否为代码名称,只能包含字母,数字或下划线
* @param s
* @return
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index e9b6c0a02..45383553e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1792,17 +1792,18 @@ public String getSampleString(boolean hasPrefix) {
for (int i = 0; i < keys.length; i++) {
String item = keys[i];
- //if ("fill(null)".equals(item) || "fill(linear)".equals(item) || "fill(prev)".equals(item) || "fill(previous)".equals(item)) {
- // continue;
- //}
String origin = item;
if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
//这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
- if (StringUtil.isNumberOrAlpha(origin) == false) {
+ if (StringUtil.isName(origin)) {}
+ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
+ continue;
+ }
+ else {
throw new IllegalArgumentException("预编译模式下 @sample:value 中 " + item + " 不合法! value 里面用 , 分割的"
- + "每一项必须是 column 且其中 column 必须是 字母或数字组合!并且不要有多余的空格!");
+ + "每一项必须是 column 且其中 column 必须是 数字或英语字母组合!并且不要有多余的空格!");
}
}
@@ -1994,13 +1995,21 @@ public String getFillString(boolean hasPrefix) {
for (int i = 0; i < keys.length; i++) {
String item = keys[i];
+ if ("NULL".equals(item) || "LINEAR".equals(item) || "PREV".equals(item) || "PREVIOUS".equals(item)) {
+ continue;
+ }
+
String origin = item;
if (isPrepared()) { //不能通过 ? 来代替,SELECT 'id','name' 返回的就是 id:"id", name:"name",而不是数据库里的值!
//这里既不对origin trim,也不对 ASC/DESC ignoreCase,希望前端严格传没有任何空格的字符串过来,减少传输数据量,节约服务器性能
- if (StringUtil.isName(origin) == false) {
+ if (StringUtil.isName(origin)) {}
+ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
+ continue;
+ }
+ else {
throw new IllegalArgumentException("预编译模式下 @fill:value 中 " + item + " 不合法! value 里面用 , 分割的"
- + "每一项必须是 column 且其中 column 必须是 英语单词!并且不要有多余的空格!");
+ + "每一项必须是 column 且其中 column 必须是 数字或英语字母组合!并且不要有多余的空格!");
}
}
@@ -3035,20 +3044,26 @@ public static int getOffset(int page, int count) {
@JSONField(serialize = false)
public String getLimitString() {
int count = getCount();
+ int page = getPage();
+
+ boolean isMilvus = isMilvus();
+ if ((count <= 0 && ! (isMilvus && isMain())) || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
+ return "";
+ }
boolean isSurrealDB = isSurrealDB();
boolean isQuestDB = isQuestDB();
- if (isSurrealDB || isQuestDB || isMilvus()) {
+ if (isSurrealDB || isQuestDB || isMilvus) {
if (count == 0) {
Parser parser = getParser();
count = parser == null ? AbstractParser.MAX_QUERY_COUNT : parser.getMaxQueryCount();
}
- int offset = getOffset(getPage(), count);
+ int offset = getOffset(page, count);
if (isQuestDB()) {
return " LIMIT " + offset + ", " + (offset + count);
}
- else if (isSurrealDB()) {
+ else if (isSurrealDB()) {
return " START " + offset + " LIMIT " + count;
}
else {
@@ -3056,18 +3071,8 @@ else if (isSurrealDB()) {
}
}
- if (count <= 0 || RequestMethod.isHeadMethod(getMethod(), true)) { // TODO HEAD 真的不需要 LIMIT ?
- return "";
- }
-
boolean isOracle = isOracle();
- return getLimitString(
- getPage()
- , count
- , isTSQL()
- , isOracle || isDameng() || isKingBase()
- , isPresto() || isTrino()
- );
+ return getLimitString(page, count, isTSQL(), isOracle || isDameng() || isKingBase(), isPresto() || isTrino());
}
/**获取限制数量及偏移量
* @param page
From 02d90c7499b0e88a3e72d1e3df57426341aee438 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:28:40 +0800
Subject: [PATCH 107/145] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E6=8B=BC=E5=86=99=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/StringUtil.java | 14 +++++++-------
.../main/java/apijson/orm/AbstractSQLConfig.java | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index 49c72677a..b357f5279 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -389,7 +389,7 @@ public static boolean isPassword(String s) {
* @return
*/
public static boolean isNumberPassword(String s) {
- return getLength(s, false) == 6 && isNumer(s);
+ return getLength(s, false) == 6 && isNumber(s);
}
/**判断email格式是否正确
* @param email
@@ -410,13 +410,13 @@ public static boolean isEmail(String email) {
* @return
*/
public static boolean isVerify(String s) {
- return getLength(s, false) >= 4 && isNumer(s);
+ return getLength(s, false) >= 4 && isNumber(s);
}
/**判断是否全是数字
* @param s
* @return
*/
- public static boolean isNumer(String s) {
+ public static boolean isNumber(String s) {
if (isNotEmpty(s, true) == false) {
return false;
}
@@ -441,7 +441,7 @@ public static boolean isAlpha(String s) {
* @return
*/
public static boolean isNumberOrAlpha(String s) {
- return isNumer(s) || isAlpha(s);
+ return isNumber(s) || isAlpha(s);
}
/**判断是否全是数字或字母
@@ -500,7 +500,7 @@ public static boolean isSmallName(String s) {
* @return
*/
public static boolean isIDCard(String number) {
- if (isNumberOrAlpha(number) == false) {
+ if (isCombineOfNumOrAlpha(number) == false) {
return false;
}
number = getString(number);
@@ -627,7 +627,7 @@ public static String getNumber(String s, boolean onlyStart) {
String single;
for (int i = 0; i < s.length(); i++) {
single = s.substring(i, i + 1);
- if (isNumer(single)) {
+ if (isNumber(single)) {
numberString.append(single);
} else {
if (onlyStart) {
@@ -732,7 +732,7 @@ public static String getPrice(String price, int formatType) {
String s;
for (int i = 0; i < price.length(); i++) {
s = price.substring(i, i + 1);
- if (".".equals(s) || isNumer(s)) {
+ if (".".equals(s) || isNumber(s)) {
correctPriceBuilder.append(s);
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 45383553e..a2882f463 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2707,7 +2707,7 @@ else if ("!=null".equals(ck)) {
if (mk.length() > 0) {
origin = mk;
}
- } else if (StringUtil.isNumer(origin)) {
+ } else if (StringUtil.isNumber(origin)) {
//do nothing
} else {
String[] keys = origin.split("[.]");
@@ -2809,7 +2809,7 @@ else if (ck.contains("`") || ck.contains("'") || origin.startsWith("_") || origi
+ " 且不包含连续减号 -- !DISTINCT 必须全大写,且后面必须有且只有 1 个空格!其它情况不允许空格!");
}
- if (StringUtil.isNumer(origin)) {
+ if (StringUtil.isNumber(origin)) {
//do nothing
} else {
String[] keys = origin.split("[.]");
From 8cdca093659a9afb9a8908cdbfb31028ce96338a Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 16:29:54 +0800
Subject: [PATCH 108/145] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/StringUtil.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index b357f5279..caac16d25 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -520,8 +520,6 @@ public static boolean isIDCard(String number) {
public static final String HTTP = "http";
public static final String URL_PREFIX = "http://";
public static final String URL_PREFIXs = "https://";
- public static final String URL_STAFFIX = URL_PREFIX;
- public static final String URL_STAFFIXs = URL_PREFIXs;
/**判断字符类型是否是网址
* @param url
* @return
From c9b124d7a1a8cfd48ee1f591d1d4a2ef989cf92f Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 17:17:38 +0800
Subject: [PATCH 109/145] =?UTF-8?q?=E7=AE=80=E5=8C=96=20StringUtil=20?=
=?UTF-8?q?=E4=B8=AD=E5=90=84=E7=A7=8D=E5=AD=97=E7=AC=A6=E4=B8=B2=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/JSON.java | 2 +-
.../src/main/java/apijson/JSONObject.java | 24 +-
.../src/main/java/apijson/JSONRequest.java | 4 +-
APIJSONORM/src/main/java/apijson/SQL.java | 2 +-
.../src/main/java/apijson/StringUtil.java | 300 +++++++++++++-----
.../main/java/apijson/orm/AbstractParser.java | 11 +-
.../java/apijson/orm/AbstractSQLConfig.java | 56 ++--
.../java/apijson/orm/AbstractSQLExecutor.java | 4 +-
.../java/apijson/orm/AbstractVerifier.java | 23 +-
.../src/main/java/apijson/orm/Logic.java | 2 +-
.../src/main/java/apijson/orm/Pair.java | 6 +-
.../src/main/java/apijson/orm/SQLConfig.java | 2 +-
12 files changed, 289 insertions(+), 147 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index d7854aae1..0a1f901b7 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -48,7 +48,7 @@ public static String getCorrectJson(String s) {
* @return
*/
public static String getCorrectJson(String s, boolean isArray) {
- s = StringUtil.getTrimedString(s);
+ s = StringUtil.trim(s);
// if (isArray) {
// while (s.startsWith("\"")) {
// s = s.substring(1);
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index c69a03569..0adf18365 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -326,7 +326,7 @@ public JSONObject setCache(String cache) {
* @return {@link #setColumn(String)}
*/
public JSONObject setColumn(String... keys) {
- return setColumn(StringUtil.getString(keys, true));
+ return setColumn(StringUtil.get(keys, true));
}
/**set keys need to be returned
* @param keys "key0,key1,key2..."
@@ -341,7 +341,7 @@ public JSONObject setColumn(String keys) {
* @return {@link #setNull(String)}
*/
public JSONObject setNull(String... keys) {
- return setNull(StringUtil.getString(keys, true));
+ return setNull(StringUtil.get(keys, true));
}
/**set keys whose value is null
* @param keys "key0,key1,key2..."
@@ -356,7 +356,7 @@ public JSONObject setNull(String keys) {
* @return {@link #setCast(String)}
*/
public JSONObject setCast(String... keyTypes) {
- return setCast(StringUtil.getString(keyTypes, true));
+ return setCast(StringUtil.get(keyTypes, true));
}
/**set keys and types whose value should be cast to type, cast(value AS DATE)
* @param keyTypes "key0:type0,key1:type1,key2:type2..."
@@ -371,7 +371,7 @@ public JSONObject setCast(String keyTypes) {
* @return {@link #setColumn(String)}
*/
public JSONObject setCombine(String... keys) {
- return setCombine(StringUtil.getString(keys, true));
+ return setCombine(StringUtil.get(keys, true));
}
/**set combination of keys for conditions
* @param keys key0,&key1,|key2,!key3 ... TODO or key0> | (key1{} & !key2)...
@@ -386,7 +386,7 @@ public JSONObject setCombine(String keys) {
* @return {@link #setGroup(String)}
*/
public JSONObject setGroup(String... keys) {
- return setGroup(StringUtil.getString(keys, true));
+ return setGroup(StringUtil.get(keys, true));
}
/**set keys for group by
* @param keys "key0,key1,key2..."
@@ -401,7 +401,7 @@ public JSONObject setGroup(String keys) {
* @return {@link #setHaving(String)}
*/
public JSONObject setHaving(String... keys) {
- return setHaving(StringUtil.getString(keys, true));
+ return setHaving(StringUtil.get(keys, true));
}
/**set keys for having
* @param keys "key0,key1,key2..."
@@ -423,7 +423,7 @@ public JSONObject setHaving(String keys, boolean isAnd) {
* @return {@link #setSample(String)}
*/
public JSONObject setSample(String... keys) {
- return setSample(StringUtil.getString(keys, true));
+ return setSample(StringUtil.get(keys, true));
}
/**set keys for sample by
* @param keys "key0,key1,key2..."
@@ -438,7 +438,7 @@ public JSONObject setSample(String keys) {
* @return {@link #setLatest(String)}
*/
public JSONObject setLatest(String... keys) {
- return setLatest(StringUtil.getString(keys, true));
+ return setLatest(StringUtil.get(keys, true));
}
/**set keys for latest on
* @param keys "key0,key1,key2..."
@@ -453,7 +453,7 @@ public JSONObject setLatest(String keys) {
* @return {@link #setPartition(String)}
*/
public JSONObject setPartition(String... keys) {
- return setPartition(StringUtil.getString(keys, true));
+ return setPartition(StringUtil.get(keys, true));
}
/**set keys for partition by
* @param keys key0, key1, key2 ...
@@ -468,7 +468,7 @@ public JSONObject setPartition(String keys) {
* @return {@link #setFill(String)}
*/
public JSONObject setFill(String... keys) {
- return setFill(StringUtil.getString(keys, true));
+ return setFill(StringUtil.get(keys, true));
}
/**set keys for fill(key): fill(null), fill(linear), fill(prev)
* @param keys key0, key1, key2 ...
@@ -483,7 +483,7 @@ public JSONObject setFill(String keys) {
* @return {@link #setOrder(String)}
*/
public JSONObject setOrder(String... keys) {
- return setOrder(StringUtil.getString(keys, true));
+ return setOrder(StringUtil.get(keys, true));
}
/**set keys for order by
* @param keys "key0,key1+,key2-..."
@@ -530,7 +530,7 @@ public JSONObject setJson(String keys) {
* @return {@link #puts(String, Object)}
*/
public JSONObject putsPath(String key, String... keys) {
- return puts(key+"@", StringUtil.getString(keys, "/"));
+ return puts(key+"@", StringUtil.get(keys, "/"));
}
/**
diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java
index 62d724199..ae5e19950 100755
--- a/APIJSONORM/src/main/java/apijson/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java
@@ -138,7 +138,7 @@ public JSONRequest setPage(int page) {
* @return
*/
public JSONRequest setJoin(String... joins) {
- return puts(KEY_JOIN, StringUtil.getString(joins));
+ return puts(KEY_JOIN, StringUtil.get(joins));
}
/**set range for Subquery
@@ -178,7 +178,7 @@ public JSONRequest toArray(int count, int page) {
* @return {name+KEY_ARRAY : this}. if needs to be put, use {@link #putsAll(Map extends String, ? extends Object>)} instead
*/
public JSONRequest toArray(int count, int page, String name) {
- return new JSONRequest(StringUtil.getString(name) + KEY_ARRAY, this.setCount(count).setPage(page));
+ return new JSONRequest(StringUtil.get(name) + KEY_ARRAY, this.setCount(count).setPage(page));
}
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 6cec79bd2..110ae3d47 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -242,7 +242,7 @@ public static String toLowerCase(String s) {
* @return column.isEmpty() ? "*" : column;
*/
public static String column(String column) {
- column = StringUtil.getTrimedString(column);
+ column = StringUtil.trim(column);
return column.isEmpty() ? "*" : column;
}
/**有别名的字段
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index caac16d25..13b0ff214 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -53,171 +53,317 @@ public StringUtil() {
public static final String YUAN = "元";
- private static String currentString = "";
- /**获取刚传入处理后的string
+ private static String current = "";
+ /**获取刚传入处理后的 string
+ * @must 上个影响 current 的方法 和 这个方法都应该在同一线程中,否则返回值可能不对
+ * @return
+ */
+ public static String cur() {
+ return get(current);
+ }
+
+ /**FIXME 改用 cur
* @must 上个影响currentString的方法 和 这个方法都应该在同一线程中,否则返回值可能不对
* @return
*/
+ @Deprecated
public static String getCurrentString() {
- return currentString == null ? "" : currentString;
+ return cur();
}
//获取string,为null时返回"" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**获取string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String get(Object obj) {
+ return obj == null ? "" : obj.toString();
+ }
+ /**获取string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String get(String s) {
+ return s == null ? "" : s;
+ }
+ /**获取string,为null则返回""
+ * ignoreEmptyItem = false;
+ * split = ","
+ * @param arr
+ * @return {@link #get(Object[], boolean)}
+ */
+ public static String get(Object[] arr) {
+ return get(arr, false);
+ }
+ /**获取string,为null则返回""
+ * split = ","
+ * @param arr
+ * @param ignoreEmptyItem
+ * @return {@link #get(Object[], boolean)}
+ */
+ public static String get(Object[] arr, boolean ignoreEmptyItem) {
+ return get(arr, null, ignoreEmptyItem);
+ }
+ /**获取string,为null则返回""
+ * ignoreEmptyItem = false;
+ * @param arr
+ * @param split
+ * @return {@link #get(Object[], String, boolean)}
+ */
+ public static String get(Object[] arr, String split) {
+ return get(arr, split, false);
+ }
+ //CS304 Issue link: https://github.com/Tencent/APIJSON/issues/182
+ /**获取string,为null则返回""
+ * @param arr -the str arr given
+ * @param split -the token used to split
+ * @param ignoreEmptyItem -whether to ignore empty item or not
+ * @return {@link #get(Object[], String, boolean)}
+ * Here we replace the simple "+" way of concatenating with Stringbuilder 's append
+ */
+ public static String get(Object[] arr, String split, boolean ignoreEmptyItem) {
+ StringBuilder s = new StringBuilder("");
+ if (arr != null) {
+ if (split == null) {
+ split = ",";
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (ignoreEmptyItem && isEmpty(arr[i], true)) {
+ continue;
+ }
+ s.append(((i > 0 ? split : "") + arr[i]));
+ }
+ }
+ return get(s.toString());
+ }
+
+ /**FIXME 用 get 替代
* @param object
* @return
*/
+ @Deprecated
public static String getString(Object object) {
return object == null ? "" : object.toString();
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getString(CharSequence cs) {
return cs == null ? "" : cs.toString();
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param s
* @return
*/
+ @Deprecated
public static String getString(String s) {
return s == null ? "" : s;
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* ignoreEmptyItem = false;
* split = ","
* @param array
- * @return {@link #getString(Object[], boolean)}
+ * @return {@link #get(Object[], boolean)}
*/
+ @Deprecated
public static String getString(Object[] array) {
- return getString(array, false);
+ return get(array, false);
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* split = ","
* @param array
* @param ignoreEmptyItem
- * @return {@link #getString(Object[], boolean)}
+ * @return {@link #get(Object[], boolean)}
*/
+ @Deprecated
public static String getString(Object[] array, boolean ignoreEmptyItem) {
- return getString(array, null, ignoreEmptyItem);
+ return get(array, null, ignoreEmptyItem);
}
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* ignoreEmptyItem = false;
* @param array
* @param split
- * @return {@link #getString(Object[], String, boolean)}
+ * @return {@link #get(Object[], String, boolean)}
*/
+ @Deprecated
public static String getString(Object[] array, String split) {
- return getString(array, split, false);
+ return get(array, split, false);
}
//CS304 Issue link: https://github.com/Tencent/APIJSON/issues/182
- /**获取string,为null则返回""
+ /**FIXME 用 get 替代
* @param array -the str array given
* @param split -the token used to split
* @param ignoreEmptyItem -whether to ignore empty item or not
- * @return {@link #getString(Object[], String, boolean)}
+ * @return {@link #get(Object[], String, boolean)}
* Here we replace the simple "+" way of concatenating with Stringbuilder 's append
*/
+ @Deprecated
public static String getString(Object[] array, String split, boolean ignoreEmptyItem) {
- StringBuilder s = new StringBuilder("");
- if (array != null) {
- if (split == null) {
- split = ",";
- }
- for (int i = 0; i < array.length; i++) {
- if (ignoreEmptyItem && isEmpty(array[i], true)) {
- continue;
- }
- s.append(((i > 0 ? split : "") + array[i]));
- }
- }
- return getString(s.toString());
+ return get(array, split, ignoreEmptyItem);
}
//获取string,为null时返回"" >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取去掉前后空格后的string<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取去掉前后空格后的string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String trim(Object obj) {
+ return trim(get(obj));
+ }
+ /**获取去掉前后空格后的string,为null则返回""
+ * @param cs
+ * @return
+ */
+ public static String trim(CharSequence cs) {
+ return trim(get(cs));
+ }
+ /**获取去掉前后空格后的string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String trim(String s) {
+ return get(s).trim();
+ }
+
+
+ /**FIXME 用 trim 替代
* @param object
* @return
*/
+ @Deprecated
public static String getTrimedString(Object object) {
- return getTrimedString(getString(object));
+ return trim(object);
}
- /**获取去掉前后空格后的string,为null则返回""
+ /**FIXME 用 trim 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getTrimedString(CharSequence cs) {
- return getTrimedString(getString(cs));
+ return trim(cs);
}
- /**获取去掉前后空格后的string,为null则返回""
+ /**FIXME 用 trim 替代
* @param s
* @return
*/
+ @Deprecated
public static String getTrimedString(String s) {
- return getString(s).trim();
+ return trim(s);
}
//获取去掉前后空格后的string>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取去掉所有空格后的string <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取去掉所有空格后的string,为null则返回""
+ * @param obj
+ * @return
+ */
+ public static String noBlank(Object obj) {
+ return noBlank(get(obj));
+ }
+ /**获取去掉所有空格后的string,为null则返回""
+ * @param cs
+ * @return
+ */
+ public static String noBlank(CharSequence cs) {
+ return noBlank(get(cs));
+ }
+ /**获取去掉所有空格后的string,为null则返回""
+ * @param s
+ * @return
+ */
+ public static String noBlank(String s) {
+ return get(s).replaceAll("\\s", "");
+ }
+
+ /**FIXME 用 noBlank 替代
* @param object
* @return
*/
+ @Deprecated
public static String getNoBlankString(Object object) {
- return getNoBlankString(getString(object));
+ return noBlank(object);
}
- /**获取去掉所有空格后的string,为null则返回""
+ /**FIXME 用 noBlank 替代
* @param cs
* @return
*/
+ @Deprecated
public static String getNoBlankString(CharSequence cs) {
- return getNoBlankString(getString(cs));
+ return noBlank(cs);
}
- /**获取去掉所有空格后的string,为null则返回""
+ /**FIXME 用 noBlank 替代
* @param s
* @return
*/
+ @Deprecated
public static String getNoBlankString(String s) {
- return getString(s).replaceAll("\\s", "");
+ return noBlank(s);
}
//获取去掉所有空格后的string >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//获取string的长度<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
/**获取string的长度,为null则返回0
* @param object
* @param trim
* @return
*/
- public static int getLength(Object object, boolean trim) {
- return getLength(getString(object), trim);
+ public static int length(Object object, boolean trim) {
+ return length(get(object), trim);
}
/**获取string的长度,为null则返回0
* @param cs
* @param trim
* @return
*/
- public static int getLength(CharSequence cs, boolean trim) {
- return getLength(getString(cs), trim);
+ public static int length(CharSequence cs, boolean trim) {
+ return length(get(cs), trim);
}
/**获取string的长度,为null则返回0
* @param s
* @param trim
* @return
*/
+ public static int length(String s, boolean trim) {
+ s = trim ? trim(s) : s;
+ return get(s).length();
+ }
+
+
+ /**FIXME 用 length 替代
+ * @param object
+ * @param trim
+ * @return
+ */
+ @Deprecated
+ public static int getLength(Object object, boolean trim) {
+ return length(object, trim);
+ }
+ /**FIXME 用 length 替代
+ * @param cs
+ * @param trim
+ * @return
+ */
+ @Deprecated
+ public static int getLength(CharSequence cs, boolean trim) {
+ return length(cs, trim);
+ }
+ /**FIXME 用 length 替代
+ * @param s
+ * @param trim
+ * @return
+ */
+ @Deprecated
public static int getLength(String s, boolean trim) {
- s = trim ? getTrimedString(s) : s;
- return getString(s).length();
+ return length(s, trim);
}
//获取string的长度>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -238,7 +384,7 @@ public static boolean isEmpty(Object obj) {
* @return
*/
public static boolean isEmpty(Object obj, boolean trim) {
- return isEmpty(getString(obj), trim);
+ return isEmpty(get(obj), trim);
}
/**判断字符是否为空 trim = true
* @param cs
@@ -253,7 +399,7 @@ public static boolean isEmpty(CharSequence cs) {
* @return
*/
public static boolean isEmpty(CharSequence cs, boolean trim) {
- return isEmpty(getString(cs), trim);
+ return isEmpty(get(cs), trim);
}
/**判断字符是否为空 trim = true
* @param s
@@ -268,7 +414,7 @@ public static boolean isEmpty(String s) {
* @return
*/
public static boolean isEmpty(String s, boolean trim) {
- // Log.i(TAG, "getTrimedString s = " + s);
+ // Log.i(TAG, "isEmpty s = " + s);
if (s == null) {
return true;
}
@@ -279,7 +425,7 @@ public static boolean isEmpty(String s, boolean trim) {
return true;
}
- currentString = s;
+ current = s;
return false;
}
@@ -289,7 +435,7 @@ public static boolean isEmpty(String s, boolean trim) {
//判断字符是否非空 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**判断字符是否非空 trim = true
- * @param object
+ * @param obj
* @return
*/
public static boolean isNotEmpty(Object obj) {
@@ -374,7 +520,7 @@ public static boolean isPhone(String phone) {
return false;
}
- currentString = phone;
+ current = phone;
return PATTERN_PHONE.matcher(phone).matches();
}
/**判断手机格式是否正确
@@ -382,14 +528,14 @@ public static boolean isPhone(String phone) {
* @return
*/
public static boolean isPassword(String s) {
- return getLength(s, false) >= 6 && PATTERN_PASSWORD.matcher(s).matches();
+ return length(s, false) >= 6 && PATTERN_PASSWORD.matcher(s).matches();
}
/**判断是否全是数字密码
* @param s
* @return
*/
public static boolean isNumberPassword(String s) {
- return getLength(s, false) == 6 && isNumber(s);
+ return length(s, false) == 6 && isNumber(s);
}
/**判断email格式是否正确
* @param email
@@ -400,7 +546,7 @@ public static boolean isEmail(String email) {
return false;
}
- currentString = email;
+ current = email;
return PATTERN_EMAIL.matcher(email).matches();
}
@@ -410,18 +556,18 @@ public static boolean isEmail(String email) {
* @return
*/
public static boolean isVerify(String s) {
- return getLength(s, false) >= 4 && isNumber(s);
+ return length(s, false) >= 4 && isNumber(s);
}
/**判断是否全是数字
* @param s
* @return
*/
public static boolean isNumber(String s) {
- if (isNotEmpty(s, true) == false) {
+ if (isEmpty(s, true)) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_NUMBER.matcher(s).matches();
}
/**判断是否全是字母
@@ -433,7 +579,7 @@ public static boolean isAlpha(String s) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_ALPHA.matcher(s).matches();
}
/**判断是否全是数字或字母
@@ -453,7 +599,7 @@ public static boolean isCombineOfNumOrAlpha(String s) {
return false;
}
- currentString = s;
+ current = s;
return PATTERN_NUM_OR_ALPHA.matcher(s).matches();
}
@@ -503,14 +649,14 @@ public static boolean isIDCard(String number) {
if (isCombineOfNumOrAlpha(number) == false) {
return false;
}
- number = getString(number);
+ number = get(number);
if (number.length() == 15) {
Log.i(TAG, "isIDCard number.length() == 15 old IDCard");
- currentString = number;
+ current = number;
return true;
}
if (number.length() == 18) {
- currentString = number;
+ current = number;
return true;
}
@@ -532,7 +678,7 @@ public static boolean isUrl(String url) {
return false;
}
- currentString = url;
+ current = url;
return true;
}
@@ -577,7 +723,7 @@ public static boolean isFilePath(String path) {
return false;
}
- currentString = path;
+ current = path;
return true;
}
@@ -592,14 +738,14 @@ public static boolean isFilePath(String path) {
* @return
*/
public static String getNumber(Object object) {
- return getNumber(getString(object));
+ return getNumber(get(object));
}
/**去掉string内所有非数字类型字符
* @param cs
* @return
*/
public static String getNumber(CharSequence cs) {
- return getNumber(getString(cs));
+ return getNumber(get(cs));
}
/**去掉string内所有非数字类型字符
* @param s
@@ -617,7 +763,7 @@ public static String getNumber(String s) {
* Here we replace the simple "+" way of concatenating with Stringbuilder 's append
*/
public static String getNumber(String s, boolean onlyStart) {
- if (isNotEmpty(s, true) == false) {
+ if (isEmpty(s, true)) {
return "";
}
@@ -669,7 +815,7 @@ public static String getCorrectPhone(String phone) {
return "";
}
- phone = getNoBlankString(phone);
+ phone = noBlank(phone);
phone = phone.replaceAll("-", "");
if (phone.startsWith("+86")) {
phone = phone.substring(3);
@@ -687,7 +833,7 @@ public static String getCorrectEmail(String email) {
return "";
}
- email = getNoBlankString(email);
+ email = noBlank(email);
if (isEmail(email) == false && ! email.endsWith(".com")) {
email += ".com";
}
@@ -857,7 +1003,7 @@ public static String[] split(String s, boolean trim) {
* @return
*/
public static String[] split(String s, String split, boolean trim) {
- s = getString(s);
+ s = get(s);
if (s.isEmpty()) {
return null;
}
@@ -881,7 +1027,7 @@ public static String[] split(String s, String split, boolean trim) {
* @return key + suffix,第一个字母小写
*/
public static String addSuffix(String key, String suffix) {
- key = getNoBlankString(key);
+ key = noBlank(key);
if (key.isEmpty()) {
return firstCase(suffix);
}
@@ -899,7 +1045,7 @@ public static String firstCase(String key) {
* @return
*/
public static String firstCase(String key, boolean upper) {
- key = getString(key);
+ key = get(key);
if (key.isEmpty()) {
return "";
}
@@ -923,7 +1069,7 @@ public static String toUpperCase(String s) {
* @return
*/
public static String toUpperCase(String s, boolean trim) {
- s = trim ? getTrimedString(s) : getString(s);
+ s = trim ? trim(s) : get(s);
return s.toUpperCase();
}
/**全部小写
@@ -938,7 +1084,7 @@ public static String toLowerCase(String s) {
* @return
*/
public static String toLowerCase(String s, boolean trim) {
- s = trim ? getTrimedString(s) : getString(s);
+ s = trim ? trim(s) : get(s);
return s.toLowerCase();
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index fd56b71b9..061e32d11 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -14,7 +14,6 @@
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;
@@ -819,9 +818,9 @@ public static JSONObject extendResult(JSONObject object, int code, String msg, S
object.put(JSONResponse.KEY_CODE, code);
}
- String m = StringUtil.getString(object.getString(JSONResponse.KEY_MSG));
+ String m = StringUtil.get(object.getString(JSONResponse.KEY_MSG));
if (m.isEmpty() == false) {
- msg = m + " ;\n " + StringUtil.getString(msg);
+ msg = m + " ;\n " + StringUtil.get(msg);
}
object.put(JSONResponse.KEY_MSG, msg);
@@ -1875,8 +1874,8 @@ public static String getValuePath(String parentPath, String valuePath) {
*/
public static String getAbsPath(String path, String name) {
Log.i(TAG, "getPath path = " + path + "; name = " + name + " <<<<<<<<<<<<<");
- path = StringUtil.getString(path);
- name = StringUtil.getString(name);
+ path = StringUtil.get(path);
+ name = StringUtil.get(name);
if (StringUtil.isNotEmpty(path, false)) {
if (StringUtil.isNotEmpty(name, false)) {
path += ((name.startsWith("/") ? "" : "/") + name);
@@ -1917,7 +1916,7 @@ public static String replaceArrayChildPath(String parentPath, String valuePath)
vs[i+1] = pos + "/" + vs[i+1];
}
}
- return StringUtil.getString(vs, "]/");
+ return StringUtil.get(vs, "]/");
}
}
return valuePath;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index a2882f463..4dd665edf 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1556,7 +1556,7 @@ public String getGroup() {
return group;
}
public AbstractSQLConfig setGroup(String... keys) {
- return setGroup(StringUtil.getString(keys));
+ return setGroup(StringUtil.get(keys));
}
@Override
public AbstractSQLConfig setGroup(String group) {
@@ -1593,7 +1593,7 @@ public String getGroupString(boolean hasPrefix) {
}
- group = StringUtil.getTrimedString(group);
+ group = StringUtil.trim(group);
String[] keys = StringUtil.split(group);
if (keys == null || keys.length <= 0) {
return StringUtil.isEmpty(joinGroup, true) ? "" : (hasPrefix ? " GROUP BY " : "") + joinGroup;
@@ -1610,7 +1610,7 @@ public String getGroupString(boolean hasPrefix) {
keys[i] = getKey(keys[i]);
}
- return (hasPrefix ? " GROUP BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinGroup, ", ");
+ return (hasPrefix ? " GROUP BY " : "") + StringUtil.concat(StringUtil.get(keys), joinGroup, ", ");
}
@Override
@@ -1633,7 +1633,7 @@ public AbstractSQLConfig setHaving(Map having) {
return this;
}
public AbstractSQLConfig setHaving(String... conditions) {
- return setHaving(StringUtil.getString(conditions));
+ return setHaving(StringUtil.get(conditions));
}
/**TODO @having 改为默认 | 或连接,且支持 @having: { "key1>": 1, "key{}": "length(key2)>0", "@combine": "key1,key2" }
@@ -1747,7 +1747,7 @@ public String getSample() {
return sample;
}
public AbstractSQLConfig setSample(String... conditions) {
- return setSample(StringUtil.getString(conditions));
+ return setSample(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setSample(String sample) {
@@ -1783,7 +1783,7 @@ public String getSampleString(boolean hasPrefix) {
}
}
- String sample = StringUtil.getTrimedString(getSample());
+ String sample = StringUtil.trim(getSample());
String[] keys = StringUtil.split(sample);
if (keys == null || keys.length <= 0) {
@@ -1810,7 +1810,7 @@ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinSample, ", ");
+ return (hasPrefix ? " SAMPLE BY " : "") + StringUtil.concat(StringUtil.get(keys), joinSample, ", ");
}
@Override
@@ -1818,7 +1818,7 @@ public String getLatest() {
return latest;
}
public AbstractSQLConfig setLatest(String... conditions) {
- return setLatest(StringUtil.getString(conditions));
+ return setLatest(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setLatest(String latest) {
@@ -1854,7 +1854,7 @@ public String getLatestString(boolean hasPrefix) {
}
}
- String latest = StringUtil.getTrimedString(getLatest());
+ String latest = StringUtil.trim(getLatest());
String[] keys = StringUtil.split(latest);
if (keys == null || keys.length <= 0) {
@@ -1876,7 +1876,7 @@ public String getLatestString(boolean hasPrefix) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.getString(keys), joinLatest, ", ");
+ return (hasPrefix ? " LATEST ON " : "") + StringUtil.concat(StringUtil.get(keys), joinLatest, ", ");
}
@Override
@@ -1884,7 +1884,7 @@ public String getPartition() {
return partition;
}
public AbstractSQLConfig setPartition(String... conditions) {
- return setPartition(StringUtil.getString(conditions));
+ return setPartition(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setPartition(String partition) {
@@ -1920,7 +1920,7 @@ public String getPartitionString(boolean hasPrefix) {
}
}
- String partition = StringUtil.getTrimedString(getPartition());
+ String partition = StringUtil.trim(getPartition());
String[] keys = StringUtil.split(partition);
if (keys == null || keys.length <= 0) {
@@ -1942,7 +1942,7 @@ public String getPartitionString(boolean hasPrefix) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinPartition, ", ");
+ return (hasPrefix ? " PARTITION BY " : "") + StringUtil.concat(StringUtil.get(keys), joinPartition, ", ");
}
@Override
@@ -1950,7 +1950,7 @@ public String getFill() {
return fill;
}
public AbstractSQLConfig setFill(String... conditions) {
- return setFill(StringUtil.getString(conditions));
+ return setFill(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setFill(String fill) {
@@ -1986,7 +1986,7 @@ public String getFillString(boolean hasPrefix) {
}
}
- String fill = StringUtil.getTrimedString(getFill());
+ String fill = StringUtil.trim(getFill());
String[] keys = StringUtil.split(fill);
if (keys == null || keys.length <= 0) {
@@ -2016,7 +2016,7 @@ else if (StringUtil.isCombineOfNumOrAlpha(origin)) {
keys[i] = getKey(origin);
}
- return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.getString(keys), joinFill, ", ") + ")";
+ return (hasPrefix ? " FILL(" : "") + StringUtil.concat(StringUtil.get(keys), joinFill, ", ") + ")";
}
@Override
@@ -2024,7 +2024,7 @@ public String getOrder() {
return order;
}
public AbstractSQLConfig setOrder(String... conditions) {
- return setOrder(StringUtil.getString(conditions));
+ return setOrder(StringUtil.get(conditions));
}
@Override
public AbstractSQLConfig setOrder(String order) {
@@ -2061,7 +2061,7 @@ public String getOrderString(boolean hasPrefix) {
}
- String order = StringUtil.getTrimedString(getOrder());
+ String order = StringUtil.trim(getOrder());
// SELECT * FROM sys.Moment ORDER BY userId ASC, rand(); 前面的 userId ASC 和后面的 rand() 都有效
// if ("rand()".equals(order)) {
// return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(order, joinOrder, ", ");
@@ -2134,7 +2134,7 @@ public String getOrderString(boolean hasPrefix) {
keys[i] = getKey(origin) + sort;
}
- return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinOrder, ", ");
+ return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(StringUtil.get(keys), joinOrder, ", ");
}
@Override
@@ -2426,7 +2426,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
, "@column:\"column0,column1:alias1;function0(arg0,arg1,...);function1(...):alias2...\"");
}
- String c = StringUtil.getString(keys);
+ String c = StringUtil.get(keys);
c = c + (StringUtil.isEmpty(joinColumn, true) ? "" : ", " + joinColumn);//不能在这里改,后续还要用到:
return isMain() && isDistinct() ? PREFIX_DISTINCT + c : c;
default:
@@ -2471,7 +2471,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
if (start < 0) {
//没有函数 ,可能是字段,也可能是 DISTINCT xx
String[] cks = parseArgsSplitWithComma(expression, true, containRaw, allowAlias);
- expression = StringUtil.getString(cks);
+ expression = StringUtil.get(cks);
} else { // FIXME 用括号断开? 如果少的话,用关键词加括号断开,例如 )OVER( 和 )AGAINST(
// 窗口函数 rank() OVER (PARTITION BY id ORDER BY userId ASC)
// 全文索引 math(name,tag) AGAINST ('a b +c -d' IN NATURALE LANGUAGE MODE) // IN BOOLEAN MODE
@@ -2547,7 +2547,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
+ " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- 或注释符 /* !不允许多余的空格!");
}
- String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "") + StringUtil.getString(ckeys) + ")" + suffix;
+ String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "") + StringUtil.get(ckeys) + ")" + suffix;
expression = origin + (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
else {
@@ -2604,8 +2604,8 @@ else if (SQL_FUNCTION_MAP.containsKey(fun) == false) {
// 获取后半部分的参数解析 (agr0 agr1 ...)
String argsString2[] = parseArgsSplitWithComma(argString2, false, containRaw, allowAlias);
- expression = fun + "(" + StringUtil.getString(agrsString1) + (containOver ? ") OVER (" : ") AGAINST (")
- + StringUtil.getString(argsString2) + ")" + suffix // 传参不传空格,拼接带空格
+ expression = fun + "(" + StringUtil.get(agrsString1) + (containOver ? ") OVER (" : ") AGAINST (")
+ + StringUtil.get(argsString2) + ")" + suffix // 传参不传空格,拼接带空格
+ (StringUtil.isEmpty(alias, true) ? "" : getAs() + quote + alias + quote);
}
}
@@ -2867,7 +2867,7 @@ public String getValuesString() {
}
items[i] += ")";
}
- s = StringUtil.getString(items);
+ s = StringUtil.get(items);
}
return s;
}
@@ -3367,7 +3367,7 @@ protected String parseCombineExpression(RequestMethod method, String quote, Stri
, Map conditionMap, String combine, boolean verifyName, boolean containRaw, boolean isHaving) throws Exception {
String errPrefix = table + (isHaving ? ":{ @having:{ " : ":{ ") + "@combine:'" + combine + (isHaving ? "' } }" : "' }");
- String s = StringUtil.getString(combine);
+ String s = StringUtil.get(combine);
if (s.startsWith(" ") || s.endsWith(" ") ) {
throw new IllegalArgumentException(errPrefix + " 中字符 '" + s
+ "' 不合法!不允许首尾有空格,也不允许连续空格!空格不能多也不能少!"
@@ -5463,7 +5463,7 @@ public static SQLConfig newSQLConfig(RequestMethod method,
String database = request.getString(KEY_DATABASE);
if (StringUtil.isEmpty(database, false) == false && DATABASE_LIST.contains(database) == false) {
throw new UnsupportedDataTypeException("@database:value 中 value 错误,只能是 ["
- + StringUtil.getString(DATABASE_LIST.toArray()) + "] 中的一种!");
+ + StringUtil.get(DATABASE_LIST.toArray()) + "] 中的一种!");
}
String datasource = request.getString(KEY_DATASOURCE);
@@ -5748,7 +5748,7 @@ else if (userId instanceof Subquery) {}
}
column = (id == null ? "" : idKey + ",") + (userId == null ? "" : userIdKey + ",")
- + StringUtil.getString(columns); //set已经判断过不为空
+ + StringUtil.get(columns); //set已经判断过不为空
int idCount = id == null ? (userId == null ? 0 : 1) : (userId == null ? 1 : 2);
int size = idCount + columns.length; // 以 key 数量为准
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 35d1efda2..838f5a8a8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -190,8 +190,8 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) thr
Log.d(TAG, "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
+ "\n已生成 " + generatedSQLCount + " 条 SQL"
+ "\nexecute startTime = " + startTime
- + "\ndatabase = " + StringUtil.getString(config.getDatabase())
- + "; schema = " + StringUtil.getString(config.getSchema())
+ + "\ndatabase = " + StringUtil.get(config.getDatabase())
+ + "; schema = " + StringUtil.get(config.getSchema())
+ "; sql = \n" + sql
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index b2eca8bc8..e4fb2526f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -34,8 +34,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import apijson.orm.script.JavaScriptExecutor;
-import apijson.orm.script.ScriptExecutor;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@@ -69,7 +67,6 @@
import apijson.orm.model.TestRecord;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
/**校验器(权限、请求参数、返回结果等)
@@ -295,7 +292,7 @@ public boolean verifyAccess(SQLConfig config) throws Exception {
if (ROLE_MAP.containsKey(role) == false) {
Set NAMES = ROLE_MAP.keySet();
throw new IllegalArgumentException("角色 " + role + " 不存在!" +
- "只能是[" + StringUtil.getString(NAMES.toArray()) + "]中的一种!");
+ "只能是[" + StringUtil.get(NAMES.toArray()) + "]中的一种!");
}
if (role.equals(UNKNOWN) == false) { //未登录的角色
@@ -435,7 +432,7 @@ else if (id instanceof String) {
Object oid;
for (List ovl : ovs) {
oid = ovl == null || index >= ovl.size() ? null : ovl.get(index);
- if (oid == null || StringUtil.getString(oid).equals("" + visitorId) == false) {
+ if (oid == null || StringUtil.get(oid).equals("" + visitorId) == false) {
throw new IllegalAccessException(visitorIdKey + " = " + oid + " 的 " + table
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
}
@@ -459,7 +456,7 @@ else if (id instanceof String) {
}
else {
requestId = config.getWhere(visitorIdKey, true);//JSON里数值不能保证是Long,可能是Integer
- if (requestId != null && StringUtil.getString(requestId).equals(StringUtil.getString(visitorId)) == false) {
+ if (requestId != null && StringUtil.get(requestId).equals(StringUtil.get(visitorId)) == false) {
throw new IllegalAccessException(visitorIdKey + " = " + requestId + " 的 " + table
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
}
@@ -934,11 +931,11 @@ public static JSONObject parse(@NotNull final RequestMethod m
JSONObject update = target.getJSONObject(UPDATE.name());
JSONObject replace = target.getJSONObject(REPLACE.name());
- String exist = StringUtil.getString(target.getString(EXIST.name()));
- String unique = StringUtil.getString(target.getString(UNIQUE.name()));
- String remove = StringUtil.getString(target.getString(REMOVE.name()));
- String must = StringUtil.getString(target.getString(MUST.name()));
- String refuse = StringUtil.getString(target.getString(REFUSE.name()));
+ String exist = StringUtil.get(target.getString(EXIST.name()));
+ String unique = StringUtil.get(target.getString(UNIQUE.name()));
+ String remove = StringUtil.get(target.getString(REMOVE.name()));
+ String must = StringUtil.get(target.getString(MUST.name()));
+ String refuse = StringUtil.get(target.getString(REFUSE.name()));
Object _if = target.get(IF.name());
boolean ifIsStr = _if instanceof String && StringUtil.isNotEmpty(_if, true);
@@ -952,7 +949,7 @@ public static JSONObject parse(@NotNull final RequestMethod m
// Object code = target.get(CODE.name());
- String allowPartialUpdateFail = StringUtil.getString(target.getString(ALLOW_PARTIAL_UPDATE_FAIL.name()));
+ String allowPartialUpdateFail = StringUtil.get(target.getString(ALLOW_PARTIAL_UPDATE_FAIL.name()));
// 移除字段<<<<<<<<<<<<<<<<<<<
@@ -1097,7 +1094,7 @@ public static JSONObject parse(@NotNull final RequestMethod m
for (String rk : rkset) {
if (refuseSet.contains(rk)) { // 不允许的字段
throw new IllegalArgumentException(method + "请求," + name
- + " 里面不允许传 " + rk + " 等" + StringUtil.getString(refuseSet) + "内的任何字段!");
+ + " 里面不允许传 " + rk + " 等" + StringUtil.get(refuseSet) + "内的任何字段!");
}
if (rk == null) { // 无效的key
diff --git a/APIJSONORM/src/main/java/apijson/orm/Logic.java b/APIJSONORM/src/main/java/apijson/orm/Logic.java
index cfc08d016..bb8e806e6 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Logic.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Logic.java
@@ -42,7 +42,7 @@ public Logic(int type) {
}
public Logic(String key) {
this.originKey = key;
- key = StringUtil.getString(key);
+ key = StringUtil.get(key);
int type = getType(key.isEmpty() ? "" : key.substring(key.length() - 1));
diff --git a/APIJSONORM/src/main/java/apijson/orm/Pair.java b/APIJSONORM/src/main/java/apijson/orm/Pair.java
index a1f471c42..71ed7eb2c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Pair.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Pair.java
@@ -79,7 +79,7 @@ public static String toPairString(String typeKey, String valueKey) {
* @return
*/
public static String toPairString(Class> type, Object value) {
- return toPairString(type == null ? null : type.getSimpleName(), StringUtil.getString(value));
+ return toPairString(type == null ? null : type.getSimpleName(), StringUtil.get(value));
}
/**
@@ -109,7 +109,7 @@ public static Entry parseEntry(String pair, boolean isRightValue
* @return @NonNull
*/
public static Entry parseEntry(String pair, boolean isRightValueDefault, String defaultValue) {
- pair = StringUtil.getString(pair);//让客户端去掉所有空格 getNoBlankString(pair);
+ pair = StringUtil.get(pair);//让客户端去掉所有空格 getNoBlankString(pair);
Entry entry = new Entry();
if (pair.isEmpty() == false) {
int index = pair.indexOf(":");
@@ -137,7 +137,7 @@ public static Entry parseVariableEntry(String pair) {
* @return
*/
public static Entry, Object> parseVariableEntry(String pair, Map valueMap) {
- pair = StringUtil.getString(pair);//让客户端去掉所有空格 getNoBlankString(pair);
+ pair = StringUtil.get(pair);//让客户端去掉所有空格 getNoBlankString(pair);
Entry, Object> entry = new Entry, Object>();
if (pair.isEmpty() == false) {
int index = pair.contains(":") ? pair.indexOf(":") : -1;
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index ac541c8da..e86fd0fc0 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -151,7 +151,7 @@ public interface SQLConfig {
@NotNull
default int[] getDBVersionNums() {
- String dbVersion = StringUtil.getNoBlankString(getDBVersion());
+ String dbVersion = StringUtil.noBlank(getDBVersion());
if (dbVersion.isEmpty()) {
return new int[]{0};
}
From 970aee59fed8de155a92dae77af8ff82b06c42c3 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 16 Mar 2025 17:54:09 +0800
Subject: [PATCH 110/145] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 838f5a8a8..2165c3b05 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1133,7 +1133,6 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
if (castToJson == false) {
List json = config.getJson();
castToJson = json != null && json.contains(label);
- castToJson = json != null && json.contains(label);
}
if (castToJson) {
try {
From dd374314a0fd5a82d9c8170f11c028f4a57fd103 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 23 Mar 2025 23:05:10 +0800
Subject: [PATCH 111/145] =?UTF-8?q?APIJSON=20=E2=80=93=20The=20No-Code=20A?=
=?UTF-8?q?PI=20Revolution=20That=20Puts=20Developers=20in=20the=20Fast=20?=
=?UTF-8?q?Lane?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
thank you, [Hazem Abbas](https://medevel.com/author/hazem/), for posting this article ~
https://medevel.com/apijson
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 67610d168..4dc10db9e 100644
--- a/README.md
+++ b/README.md
@@ -622,6 +622,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[wend看源码-ORM-APIJSON](https://itwend.blog.csdn.net/article/details/143980281)
+[APIJSON – The No-Code API Revolution That Puts Developers in the Fast Lane](https://medevel.com/apijson)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
From 6712e9602cd1a2db95667a3c7cbd29e35233ef66 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sun, 30 Mar 2025 05:16:12 +0800
Subject: [PATCH 112/145] =?UTF-8?q?=E5=8E=BB=E9=99=A4=20fastjson=EF=BC=8CJ?=
=?UTF-8?q?SONObject=20=E7=94=A8=20M=20extends=20Map=20?=
=?UTF-8?q?=E6=9B=BF=E4=BB=A3=EF=BC=8CJSONArray=20=E7=94=A8=20L=20extends?=
=?UTF-8?q?=20List=20=E6=9B=BF=E4=BB=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 5 -
APIJSONORM/src/main/java/apijson/JSON.java | 670 +++++++++++++-----
.../src/main/java/apijson/JSONArray.java | 221 ++++++
.../src/main/java/apijson/JSONCreator.java | 43 ++
.../src/main/java/apijson/JSONField.java | 5 +
.../src/main/java/apijson/JSONObject.java | 145 +++-
.../src/main/java/apijson/JSONParser.java | 27 +
.../src/main/java/apijson/JSONResponse.java | 155 ++--
.../apijson/orm/AbstractFunctionParser.java | 309 +++++---
.../apijson/orm/AbstractObjectParser.java | 273 ++++---
.../main/java/apijson/orm/AbstractParser.java | 438 ++++++------
.../java/apijson/orm/AbstractSQLConfig.java | 412 +++++------
.../java/apijson/orm/AbstractSQLExecutor.java | 171 +++--
.../java/apijson/orm/AbstractVerifier.java | 322 ++++-----
.../main/java/apijson/orm/FunctionParser.java | 33 +-
.../main/java/apijson/orm/JSONRequest.java | 11 +-
.../src/main/java/apijson/orm/Join.java | 35 +-
.../main/java/apijson/orm/ObjectParser.java | 56 +-
.../java/apijson/orm/OnParseCallback.java | 10 +-
.../src/main/java/apijson/orm/Pair.java | 11 +-
.../src/main/java/apijson/orm/Parser.java | 68 +-
.../main/java/apijson/orm/ParserCreator.java | 9 +-
.../src/main/java/apijson/orm/SQLConfig.java | 118 +--
.../src/main/java/apijson/orm/SQLCreator.java | 9 +-
.../main/java/apijson/orm/SQLExecutor.java | 37 +-
.../src/main/java/apijson/orm/Subquery.java | 35 +-
.../src/main/java/apijson/orm/Verifier.java | 25 +-
.../java/apijson/orm/VerifierCreator.java | 7 +-
.../orm/exception/CommonException.java | 4 +-
.../orm/script/JSR223ScriptExecutor.java | 11 +-
.../orm/script/JavaScriptExecutor.java | 7 +-
.../apijson/orm/script/ScriptExecutor.java | 9 +-
32 files changed, 2273 insertions(+), 1418 deletions(-)
create mode 100644 APIJSONORM/src/main/java/apijson/JSONArray.java
create mode 100755 APIJSONORM/src/main/java/apijson/JSONCreator.java
create mode 100644 APIJSONORM/src/main/java/apijson/JSONField.java
create mode 100755 APIJSONORM/src/main/java/apijson/JSONParser.java
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 2cf436bd5..b4a860789 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -21,11 +21,6 @@
-
- com.alibaba
- fastjson
- 1.2.83
-
diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index 0a1f901b7..c6573a650 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -4,269 +4,575 @@
package apijson;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.parser.Feature;
-import com.alibaba.fastjson.serializer.SerializerFeature;
-
import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
+import apijson.Log;
+import apijson.StringUtil;
-/**阿里FastJSON封装类 防止解析时异常
+/**JSON工具类 防止解析时异常
* @author Lemon
*/
-public class JSON {
- private static final String TAG = "JSON";
+public interface JSON {
+ static final String TAG = "JSON";
- /**判断json格式是否正确
- * @param s
- * @return
- */
- public static boolean isJsonCorrect(String s) {
- //太长 Log.i(TAG, "isJsonCorrect <<<< " + s + " >>>>>>>");
- if (s == null
- // || s.equals("[]")
- // || s.equals("{}")
- || s.equals("")
- || s.equals("[null]")
- || s.equals("{null}")
- || s.equals("null")) {
- return false;
+ JSONParser extends Map, ? extends List> DEFAULT_JSON_PARSER = new JSONParser<>() {
+
+ @Override
+ public Map parseJSON(Object json) {
+ return Map.of();
}
- return true;
- }
- /**获取有效的json
- * @param s
- * @return
- */
- public static String getCorrectJson(String s) {
- return getCorrectJson(s, false);
- }
- /**获取有效的json
- * @param s
- * @param isArray
- * @return
- */
- public static String getCorrectJson(String s, boolean isArray) {
- s = StringUtil.trim(s);
- // if (isArray) {
- // while (s.startsWith("\"")) {
- // s = s.substring(1);
- // }
- // while (s.endsWith("\"")) {
- // s = s.substring(0, s.length() - 1);
- // }
- // }
- return s;//isJsonCorrect(s) ? s : null;
+ @Override
+ public Map parseObject(Object json) {
+ return Map.of();
+ }
+
+ @Override
+ public T parseObject(Object json, Class clazz) {
+ return null;
+ }
+
+ @Override
+ public List parseArray(Object json) {
+ return List.of();
+ }
+
+ @Override
+ public List parseArray(Object json, Class clazz) {
+ return List.of();
+ }
+
+ //
+ @Override
+ public String toJSONString(Object obj) {
+ return JSON.toJSONString(obj);
+ }
+
+ @Override
+ public Map createJSONObject() {
+ return new LinkedHashMap<>();
+ }
+
+ @Override
+ public List createJSONArray() {
+ return new ArrayList<>();
+ }
+ };
+
+ public static JSONCreator extends Map, ? extends List> DEFAULT_JSON_CREATOR = DEFAULT_JSON_PARSER;
+
+
+ public static Object parseJSON(Object json) throws Exception {
+ if (json instanceof Boolean || json instanceof Number || json instanceof Enum>) {
+ return json;
+ }
+
+ String s = StringUtil.trim(toJSONString(json));
+ if (s.startsWith("{")) {
+ return parseObject(json, DEFAULT_JSON_PARSER);
+ }
+
+ if (s.startsWith("[")) {
+ return parseArray(json, DEFAULT_JSON_PARSER);
+ }
+
+ throw new UnsupportedDataTypeException("JSON 格式错误!" + s);
}
/**
* @param json
* @return
*/
- private static final Feature[] DEFAULT_FASTJSON_FEATURES = {Feature.OrderedField, Feature.UseBigDecimal};
- public static Object parse(Object obj) {
+ public static Map parseObject(Object json) {
+ return parseObject(json, DEFAULT_JSON_PARSER);
+ }
+ public static , L extends List> M parseObject(Object json, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
try {
- return com.alibaba.fastjson.JSON.parse(obj instanceof String ? (String) obj : toJSONString(obj), DEFAULT_FASTJSON_FEATURES);
+ M obj = parser.parseObject(s);
+ return obj;
} catch (Exception e) {
- Log.i(TAG, "parse catch \n" + e.getMessage());
+ Log.i(TAG, "parseObject catch \n" + e.getMessage());
}
return null;
}
+ public static , L extends List> T parseObject(Object json, Class clazz, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
- /**obj转JSONObject
- * @param obj
- * @return
- */
- public static JSONObject parseObject(Object obj) {
- if (obj instanceof JSONObject) {
- return (JSONObject) obj;
+ try {
+ T obj = parser.parseObject(s, clazz);
+ return obj;
+ } catch (Exception e) {
+ Log.i(TAG, "parseObject catch \n" + e.getMessage());
}
- return parseObject(toJSONString(obj));
+ return null;
}
- /**json转JSONObject
+
+ /**
* @param json
* @return
*/
- public static JSONObject parseObject(String json) {
- return parseObject(json, JSONObject.class);
+ public static List parseArray(Object json) {
+ return parseArray(json, DEFAULT_JSON_PARSER);
}
- /**json转实体类
- * @param json
- * @param clazz
+
+ public static , L extends List> L parseArray(Object json, JSONParser parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
+ try {
+ L arr = parser.parseArray(s);
+ return arr;
+ } catch (Exception e) {
+ Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ }
+ return null;
+ }
+
+// public static > List parseArray(Object json, Class clazz) {
+// return parseArray(json, clazz, DEFAULT_JSON_PARSER);
+// }
+ public static > List parseArray(Object json, Class clazz, JSONParser> parser) {
+ String s = toJSONString(json);
+ if (StringUtil.isEmpty(s, true)) {
+ return null;
+ }
+
+ try {
+ List arr = parser.parseArray(s, clazz);
+ return arr;
+ } catch (Exception e) {
+ Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * @param obj
* @return
*/
- public static T parseObject(String json, Class clazz) {
- if (clazz == null || StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseObject clazz == null || StringUtil.isEmpty(json, true) >> return null;");
- } else {
- try {
- return com.alibaba.fastjson.JSON.parseObject(getCorrectJson(json), clazz, DEFAULT_FASTJSON_FEATURES);
- } catch (Exception e) {
- Log.i(TAG, "parseObject catch \n" + e.getMessage());
+ public static String toJSONString(Object obj) {
+ if (obj == null) {
+ return null;
+ }
+
+ // In a real implementation, you would use a JSON parser library
+ // Here we're just providing a basic implementation to replace fastjson
+ try {
+ // For now, this is a placeholder. In a real implementation,
+ // you would convert the object to a JSON string
+ if (obj instanceof String) {
+ return (String) obj;
+ }
+
+ if (obj instanceof Map) {
+ // Simple JSON object format
+ StringBuilder sb = new StringBuilder("{");
+ @SuppressWarnings("unchecked")
+ Map map = (Map) obj;
+ boolean first = true;
+ for (Map.Entry entry : map.entrySet()) {
+ if (! first) {
+ sb.append(",");
+ }
+ first = false;
+ sb.append("\"").append(entry.getKey()).append("\":");
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ sb.append("\"").append(value).append("\"");
+ } else {
+ sb.append(toJSONString(value));
+ }
+ }
+ sb.append("}");
+ return sb.toString();
}
+
+ if (obj instanceof List) {
+ // Simple JSON array format
+ StringBuilder sb = new StringBuilder("[");
+ @SuppressWarnings("unchecked")
+ List list = (List) obj;
+ boolean first = true;
+ for (Object item : list) {
+ if (! first) {
+ sb.append(",");
+ }
+ first = false;
+ if (item instanceof String) {
+ sb.append("\"").append(item).append("\"");
+ } else {
+ sb.append(toJSONString(item));
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ return obj.toString();
+ }
+ catch (Exception e) {
+ Log.i(TAG, "toJSONString catch \n" + e.getMessage());
}
return null;
}
- /**list转JSONArray
- * @param list
+
+ /**判断是否为JSONObject或JSONArray的isXxx方法名
+ * @param key
* @return
*/
- public static JSONArray parseArray(List list) {
- return new JSONArray(list);
+ public static boolean isJSONType(String key) {
+ return key != null && key.startsWith("is") && key.length() > 2 && key.contains("JSON");
}
- /**obj转JSONArray
- * @param obj
- * @return
+
+ public static boolean isBooleanOrNumberOrString(Object obj) {
+ return obj instanceof Boolean || obj instanceof Number || obj instanceof String;
+ }
+
+ /**
+ * Get a value from a Map and convert to the specified type
+ * @param map Source map
+ * @param key The key
+ * @param Target type
+ * @return The converted value
*/
- public static JSONArray parseArray(Object obj) {
- if (obj instanceof JSONArray) {
- return (JSONArray) obj;
- }
- return parseArray(toJSONString(obj));
+ @SuppressWarnings("unchecked")
+ public static T get(Map map, String key) {
+ return map == null || key == null ? null : (T) map.get(key);
}
- /**json转JSONArray
- * @param json
- * @return
+
+ /**
+ * Get a value from a Map and convert to the specified type
+ * @param list Source map
+ * @param index The key
+ * @param Target type
+ * @return The converted value
*/
- public static JSONArray parseArray(String json) {
- if (StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseArray StringUtil.isEmpty(json, true) >> return null;");
- } else {
- try {
- return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true));
- } catch (Exception e) {
- Log.i(TAG, "parseArray catch \n" + e.getMessage());
- }
+ @SuppressWarnings("unchecked")
+ public static T get(List list, int index) {
+ return list == null || index < 0 || index >= list.size() ? null : (T) list.get(index);
+ }
+// /**
+// * Get a value from a Map and convert to the specified type
+// * @param map Source map
+// * @param key The key
+// * @param Target type
+// * @return The converted value
+// */
+// @SuppressWarnings("unchecked")
+// public static T get(List list, int index) {
+// return list == null || index < 0 || index >= list.size() ? null : list.get(index);
+// }
+
+ /**
+ * Get a Map value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The Map value
+ * @throws UnsupportedDataTypeException If value is not a Map and cannot be converted
+ */
+ @SuppressWarnings("unchecked")
+ public static Map getMap(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
}
- return null;
+
+ if (value instanceof Map) {
+ return (Map) value;
+ }
+
+ throw new UnsupportedDataTypeException("Value for key '" + key + "' is not a Map: " + value.getClass().getName());
}
- /**JSONArray转实体类列表
- * @param array
- * @param clazz
- * @return
+
+ /**
+ * Get a List value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The List value
+ * @throws UnsupportedDataTypeException If value is not a List and cannot be converted
*/
- public static List parseArray(JSONArray array, Class clazz) {
- return parseArray(toJSONString(array), clazz);
+ @SuppressWarnings("unchecked")
+ public static List getList(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof List) {
+ return (List) value;
+ }
+
+ throw new UnsupportedDataTypeException("Value for key '" + key + "' is not a List: " + value.getClass().getName());
}
- /**json转实体类列表
- * @param json
- * @param clazz
- * @return
+
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static List parseArray(String json, Class clazz) {
- if (clazz == null || StringUtil.isEmpty(json, true)) {
- Log.e(TAG, "parseArray clazz == null || StringUtil.isEmpty(json, true) >> return null;");
- } else {
+ public static Integer getInteger(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
+ }
+
+ if (value instanceof String) {
try {
- return com.alibaba.fastjson.JSON.parseArray(getCorrectJson(json, true), clazz);
- } catch (Exception e) {
- Log.i(TAG, "parseArray catch \n" + e.getMessage());
+ return Integer.parseInt((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
}
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**实体类转json
- * @param obj
- * @return
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static String toJSONString(Object obj) {
- if (obj == null) {
- return null;
+ public static int getIntValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
}
- if (obj instanceof String) {
- return (String) obj;
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
}
- try {
- return com.alibaba.fastjson.JSON.toJSONString(obj);
- } catch (Exception e) {
- Log.e(TAG, "toJSONString catch \n" + e.getMessage());
+
+ if (value instanceof String) {
+ try {
+ return Integer.parseInt((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
+ }
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**实体类转json
- * @param obj
- * @param features
- * @return
+ /**
+ * Get an int value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The int value
+ * @throws UnsupportedDataTypeException If value cannot be converted to int
*/
- public static String toJSONString(Object obj, SerializerFeature... features) {
- if (obj == null) {
+ public static Long getLong(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
return null;
}
- if (obj instanceof String) {
- return (String) obj;
+
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
}
- try {
- return com.alibaba.fastjson.JSON.toJSONString(obj, features);
- } catch (Exception e) {
- Log.e(TAG, "toJSONString catch \n" + e.getMessage());
+
+ if (value instanceof String) {
+ try {
+ return Long.parseLong((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to int: " + e.getMessage());
+ }
}
- return null;
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to int");
}
- /**格式化,显示更好看
- * @param json
- * @return
+ /**
+ * Get a long value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The long value
+ * @throws UnsupportedDataTypeException If value cannot be converted to long
*/
- public static String format(String json) {
- return format(parse(json));
+ public static long getLongValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+
+ if (value instanceof String) {
+ try {
+ return Long.parseLong((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to long: " + e.getMessage());
+ }
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to long");
}
- /**格式化,显示更好看
- * @param object
- * @return
+
+ /**
+ * Get a double value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The double value
+ * @throws UnsupportedDataTypeException If value cannot be converted to double
*/
- public static String format(Object object) {
- return toJSONString(object, SerializerFeature.PrettyFormat);
+ public static Double getDouble(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
+ }
+
+ if (value instanceof String) {
+ try {
+ return Double.parseDouble((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to double: " + e.getMessage());
+ }
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to double");
}
- /**判断是否为JSONObject
- * @param obj instanceof String ? parseObject
- * @return
+ /**
+ * Get a double value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The double value
+ * @throws UnsupportedDataTypeException If value cannot be converted to double
*/
- public static boolean isJSONObject(Object obj) {
- if (obj instanceof JSONObject) {
- return true;
+ public static double getDoubleValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return 0;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
}
- if (obj instanceof String) {
+
+ if (value instanceof String) {
try {
- JSONObject json = parseObject((String) obj);
- return json != null && json.isEmpty() == false;
- } catch (Exception e) {
- Log.e(TAG, "isJSONObject catch \n" + e.getMessage());
+ return Double.parseDouble((String) value);
+ } catch (NumberFormatException e) {
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to double: " + e.getMessage());
}
}
- return false;
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to double");
}
- /**判断是否为JSONArray
- * @param obj instanceof String ? parseArray
- * @return
+
+
+ /**
+ * Get a boolean value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The boolean value
+ * @throws UnsupportedDataTypeException If value cannot be converted to boolean
*/
- public static boolean isJSONArray(Object obj) {
- if (obj instanceof JSONArray) {
- return true;
+ public static Boolean getBoolean(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
}
- if (obj instanceof String) {
- try {
- JSONArray json = parseArray((String) obj);
- return json != null && json.isEmpty() == false;
- } catch (Exception e) {
- Log.e(TAG, "isJSONArray catch \n" + e.getMessage());
+
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ if (value instanceof String) {
+ String str = ((String) value).toLowerCase();
+ if (str.equals("true") || str.equals("false")) {
+ return Boolean.parseBoolean(str);
+ }
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to boolean");
+ }
+
+ if (value instanceof Number) {
+ int intValue = ((Number) value).intValue();
+ if (intValue == 0 || intValue == 1) {
+ return intValue != 0;
}
+ throw new UnsupportedDataTypeException("Cannot convert Number value '" + value + "' to boolean. Only 0 and 1 are supported.");
}
- return false;
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to boolean");
}
- /**判断是否为 Boolean,Number,String 中的一种
- * @param obj
- * @return
+ /**
+ * Get a boolean value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The boolean value
+ * @throws UnsupportedDataTypeException If value cannot be converted to boolean
*/
- public static boolean isBooleanOrNumberOrString(Object obj) {
- return obj instanceof Boolean || obj instanceof Number || obj instanceof String;
+ public static boolean getBooleanValue(Map map, String key) throws UnsupportedDataTypeException {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return false;
+ }
+
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ if (value instanceof String) {
+ String str = ((String) value).toLowerCase();
+ if (str.equals("true") || str.equals("false")) {
+ return Boolean.parseBoolean(str);
+ }
+ throw new UnsupportedDataTypeException("Cannot convert String value '" + value + "' to boolean");
+ }
+
+ if (value instanceof Number) {
+ int intValue = ((Number) value).intValue();
+ if (intValue == 0 || intValue == 1) {
+ return intValue != 0;
+ }
+ throw new UnsupportedDataTypeException("Cannot convert Number value '" + value + "' to boolean. Only 0 and 1 are supported.");
+ }
+
+ throw new UnsupportedDataTypeException("Cannot convert value of type " + value.getClass().getName() + " to boolean");
}
+ /**
+ * Get a string value from a Map
+ * @param map Source map
+ * @param key The key
+ * @return The string value
+ */
+ public static String getString(Map map, String key) {
+ Object value = map == null || key == null ? null : map.get(key);
+ if (value == null) {
+ return null;
+ }
+
+ return value.toString();
+ }
}
diff --git a/APIJSONORM/src/main/java/apijson/JSONArray.java b/APIJSONORM/src/main/java/apijson/JSONArray.java
new file mode 100644
index 000000000..7760f6567
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONArray.java
@@ -0,0 +1,221 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+package apijson;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
+
+/**
+ * Custom JSONArray implementation based on ArrayList to replace com.alibaba.fastjson.JSONArray
+ * Maintains same API as fastjson but uses standard Java List implementation
+ * @author Lemon
+ */
+public class JSONArray extends ArrayList implements JSON {
+ private static final long serialVersionUID = 1L;
+ private static final String TAG = "JSONArray";
+
+ /**
+ * Create an empty JSONArray
+ */
+ public JSONArray() {
+ super();
+ }
+
+ /**
+ * Create a JSONArray with initial capacity
+ * @param initialCapacity the initial capacity
+ */
+ public JSONArray(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ /**
+ * Create a JSONArray from a Collection
+ * @param collection the collection to copy from
+ */
+ public JSONArray(Collection> collection) {
+ super();
+ if (collection != null) {
+ addAll(collection);
+ }
+ }
+
+ /**
+ * Create a JSONArray from a JSON string
+ * @param json JSON string
+ */
+ public JSONArray(String json) {
+ this();
+ List list = JSON.parseArray(json);
+ if (list != null) {
+ addAll(list);
+ }
+ }
+
+ /**
+ * Get a JSONObject at the specified index
+ * @param index the index
+ * @return the JSONObject or null if not a JSONObject
+ */
+ public JSONObject getJSONObject(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) obj;
+ return new JSONObject(map);
+ } else if (obj instanceof JSONObject) {
+ return (JSONObject) obj;
+ }
+ return null;
+ }
+
+ /**
+ * Get a JSONArray at the specified index
+ * @param index the index
+ * @return the JSONArray or null if not a JSONArray
+ */
+ public JSONArray getJSONArray(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof List) {
+ @SuppressWarnings("unchecked")
+ List list = (List) obj;
+ return new JSONArray(list);
+ } else if (obj instanceof List>) {
+ return (JSONArray) obj;
+ }
+ return null;
+ }
+
+ /**
+ * Get a boolean value at the specified index
+ * @param index the index
+ * @return the boolean value or false if not found
+ */
+ public boolean getBooleanValue(int index) {
+ if (index < 0 || index >= size()) {
+ return false;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Boolean) {
+ return (Boolean) obj;
+ } else if (obj instanceof Number) {
+ return ((Number) obj).intValue() != 0;
+ } else if (obj instanceof String) {
+ return Boolean.parseBoolean((String) obj);
+ }
+ return false;
+ }
+
+ /**
+ * Get an integer value at the specified index
+ * @param index the index
+ * @return the integer value or 0 if not found
+ */
+ public int getIntValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).intValue();
+ } else if (obj instanceof String) {
+ try {
+ return Integer.parseInt((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Get a long value at the specified index
+ * @param index the index
+ * @return the long value or 0 if not found
+ */
+ public long getLongValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0L;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).longValue();
+ } else if (obj instanceof String) {
+ try {
+ return Long.parseLong((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0L;
+ }
+
+ /**
+ * Get a double value at the specified index
+ * @param index the index
+ * @return the double value or 0 if not found
+ */
+ public double getDoubleValue(int index) {
+ if (index < 0 || index >= size()) {
+ return 0.0;
+ }
+
+ Object obj = get(index);
+ if (obj instanceof Number) {
+ return ((Number) obj).doubleValue();
+ } else if (obj instanceof String) {
+ try {
+ return Double.parseDouble((String) obj);
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
+ }
+ return 0.0;
+ }
+
+ /**
+ * Get a string value at the specified index
+ * @param index the index
+ * @return the string value or null if not found
+ */
+ public String getString(int index) {
+ if (index < 0 || index >= size()) {
+ return null;
+ }
+
+ Object obj = get(index);
+ return obj != null ? obj.toString() : null;
+ }
+
+ /**
+ * Add a value to the JSONArray
+ * @param obj the value to add
+ * @return this JSONArray
+ */
+ public JSONArray fluentAdd(Object obj) {
+ add(obj);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
+}
\ No newline at end of file
diff --git a/APIJSONORM/src/main/java/apijson/JSONCreator.java b/APIJSONORM/src/main/java/apijson/JSONCreator.java
new file mode 100755
index 000000000..fbd9da20d
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONCreator.java
@@ -0,0 +1,43 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+package apijson;
+
+import apijson.orm.SQLConfig;
+import apijson.orm.SQLExecutor;
+
+import java.util.List;
+import java.util.Map;
+
+/**SQL相关创建器
+ * @author Lemon
+ */
+public interface JSONCreator, L extends List> {
+
+ @NotNull
+ M createJSONObject();
+
+// @NotNull
+// M createJSONObject(String json);
+
+ @NotNull
+ default M createJSONObject(Map m) {
+ M obj = createJSONObject();
+ obj.putAll(m);
+ return obj;
+ }
+
+ @NotNull
+ L createJSONArray();
+// @NotNull
+// L createJSONArray(String json);
+
+ @NotNull
+ default L createJSONArray(List l){
+ L arr = createJSONArray();
+ arr.addAll(l);
+ return arr;
+ }
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONField.java b/APIJSONORM/src/main/java/apijson/JSONField.java
new file mode 100644
index 000000000..11a5cc309
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONField.java
@@ -0,0 +1,5 @@
+package apijson;
+
+public @interface JSONField {
+ boolean serialize() default true;
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 0adf18365..ee6828279 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -9,6 +9,10 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import apijson.orm.exception.UnsupportedDataTypeException;
/**use this class instead of com.alibaba.fastjson.JSONObject
* @author Lemon
@@ -16,7 +20,7 @@
* @see #puts
* @see #putsAll
*/
-public class JSONObject extends com.alibaba.fastjson.JSONObject {
+public class JSONObject extends LinkedHashMap implements JSON {
private static final long serialVersionUID = 1L;
private static final String TAG = "JSONObject";
@@ -25,27 +29,44 @@ public class JSONObject extends com.alibaba.fastjson.JSONObject {
/**ordered
*/
public JSONObject() {
- super(true);
+ super();
}
/**transfer Object to JSONObject
* @param object
* @see {@link #JSONObject(Object)}
*/
public JSONObject(Object object) {
- this(toJSONString(object));
+ this();
+ if (object instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) object;
+ putAll(map);
+ } else if (object != null) {
+ String json = JSON.toJSONString(object);
+ if (json != null) {
+ Map map = JSON.parseObject(json);
+ if (map != null) {
+ putAll(map);
+ }
+ }
+ }
}
/**parse JSONObject with JSON String
* @param json
* @see {@link #JSONObject(String)}
*/
public JSONObject(String json) {
- this(parseObject(json));
+ this();
+ Map map = JSON.parseObject(json);
+ if (map != null) {
+ putAll(map);
+ }
}
/**transfer com.alibaba.fastjson.JSONObject to JSONObject
* @param object
* @see {@link #putsAll(Map extends String, ? extends Object>)}
*/
- public JSONObject(com.alibaba.fastjson.JSONObject object) {
+ public JSONObject(Map object) {
this();
putsAll(object);
}
@@ -682,6 +703,116 @@ public Object put(String key, Object value) {
return super.put(key, value);
}
-
-
+ /**
+ * Get a boolean value from the JSONObject
+ * @param key the key
+ * @return the boolean value or false if not found
+ */
+ public boolean getBooleanValue(String key) {
+ try {
+ return JSON.getBooleanValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Get an integer value from the JSONObject
+ * @param key the key
+ * @return the integer value or 0 if not found
+ */
+ public int getIntValue(String key) {
+ try {
+ return JSON.getIntValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Get a long value from the JSONObject
+ * @param key the key
+ * @return the long value or 0 if not found
+ */
+ public long getLongValue(String key) {
+ try {
+ return JSON.getLongValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0L;
+ }
+ }
+
+ /**
+ * Get a double value from the JSONObject
+ * @param key the key
+ * @return the double value or 0 if not found
+ */
+ public double getDoubleValue(String key) {
+ try {
+ return JSON.getDoubleValue(this, key);
+ } catch (UnsupportedDataTypeException e) {
+ return 0.0;
+ }
+ }
+
+ /**
+ * Get a string value from the JSONObject
+ * @param key the key
+ * @return the string value or null if not found
+ */
+ public String getString(String key) {
+ Object value = get(key);
+ return value != null ? value.toString() : null;
+ }
+
+ /**
+ * Get a JSONObject value from the JSONObject
+ * @param key the key
+ * @return the JSONObject value or null if not found
+ */
+ public JSONObject getJSONObject(String key) {
+ try {
+ Map map = JSON.getMap(this, key);
+ return map != null ? new JSONObject(map) : null;
+ } catch (UnsupportedDataTypeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get a JSONArray value from the JSONObject
+ * @param key the key
+ * @return the JSONArray value or null if not found
+ */
+ public JSONArray getJSONArray(String key) {
+ try {
+ List list = JSON.getList(this, key);
+ return list != null ? new JSONArray(list) : null;
+ } catch (UnsupportedDataTypeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Check if the JSONObject is empty or has no values other than null
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ if (super.isEmpty()) {
+ return true;
+ }
+
+ Set> set = entrySet();
+ for (Entry entry : set) {
+ if (entry.getValue() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return JSON.toJSONString(this);
+ }
}
diff --git a/APIJSONORM/src/main/java/apijson/JSONParser.java b/APIJSONORM/src/main/java/apijson/JSONParser.java
new file mode 100755
index 000000000..1199ef5d5
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/JSONParser.java
@@ -0,0 +1,27 @@
+/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+package apijson;
+
+import java.util.List;
+import java.util.Map;
+
+/**SQL相关创建器
+ * @author Lemon
+ */
+public interface JSONParser, L extends List> extends JSONCreator {
+
+ M parseJSON(Object json);
+
+ M parseObject(Object json);
+
+ T parseObject(Object json, Class clazz);
+
+ L parseArray(Object json);
+
+ List parseArray(Object json, Class clazz);
+
+ String toJSONString(Object obj);
+}
diff --git a/APIJSONORM/src/main/java/apijson/JSONResponse.java b/APIJSONORM/src/main/java/apijson/JSONResponse.java
index 21f3fe8f6..7ee4f9f41 100755
--- a/APIJSONORM/src/main/java/apijson/JSONResponse.java
+++ b/APIJSONORM/src/main/java/apijson/JSONResponse.java
@@ -5,11 +5,11 @@
package apijson;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
+import apijson.orm.exception.UnsupportedDataTypeException;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+
+import static apijson.JSON.parseObject;
/**parser for response
* @author Lemon
@@ -19,7 +19,7 @@
*