From f044d5f903e6a109014ab438b23a36689340b705 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 00:41:42 +0800
Subject: [PATCH 021/674] Update Document.md
---
Document.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Document.md b/Document.md
index 5da8589ae..73f262b15 100644
--- a/Document.md
+++ b/Document.md
@@ -5,7 +5,7 @@ https://github.com/Tencent/APIJSON

-后端开发者可以先看 [图文入门教程1](https://vincentcheng.github.io/apijson-doc/zh) 或 [图文入门教程2](https://hanxu2018.github.io/APIJSON-DOC/) (都非官方,和本文档有出入的点以本文档为准。例如正则匹配 key? 已废弃,用 key~ 替代;例如 "@column":"store_id,sum(amt):totAmt" 中逗号 , 有误,应该用分号 ; 隔开 SQL 函数,改为 "@column":"store_id;sum(amt):totAmt")
+后端开发者可以先看 [图文入门教程1](http://apijson.cn/doc/zh/) 或 [图文入门教程2](https://hanxu2018.github.io/APIJSON-DOC/) (和本文档有出入的点以本文档为准。例如正则匹配 key? 已废弃,用 key~ 替代;例如 "@column":"store_id,sum(amt):totAmt" 中逗号 , 有误,应该用分号 ; 隔开 SQL 函数,改为 "@column":"store_id;sum(amt):totAmt")
* ### [1.示例](#1)
* ### [2.对比传统方式](#2)
From 7e60828122961f7cd50fa29deec64c1814e04a85 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 00:52:57 +0800
Subject: [PATCH 022/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index dd8d36af8..7b1908260 100644
--- a/README.md
+++ b/README.md
@@ -353,7 +353,7 @@ https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-[DB2](https://www.ibm.com/support/knowledgecenter/SSEPGG_11.1.0/com.ibm.db2.luw.sql.ref.doc/doc/r0059224.html), [Elasticsearch](https://www.elastic.co/cn/what-is/elasticsearch-sql), [ClickHouse](https://clickhouse.tech/docs/zh/sql-reference/syntax/), [OceanBase](https://www.oceanbase.com/docs/oceanbase/V2.2.50/ss-sr-select_daur3l), [Presto](https://prestodb.io/docs/current/admin/function-namespace-managers.html), [Spark](http://spark.apache.org/sql/), [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Select)(延伸支持 Hadoop, Spark), [Phoenix](http://phoenix.apache.org/language/index.html#select)(延伸支持 HBase), [Presto/Trino](https://prestodb.io/docs/current/sql/select.html)(延伸支持 Redis, Hive, Kafka, Elasticsearch, Thrift, Cassandra, MySQL, PostgreSQL, Oracle, MongoDB...)
+[Elasticsearch](https://www.elastic.co/cn/what-is/elasticsearch-sql), [OceanBase](https://www.oceanbase.com/docs/oceanbase/V2.2.50/ss-sr-select_daur3l), [Presto](https://prestodb.io/docs/current/admin/function-namespace-managers.html), [Spark](http://spark.apache.org/sql/), [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Select)(延伸支持 Hadoop, Spark), [Phoenix](http://phoenix.apache.org/language/index.html#select)(延伸支持 HBase), [Presto/Trino](https://prestodb.io/docs/current/sql/select.html)(延伸支持 Redis, Hive, Kafka, Elasticsearch, Thrift, Cassandra, MySQL, PostgreSQL, Oracle, MongoDB...)
### 我要赞赏
如果你喜欢 APIJSON,感觉 APIJSON 帮助到了你,可以点右上角 ⭐Star 支持一下,谢谢 ^_^
From 76c8885a9d47198fb07aee314944f4b39c91d0f0 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 00:53:45 +0800
Subject: [PATCH 023/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7b1908260..61e4709bb 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ This source code is licensed under the Apache License Version 2.0
-
+
From b85aed18b27e3ff130b6a6c6b707ceb80d666b23 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 00:58:35 +0800
Subject: [PATCH 024/674] Update Roadmap.md
---
Roadmap.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Roadmap.md b/Roadmap.md
index 74c3b4ca4..572fa41c1 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -7,7 +7,7 @@
### 新增功能
部分功能描述可在 [APIAuto](https://github.com/TommyLemon/APIAuto) 上查看
账号 13000002020 密码 123456
-http://apijson.org:8000/auto/
+http://apijson.cn:8000/api
##### 基本原则
1.一定要有相关的应用场景,不能是伪需求,最好举例说明
@@ -35,6 +35,7 @@ POST: 用不上,不处理
@having! 必须性不大,可通过反转内部条件来实现,但如果实现简单、且不影响原来的功能,则可以顺便加上。
#### 新增支持 @column!
+【更新:已提供字段插件 [apijson-column](https://github.com/APIJSON/apijson-column),支持 字段名映射 和 !key 反选字段。】
这个只在 [apijson-framework](https://github.com/APIJSON/apijson-framework) 支持,需要配置每个接口版本、每张表所拥有的全部字段,然后排除掉 @column! 的。
可新增一个 VersionedColumn 表记录来代替 HashMap 代码配置。
@@ -191,7 +192,8 @@ https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/
### 提高性能
-20200205 更新:最近的两次大幅提升性能相关优化及 Release
+20200205 更新:最近的及次大幅提升性能相关优化及 Release
+[4.8.0【性能】大幅提升提升单表数组查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.8.0)
[4.6.0【性能】大幅提升数组内主表查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.6.0)
[4.4.5【性能】大幅提升增删改的性能](https://github.com/Tencent/APIJSON/releases/tag/4.4.5)
From 1d9f0e24998e2c4bea2234bb214c694a7bb46905 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 01:03:20 +0800
Subject: [PATCH 025/674] Update Roadmap.md
---
Roadmap.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Roadmap.md b/Roadmap.md
index 572fa41c1..4a479cd2e 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -7,7 +7,7 @@
### 新增功能
部分功能描述可在 [APIAuto](https://github.com/TommyLemon/APIAuto) 上查看
账号 13000002020 密码 123456
-http://apijson.cn:8000/api
+http://apijson.cn/api
##### 基本原则
1.一定要有相关的应用场景,不能是伪需求,最好举例说明
@@ -35,7 +35,7 @@ POST: 用不上,不处理
@having! 必须性不大,可通过反转内部条件来实现,但如果实现简单、且不影响原来的功能,则可以顺便加上。
#### 新增支持 @column!
-【更新:已提供字段插件 [apijson-column](https://github.com/APIJSON/apijson-column),支持 字段名映射 和 !key 反选字段。】
+20210415 更新:已提供字段插件 [apijson-column](https://github.com/APIJSON/apijson-column),支持 字段名映射 和 !key 反选字段。
这个只在 [apijson-framework](https://github.com/APIJSON/apijson-framework) 支持,需要配置每个接口版本、每张表所拥有的全部字段,然后排除掉 @column! 的。
可新增一个 VersionedColumn 表记录来代替 HashMap 代码配置。
@@ -193,7 +193,6 @@ https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/
### 提高性能
20200205 更新:最近的及次大幅提升性能相关优化及 Release
-[4.8.0【性能】大幅提升提升单表数组查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.8.0)
[4.6.0【性能】大幅提升数组内主表查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.6.0)
[4.4.5【性能】大幅提升增删改的性能](https://github.com/Tencent/APIJSON/releases/tag/4.4.5)
@@ -231,7 +230,7 @@ https://github.com/Tencent/APIJSON/issues/created_by/QiAnXinCodeSafe
##### [APIAuto](https://github.com/TommyLemon/APIAuto) 上统计的 bug
账号 13000002000 密码 123456
-http://apijson.org:8000/auto/
+http://apijson.cn/api
##### 其它发现的 Bug
https://github.com/Tencent/APIJSON/issues?q=is%3Aissue+is%3Aopen+label%3Abug
@@ -239,13 +238,14 @@ https://github.com/Tencent/APIJSON/issues?q=is%3Aissue+is%3Aopen+label%3Abug
+http://apijson.cn/api
-##### 接入 UnitAuto-机器学习自动化单元测试平台,每次启动都自动测试所有可测方法并输出报告
-https://gitee.com/TommyLemon/UnitAuto
+##### 在 UnitAuto-机器学习自动化单元测试平台 上传更多、更全面、更细致的测试用例、动态参数等
+http://apijson.cn/unit
### 完善文档
+20211112 更新:已在官网部署文档 http://apijson.cn/doc/zh
20200205 更新:最近完善及更新了通用文档、上手文档、图文入门文档等,还对首页引导文档加了导航目录
https://github.com/Tencent/APIJSON/blob/master/Navigation.md
From d970eeda31912621bc1c01f7732d7900b825b58a Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 15 Nov 2021 01:11:42 +0800
Subject: [PATCH 026/674] =?UTF-8?q?Update=20=E8=AF=A6=E7=BB=86=E7=9A=84?=
=?UTF-8?q?=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...232\204\350\257\264\346\230\216\346\226\207\346\241\243.md" | 3 +++
1 file changed, 3 insertions(+)
diff --git "a/\350\257\246\347\273\206\347\232\204\350\257\264\346\230\216\346\226\207\346\241\243.md" "b/\350\257\246\347\273\206\347\232\204\350\257\264\346\230\216\346\226\207\346\241\243.md"
index 93279022b..4a25253a3 100644
--- "a/\350\257\246\347\273\206\347\232\204\350\257\264\346\230\216\346\226\207\346\241\243.md"
+++ "b/\350\257\246\347\273\206\347\232\204\350\257\264\346\230\216\346\226\207\346\241\243.md"
@@ -4,6 +4,9 @@
https://search.bilibili.com/all?keyword=APIJSON&from_source=webtop_search&spm_id_from=333.851

+本文档已部署到官网,浏览和检索体验更好
+http://apijson.cn/doc/zh/
+
其它各种官方和第三方文档见首页相关推荐
https://github.com/Tencent/APIJSON#%E7%9B%B8%E5%85%B3%E6%8E%A8%E8%8D%90
From fb26ccfc5af79270865f1a81489d09222c06550f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Thu, 25 Nov 2021 21:20:59 +0800
Subject: [PATCH 027/674] =?UTF-8?q?=E8=B7=AF=E7=BA=BF=E8=A7=84=E5=88=92?=
=?UTF-8?q?=EF=BC=9A=E6=96=B0=E5=A2=9E=204.8.0=20=E5=A4=A7=E5=B9=85?=
=?UTF-8?q?=E6=8F=90=E5=8D=87=E5=8D=95=E8=BE=B9=E6=95=B0=E7=BB=84=E6=9F=A5?=
=?UTF-8?q?=E8=AF=A2=E6=80=A7=E8=83=BD=E7=9A=84=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/releases/tag/4.8.0
---
Roadmap.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Roadmap.md b/Roadmap.md
index 4a479cd2e..dd8fcea9e 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -193,6 +193,7 @@ https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/
### 提高性能
20200205 更新:最近的及次大幅提升性能相关优化及 Release
+[新增支持 ClickHouse、窗口函数 OVER、反引号 `key`、单引号 'value';大幅提升单表数组查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.8.0)
[4.6.0【性能】大幅提升数组内主表查询性能](https://github.com/Tencent/APIJSON/releases/tag/4.6.0)
[4.4.5【性能】大幅提升增删改的性能](https://github.com/Tencent/APIJSON/releases/tag/4.4.5)
@@ -279,7 +280,7 @@ https://github.com/APIJSON/APIJSON#%E7%94%9F%E6%80%81%E9%A1%B9%E7%9B%AE
JavaScript 前端,TypeScript 前端,微信等小程序,
Android 客户端,iOS 客户端,C# 游戏客户端等。
-Java, C#, Node, Python 等后端 Demo 及数据。
+Java, C#, PHP, Node, Python 等后端 Demo 及数据。
https://github.com/APIJSON/APIJSON-Demo
#### 新增扩展
From 12cdd0f5848fe58a56ca6c0799e69cead5553f94 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 27 Nov 2021 02:24:37 +0800
Subject: [PATCH 028/674] Update README.md
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 61e4709bb..282325e22 100644
--- a/README.md
+++ b/README.md
@@ -147,13 +147,13 @@ https://www.bilibili.com/video/BV1yv411p7Y4
### 为什么选择 APIJSON?
-前后端 关于接口的 开发、文档、联调 等 10 个痛点解析
+前后端 关于接口的 开发、文档、联调 等 10 大痛点解析
https://github.com/Tencent/APIJSON/wiki
-* **解决十个痛点** (APIJSON 可帮助用户 提振开发效率、杜绝联调扯皮、规避文档缺陷、节省流量带宽 等)
+* **解决十大痛点** (APIJSON 可帮助用户 提振开发效率、杜绝联调扯皮、规避文档缺陷、节省流量带宽 等)
* **开发提速很大** (CRUD 零代码热更新自动化,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 1W+ Star 在 350W Java 项目中排名前 140,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **社区影响力大** (GitHub 1W+ Star 在 350W Java 项目中排名前 120,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **多样用户案例** (腾讯内部用户包含 互娱、音乐、云与智慧,外部用户包含 500 强上市公司、数千亿资本国企 等)
* **适用场景广泛** (社交聊天、阅读资讯、影音视频、办公学习 等各种 App、网站、公众号、小程序 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
@@ -164,7 +164,7 @@ https://github.com/Tencent/APIJSON/wiki
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的 Demo)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年开源至今已连续维护 4 年,累计 2000+ Commits、70+ Releases,不断更新迭代中...)
+* **多年持续迭代** (自 2016 年开源至今已连续维护 5 年,累计 2000+ Commits、80+ Releases,不断更新迭代中...)
### 常见问题
From 128ca8e76a2d56a849ce63e717c2b8f3932311e7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 27 Nov 2021 02:25:22 +0800
Subject: [PATCH 029/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 282325e22..7704d2e5e 100644
--- a/README.md
+++ b/README.md
@@ -162,7 +162,7 @@ https://github.com/Tencent/APIJSON/wiki
* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防SQL注入等)
* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
-* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的 Demo)
+* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
* **多年持续迭代** (自 2016 年开源至今已连续维护 5 年,累计 2000+ Commits、80+ Releases,不断更新迭代中...)
From 44917295296fa4dd6479ea08f9618ccb84238774 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 1 Dec 2021 01:37:04 +0800
Subject: [PATCH 030/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=207=20=E7=AF=87?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=88=86=E6=9E=90=E7=9B=B8=E5=85=B3=E6=96=87?=
=?UTF-8?q?=E7=AB=A0=EF=BC=8C=E5=9F=BA=E6=9C=AC=E9=83=BD=E6=98=AF=2027=20?=
=?UTF-8?q?=E7=AF=87=E4=B8=AD=E7=9A=84=E5=BC=80=E7=AF=87=EF=BC=8C=E6=84=9F?=
=?UTF-8?q?=E8=B0=A2=203=20=E4=B8=AA=E5=8D=9A=E4=B8=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON/edit/master/README.md#%E7%9B%B8%E5%85%B3%E6%8E%A8%E8%8D%90
---
README.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/README.md b/README.md
index 7704d2e5e..bb6639c11 100644
--- a/README.md
+++ b/README.md
@@ -411,6 +411,21 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[全国行政区划数据抓取与处理](https://www.xlongwei.com/detail/21032616)
[新手搭建 APIJSON 项目指北](https://github.com/jerrylususu/apijson_todo_demo/blob/master/FULLTEXT.md)
+
+[APIJSON(一:综述)](https://blog.csdn.net/qq_50861917/article/details/120556168)
+
+[APIJSON 代码分析(三:demo主体代码)](https://blog.csdn.net/qq_50861917/article/details/120751630)
+
+[APIJSON 代码分析(二)AbstractParser类(解析器)](https://blog.csdn.net/weixin_45767055/article/details/120815927)
+
+[APIJSON 代码分析(四:AbstractObjectParser源码阅读)](https://blog.csdn.net/qq_50861917/article/details/120896381)
+
+[APIJSON 代码分析 AbstractSQLConfig 第二篇](https://blog.csdn.net/csascscascd/article/details/120684889)
+
+[APIJSON 代码分析(六)APIJSON—Verifier检查类](https://blog.csdn.net/weixin_45767055/article/details/121321731)
+
+[APIJSON 代码分析(四)AbstractSQLExecutor—SQL执行器](https://blog.csdn.net/weixin_45767055/article/details/121069887)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
@@ -483,6 +498,7 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧。
+
### 腾讯犀牛鸟开源人才培养计划
https://github.com/Tencent/APIJSON/issues/229
From e114eff51ed1c9c2c9fc2776a3a5462c4a311ed2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 1 Dec 2021 01:38:36 +0800
Subject: [PATCH 031/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=207=20=E7=AF=87?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=88=86=E6=9E=90=E7=9B=B8=E5=85=B3=E6=96=87?=
=?UTF-8?q?=E7=AB=A0=EF=BC=8C=E5=9F=BA=E6=9C=AC=E9=83=BD=E6=98=AF=2027=20?=
=?UTF-8?q?=E7=AF=87=E4=B8=AD=E7=9A=84=E5=BC=80=E7=AF=87=EF=BC=8C=E6=84=9F?=
=?UTF-8?q?=E8=B0=A2=203=20=E4=B8=AA=E5=8D=9A=E4=B8=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#%E7%9B%B8%E5%85%B3%E6%8E%A8%E8%8D%90
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index bb6639c11..376bb2a2f 100644
--- a/README.md
+++ b/README.md
@@ -412,6 +412,7 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[新手搭建 APIJSON 项目指北](https://github.com/jerrylususu/apijson_todo_demo/blob/master/FULLTEXT.md)
+
[APIJSON(一:综述)](https://blog.csdn.net/qq_50861917/article/details/120556168)
[APIJSON 代码分析(三:demo主体代码)](https://blog.csdn.net/qq_50861917/article/details/120751630)
From 7a0c85d961116c191b73fd919335b7e66a90db22 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Thu, 2 Dec 2021 20:20:59 +0800
Subject: [PATCH 032/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E7=AB=A0=20?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8APIJSON=E5=86=99=E4=BD=8E=E4=BB=A3=E7=A0=81Cr?=
=?UTF-8?q?ud=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=84=9F=E8=B0=A2=E5=8D=9A?=
=?UTF-8?q?=E4=B8=BB=E8=B4=A1=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://blog.csdn.net/weixin_42375862/article/details/121654264
位于相关推荐的多篇代码分析博文上方
https://github.com/Tencent/APIJSON#%E7%9B%B8%E5%85%B3%E6%8E%A8%E8%8D%90
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 376bb2a2f..7388173da 100644
--- a/README.md
+++ b/README.md
@@ -412,6 +412,7 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[新手搭建 APIJSON 项目指北](https://github.com/jerrylususu/apijson_todo_demo/blob/master/FULLTEXT.md)
+[使用APIJSON写低代码Crud接口](https://blog.csdn.net/weixin_42375862/article/details/121654264)
[APIJSON(一:综述)](https://blog.csdn.net/qq_50861917/article/details/120556168)
From 6023bc0fba0ad9a6d4101e361d9fc987d4548139 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 5 Dec 2021 01:24:56 +0800
Subject: [PATCH 033/674] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=9F=90=E4=B8=AA?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=80=BC=E4=B8=BA=20null=20=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E4=B8=AD=E6=96=AD=E5=90=8E=E7=BB=AD=E6=AD=A3=E5=B8=B8?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=EF=BC=9B=E8=A7=A3=E5=86=B3=20LEFT/R?=
=?UTF-8?q?IGHT=20JOIN=20=E5=89=AF=E8=A1=A8=E5=85=B3=E8=81=94=E4=B8=BB?=
=?UTF-8?q?=E8=A1=A8=E5=A4=96=E9=94=AE=E7=9A=84=E5=AD=97=E6=AE=B5=E5=8F=96?=
=?UTF-8?q?=E5=88=AB=E5=90=8D=E5=AF=BC=E8=87=B4=20SQL=20=E6=8A=A5=E9=94=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/apijson/orm/AbstractParser.java | 1 +
.../java/apijson/orm/AbstractSQLConfig.java | 45 +++++++++++--------
.../java/apijson/orm/AbstractSQLExecutor.java | 15 ++++---
3 files changed, 35 insertions(+), 26 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 65b79cea9..9be40b0d2 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -432,6 +432,7 @@ public JSONObject parseResponse(JSONObject request) {
requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount());
requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth());
requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime);
+// TODO 放在 msg 中的调试和提示信息应该单独放一个字段,避免 APIAuto 异常分支不显示提示语或太长,以及 DEBUG 和非 DEBUG 模式下提示语不一致 requestObject.put("debug", debugStr);
if (error != null) {
requestObject.put("throw", error.getClass().getName());
requestObject.put("trace", error.getStackTrace());
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 5e5a59917..dc6a33647 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -83,7 +83,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// * 和 / 不能同时出现,防止 /* */ 段注释! # 和 -- 不能出现,防止行注释! ; 不能出现,防止隔断SQL语句!空格不能出现,防止 CRUD,DROP,SHOW TABLES等语句!
private static final Pattern PATTERN_RANGE;
private static final Pattern PATTERN_FUNCTION;
- private static final Pattern PATTERN_STRING;
+ private static final Pattern PATTERN_STRING;
/**
* 表名映射,隐藏真实表名,对安全要求很高的表可以这么做
@@ -1490,9 +1490,8 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
return "(" + s + ")";
case GET:
case GETS:
- boolean isQuery = RequestMethod.isQueryMethod(method); //TODO 这个有啥用?上面应是 getMethod 的值 GET 和 GETS 了。
String joinColumn = "";
- if (isQuery && joinList != null) {
+ if (joinList != null) {
SQLConfig ecfg;
SQLConfig cfg;
String c;
@@ -1501,23 +1500,31 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
if (j.isAppJoin()) {
continue;
}
-
- ecfg = j.getOuterConfig();
- if (ecfg != null && ecfg.getColumn() != null) { //优先级更高
- cfg = ecfg;
- }
- else {
- cfg = j.getJoinConfig();
- }
-
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
-
- c = ((AbstractSQLConfig) cfg).getColumnString(true);
- if (StringUtil.isEmpty(c, true) == false) {
- joinColumn += (first ? "" : ", ") + c;
+
+ if (j.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 + ".*";
first = false;
+ } else {
+ ecfg = j.getOuterConfig();
+ if (ecfg != null && ecfg.getColumn() != null) { //优先级更高
+ cfg = ecfg;
+ }
+ else {
+ cfg = j.getJoinConfig();
+ }
+
+ if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ cfg.setAlias(cfg.getTable());
+ }
+
+ c = ((AbstractSQLConfig) cfg).getColumnString(true);
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinColumn += (first ? "" : ", ") + c;
+ first = false;
+ }
}
inSQLJoin = true;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index c0fce49f7..49703a2a1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -542,7 +542,8 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet r
if (joinList != null) {
for (Join j : joinList) {
childConfig = j.isAppJoin() ? null : j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
-
+
+ // FIXME 副表的 SQL 函数,甚至普通字段都可能从 rsmd.getTableName(columnIndex) 拿到 ""
if (childConfig != null && childTable.equalsIgnoreCase(childConfig.getSQLTable())) {
childConfig.putWhere(j.getKey(), table.get(j.getTargetKey()), true);
@@ -561,13 +562,13 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet r
}
Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, lable, childMap);
- if (value != null) {
- if (finalTable == null) {
- finalTable = new JSONObject(true);
- childMap.put(childSql, finalTable);
- }
- finalTable.put(lable, value);
+ // 必须 put 进去,否则某个字段为 null 可能导致中断后续正常返回值 if (value != null) {
+ if (finalTable == null) {
+ finalTable = new JSONObject(true);
+ childMap.put(childSql, finalTable);
}
+ finalTable.put(lable, value);
+ // }
return table;
}
From 00dae1b6bfa0de09b3e2465f62319c278524f375 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 7 Dec 2021 03:49:49 +0800
Subject: [PATCH 034/674] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20JOIN=20=E5=89=AF?=
=?UTF-8?q?=E8=A1=A8=E5=8C=85=E5=90=AB=20SQL=20=E5=87=BD=E6=95=B0=E6=97=B6?=
=?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=BF=94=E5=9B=9E=20SQL=20=E5=87=BD=E6=95=B0?=
=?UTF-8?q?=E7=9A=84=E6=89=A7=E8=A1=8C=E7=BB=93=E6=9E=9C=E4=BB=A5=E5=8F=8A?=
=?UTF-8?q?=E6=9C=AA=E7=94=A8=E4=B8=8A=20SQL=20=E7=BC=93=E5=AD=98=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E5=86=97=E4=BD=99=20SQL=20=E6=9F=A5=E8=AF=A2=20#341?=
=?UTF-8?q?=EF=BC=9B=E6=8F=90=E5=8D=87=20JOIN=20=E5=B0=81=E8=A3=85?=
=?UTF-8?q?=E7=BB=93=E6=9E=9C=E7=9A=84=E6=80=A7=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLExecutor.java | 183 ++++++++++++++----
1 file changed, 146 insertions(+), 37 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 49703a2a1..72065302f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
import com.alibaba.fastjson.JSON;
@@ -267,17 +268,29 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
//
childMap = new HashMap<>(); //要存到cacheMap
+// Map columnIndexAndJoinMap = new HashMap<>(length);
+ String lastTableName = null; // 默认就是主表 config.getTable();
+ int lastViceTableStart = 0;
+ int lastViceColumnStart = 0;
+ Join lastJoin = null;
+ // TODO String[] columnIndexAndTableMap = new String[length];
// WHERE id = ? AND ... 或 WHERE ... AND id = ? 强制排序 remove 再 put,还是重新 getSQL吧
- boolean hasJoin = config.hasJoin();
- int viceColumnStart = length + 1; //第一个副表字段的index
+ List joinList = config.getJoinList();
+ boolean hasJoin = config.hasJoin() && joinList != null && ! joinList.isEmpty();
+
+ // 直接用数组存取更快 Map columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new HashMap<>(length);
+ Join[] columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new Join[length];
+
+// int viceColumnStart = length + 1; //第一个副表字段的index
while (rs.next()) {
index ++;
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n execute while (rs.next()){ index = " + index + "\n\n");
JSONObject item = new JSONObject(true);
-
+ boolean isMain = true;
+
for (int i = 1; i <= length; i++) {
// if (hasJoin && viceColumnStart > length && config.getSQLTable().equalsIgnoreCase(rsmd.getTableName(i)) == false) {
@@ -285,21 +298,117 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
// }
// bugfix-修复非常规数据库字段,获取表名失败导致输出异常
- if (isExplain == false && hasJoin && viceColumnStart > length) {
- List column = config.getColumn();
- String sqlTable = rsmd.getTableName(i);
- if (config.isClickHouse()&&(sqlTable.startsWith("`")||sqlTable.startsWith("\""))){
- sqlTable = sqlTable.substring(1,sqlTable.length()-1);
+ Join curJoin = columnIndexAndJoinMap == null ? null : columnIndexAndJoinMap[i - 1]; // columnIndexAndJoinMap.get(i);
+
+ // 为什么 isExplain == false 不用判断?因为所有字段都在一张 Query Plan 表
+ if (index <= 0 && hasJoin && ! isExplain) { // && viceColumnStart > length) {
+
+ SQLConfig curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getCacheConfig();
+ List curColumn = curConfig == null ? null : curConfig.getColumn();
+ String sqlTable = curConfig == null ? null : curConfig.getSQLTable();
+
+ List column = config.getColumn();
+ int mainColumnSize = column == null ? 0 : column.size();
+ boolean toFindJoin = mainColumnSize <= 0 || i > mainColumnSize; // 主表就不用找 JOIN 配置
+
+ if (StringUtil.isEmpty(sqlTable , true)) {
+ if (toFindJoin) { // 在主表字段数量内的都归属主表
+ sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
+
+ if (StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
+
+ int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
+ for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
+ Join join = joinList.get(j);
+ SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
+ List c = cfg == null ? null : cfg.getColumn();
+
+ nextViceColumnStart += (
+ c != null && ! c.isEmpty() ? c.size()
+ : (Objects.equals(sqlTable, lastTableName) || sqlTable.equalsIgnoreCase(lastTableName) ? 1 : 0)
+ );
+ if (i < nextViceColumnStart) {
+ sqlTable = cfg.getSQLTable();
+ lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
+
+ curJoin = join;
+ curConfig = cfg;
+ curColumn = c;
+
+ toFindJoin = false;
+ isMain = false;
+ break;
+ }
+ }
+ }
+
+ // 没有 @column,仍然定位不了,用前一个 table 名。FIXME 如果刚好某个表内第一个字段是就是 SQL 函数?
+ if (StringUtil.isEmpty(sqlTable, true)) {
+ sqlTable = lastTableName;
+ toFindJoin = false;
+ }
+ }
}
- if (column != null && column.isEmpty() == false) {
- viceColumnStart = column.size() + 1;
+ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWith("\""))){
+ sqlTable = sqlTable.substring(1, sqlTable.length() - 1);
}
- else if (config.getSQLTable().equalsIgnoreCase(sqlTable) == false) {
- viceColumnStart = i;
+
+ if ((sqlTable == null && lastTableName != null) || (sqlTable != null && ! sqlTable.equalsIgnoreCase(lastTableName))) {
+ lastTableName = sqlTable;
+ lastViceColumnStart = i;
+
+ if (toFindJoin) { // 找到对应的副表 JOIN 配置
+ for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
+ Join join = joinList.get(j);
+ SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
+
+ if (cfg != null && sqlTable != null && sqlTable.equalsIgnoreCase(cfg.getSQLTable())) {
+ lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
+
+ curJoin = join;
+ curConfig = cfg;
+ curColumn = curConfig == null ? null : curConfig.getColumn();
+
+ isMain = false;
+ break;
+ }
+ }
+ }
}
+
+ if (isMain) {
+ lastViceColumnStart ++;
+ }
+ else {
+ if (curJoin == null) {
+ curJoin = lastJoin;
+ }
+ else {
+ lastJoin = curJoin;
+ }
+
+ if (curColumn == null) {
+ curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getJoinConfig();
+ curColumn = curConfig == null ? null : curConfig.getColumn();
+ }
+
+ // 解决后面的表内 SQL 函数被放到之前的空 @column 表
+ if (curColumn == null || curColumn.isEmpty()) {
+ lastViceColumnStart ++;
+ }
+ }
+
+ columnIndexAndJoinMap[i - 1] = curJoin; // columnIndexAndJoinMap.put(i, curJoin); // TODO columnIndexAndTableMap[i] = sqlTable 提速?
+
+// if (column != null && column.isEmpty() == false) {
+// viceColumnStart = column.size() + 1;
+// }
+// else if (config.getSQLTable().equalsIgnoreCase(sqlTable) == false) {
+// viceColumnStart = i;
+// }
}
- item = onPutColumn(config, rs, rsmd, index, item, i, isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
+ item = onPutColumn(config, rs, rsmd, index, item, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
}
resultList = onPutTable(config, rs, rsmd, resultList, index, item);
@@ -391,13 +500,13 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
SQLConfig jc;
SQLConfig cc;
- for (Join j : joinList) {
- if (j.isAppJoin() == false) {
+ for (Join join : joinList) {
+ if (join.isAppJoin() == false) {
Log.i(TAG, "executeAppJoin for (Join j : joinList) >> j.isAppJoin() == false >> continue;");
continue;
}
- cc = j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
+ cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
if (cc == null) {
if (Log.DEBUG) {
throw new NullPointerException("服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据!");
@@ -405,7 +514,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
continue;
}
- jc = j.getJoinConfig();
+ jc = join.getJoinConfig();
//取出 "id@": "@/User/userId" 中所有 userId 的值
List targetValueList = new ArrayList<>();
@@ -414,7 +523,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
for (int i = 0; i < resultList.size(); i++) {
mainTable = resultList.get(i);
- targetValue = mainTable == null ? null : mainTable.get(j.getTargetKey());
+ targetValue = mainTable == null ? null : mainTable.get(join.getTargetKey());
if (targetValue != null && targetValueList.contains(targetValue) == false) {
targetValueList.add(targetValue);
@@ -423,8 +532,8 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
//替换为 "id{}": [userId1, userId2, userId3...]
- jc.putWhere(j.getOriginKey(), null, false); // remove orginKey
- jc.putWhere(j.getKey() + "{}", targetValueList, true); // add orginKey{}
+ jc.putWhere(join.getOriginKey(), null, false); // remove orginKey
+ jc.putWhere(join.getKey() + "{}", targetValueList, true); // add orginKey{}
jc.setMain(true).setPreparedValueList(new ArrayList<>());
@@ -462,7 +571,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
for (int i = 1; i <= length; i++) {
- result = onPutColumn(jc, rs, rsmd, index, result, i, null);
+ result = onPutColumn(jc, rs, rsmd, index, result, i, null, null);
}
//每个 result 都要用新的 SQL 来存 childResultMap = onPutTable(config, rs, rsmd, childResultMap, index, result);
@@ -471,7 +580,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
+ "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
//缓存到 childMap
- cc.putWhere(j.getKey(), result.get(j.getKey()), true);
+ cc.putWhere(join.getKey(), result.get(join.getKey()), true);
cacheSql = cc.getSQL(false);
childMap.put(cacheSql, result);
@@ -512,7 +621,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
* @throws Exception
*/
protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
- , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws Exception {
+ , final int tablePosition, @NotNull JSONObject table, final int columnIndex, Join join, Map childMap) throws Exception {
if (isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap)) {
Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap) >> return table;");
@@ -524,13 +633,13 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet r
// int dotIndex = lable.indexOf(".");
String lable = getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap);
- String childTable = childMap == null ? null : rsmd.getTableName(columnIndex); //dotIndex < 0 ? null : lable.substring(0, dotIndex);
+// String childTable = childMap == null ? null : sqlTableName; // rsmd.getTableName(columnIndex); //dotIndex < 0 ? null : lable.substring(0, dotIndex);
JSONObject finalTable = null;
String childSql = null;
- SQLConfig childConfig = null;
-
- if (childTable == null) {
+
+ SQLConfig childConfig = join == null || join.isSQLJoin() == false ? null : join.getCacheConfig();
+ if (childConfig == null) {
finalTable = table;
}
else {
@@ -538,15 +647,15 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet r
//
- List joinList = config.getJoinList();
- if (joinList != null) {
- for (Join j : joinList) {
- childConfig = j.isAppJoin() ? null : j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
+// List joinList = config.getJoinList();
+// if (joinList != null) {
+// for (Join j : joinList) {
+// childConfig = j.isAppJoin() ? null : j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
// FIXME 副表的 SQL 函数,甚至普通字段都可能从 rsmd.getTableName(columnIndex) 拿到 ""
- if (childConfig != null && childTable.equalsIgnoreCase(childConfig.getSQLTable())) {
+// if (childConfig != null && childTable.equalsIgnoreCase(childConfig.getSQLTable())) {
- childConfig.putWhere(j.getKey(), table.get(j.getTargetKey()), true);
+ childConfig.putWhere(join.getKey(), table.get(join.getTargetKey()), true);
childSql = childConfig.getSQL(false);
if (StringUtil.isEmpty(childSql, true)) {
@@ -554,10 +663,10 @@ protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet r
}
finalTable = (JSONObject) childMap.get(childSql);
- break;
- }
- }
- }
+// break;
+// }
+// }
+// }
}
From c496f016d810d30d96a00b21c3a097f2e118daa5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 20 Dec 2021 22:06:39 +0800
Subject: [PATCH 035/674] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20=20apijson=5Ftemplate=20=E5=92=8C=20api-js?=
=?UTF-8?q?on-demo=EF=BC=8C=E6=84=9F=E8=B0=A2=E4=B8=A4=E4=B8=AA=E4=BD=9C?=
=?UTF-8?q?=E8=80=85=E7=9A=84=E8=B4=A1=E7=8C=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
基于 APIJSON,实现低代码写 CURD 代码,代替传统 ORM 框架,适配 Oracle 事务:
https://gitee.com/hxdwd/api-json-demo
apijson java 模版,使用 gradle 管理依赖和构建应用:
https://github.com/abliger/apijson_template
---
README.md | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 7388173da..af6520919 100644
--- a/README.md
+++ b/README.md
@@ -467,11 +467,17 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[FfApiJson](https://gitee.com/own_3_0/ff-api-json) 用 JSON 格式直接生成 SQL,借鉴 APIJSON 支持多数据源
[APIJSON-ToDo-Demo](https://github.com/jerrylususu/apijson_todo_demo) 一个简单的 todo 示例项目,精简数据,简化上手流程,带自定义鉴权逻辑
-
+
[apijson-learn](https://github.com/rainboy-learn/apijson-learn) APIJSON 学习笔记和源码解析
[apijson-practice](https://github.com/vcoolwind/apijson-practice) BAT 技术专家开源的 APIJSON 参数校验注解 Library 及相关 Demo
+[apijson-db2](https://github.com/andream7/apijson-db2) APIJSON 接入 IBM 数据库 DB2 的 Demo
+
+[APIJSONDemo](https://github.com/qiujunlin/APIJSONDemo) APIJSON 接入 ClickHouse 的 Demo
+
+[APIJSONDemo_ClickHouse](https://github.com/chenyanlann/APIJSONDemo_ClickHouse) APIJSON + SpringBoot 连接 ClickHouse 使用的 Demo
+
[apijson-sample](https://gitee.com/greyzeng/apijson-sample) APIJSON 简单使用 Demo 及教程
[apijson-examples](https://gitee.com/drone/apijson-examples) APIJSON 的前端、业务后端、管理后端 Demo
@@ -480,16 +486,12 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[SpringServer1.2-APIJSON](https://github.com/Airforce-1/SpringServer1.2-APIJSON) 智慧党建服务器端,提供 上传 和 下载 文件的接口
-[apijson-examples](https://gitee.com/drone/apijson-examples) APIJSON 的前端、业务后端、管理后端 Demo
+[apijson_template](https://github.com/abliger/apijson_template) apijson java 模版,使用 gradle 管理依赖和构建应用
+
+[api-json-demo](https://gitee.com/hxdwd/api-json-demo) 基于 APIJSON,实现低代码写 CURD 代码,代替传统 ORM 框架,适配 Oracle 事务
[ApiJsonByJFinal](https://gitee.com/zhiyuexin/ApiJsonByJFinal) 整合 APIJSON 和 JFinal 的 Demo
-[apijson-db2](https://github.com/andream7/apijson-db2) APIJSON 接入 IBM 数据库 DB2 的 Demo
-
-[APIJSONDemo](https://github.com/qiujunlin/APIJSONDemo) APIJSON 接入 ClickHouse 的 Demo
-
-[APIJSONDemo_ClickHouse](https://github.com/chenyanlann/APIJSONDemo_ClickHouse) APIJSON + SpringBoot 连接 ClickHouse 使用的 Demo
-
[apijson-builder](https://github.com/pengxianggui/apijson-builder) 一个方便为 APIJSON 构建 RESTful 请求的 JavaScript 库
[AbsGrade](https://github.com/APIJSON/AbsGrade) 列表级联算法,支持微信朋友圈单层评论、QQ空间双层评论、百度网盘多层(无限层)文件夹等
From ed05ae37afdc6274bb7763b2792bf1311a16b662 Mon Sep 17 00:00:00 2001
From: chenyanlann <32511cyl@gmail.com>
Date: Fri, 31 Dec 2021 17:53:06 +0800
Subject: [PATCH 036/674] Add:ORM's support for Hive
---
.../main/java/apijson/orm/AbstractSQLConfig.java | 11 +++++++++++
.../main/java/apijson/orm/AbstractSQLExecutor.java | 13 ++++++++++++-
APIJSONORM/src/main/java/apijson/orm/SQLConfig.java | 2 ++
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index dc6a33647..57deaa44e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -126,6 +126,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
DATABASE_LIST.add(DATABASE_ORACLE);
DATABASE_LIST.add(DATABASE_DB2);
DATABASE_LIST.add(DATABASE_CLICKHOUSE);
+ DATABASE_LIST.add(DATABASE_HIVE);
RAW_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况
@@ -903,6 +904,13 @@ public boolean isClickHouse() {
public static boolean isClickHouse(String db) {
return DATABASE_CLICKHOUSE.equals(db);
}
+ @Override
+ public boolean isHive() {
+ return isHive(getSQLDatabase());
+ }
+ public static boolean isHive(String db) {
+ return DATABASE_HIVE.equals(db);
+ }
@Override
public String getQuote() {
@@ -2727,6 +2735,9 @@ public String getRegExpString(String key, String value, boolean ignoreCase) {
if (isClickHouse()) {
return "match(" + (ignoreCase ? "lower(" : "") + getKey(key) + (ignoreCase ? ")" : "") + ", " + (ignoreCase ? "lower(" : "") + getValue(value) + (ignoreCase ? ")" : "") + ")";
}
+ if (isHive()) {
+ return (ignoreCase ? "lower(" : "") + getKey(key) + (ignoreCase ? ")" : "") + " REGEXP " + (ignoreCase ? "lower(" : "") + getValue(value) + (ignoreCase ? ")" : "");
+ }
return getKey(key) + " REGEXP " + (ignoreCase ? "" : "BINARY ") + getValue(value);
}
//~ regexp >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 72065302f..1f1ddcb1e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -27,6 +27,7 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.regex.Pattern;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@@ -36,6 +37,7 @@
import apijson.NotNull;
import apijson.RequestMethod;
import apijson.StringUtil;
+import apijson.orm.AbstractSQLConfig;
/**executor for query(read) or update(write) MySQL database
* @author Lemon
@@ -718,7 +720,15 @@ protected List onPutTable(@NotNull SQLConfig config, @NotNull Result
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 {
- return rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ String key = rsmd.getColumnLabel(columnIndex);// dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ if (config.isHive()) {
+ String table_name = config.getTable();
+ if(AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ String pattern = "^" + table_name + "\\." + "[a-zA-Z]+$";
+ boolean isMatch = Pattern.matches(pattern, key);
+ if(isMatch) key = key.split("\\.")[1];
+ }
+ return key;
}
protected Object getValue(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
@@ -974,6 +984,7 @@ public ResultSet executeQuery(@NotNull SQLConfig config) throws Exception {
public int executeUpdate(@NotNull SQLConfig config) throws Exception {
PreparedStatement s = getStatement(config);
int count = s.executeUpdate(); //PreparedStatement 不用传 SQL
+ if (config.isHive() && count==0) count = 1;
if (config.getMethod() == RequestMethod.POST && config.getId() == null) { //自增id
ResultSet rs = s.getGeneratedKeys();
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index ab8e2d254..83eddb301 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -22,6 +22,7 @@ public interface SQLConfig {
String DATABASE_ORACLE = "ORACLE";
String DATABASE_DB2 = "DB2";
String DATABASE_CLICKHOUSE = "CLICKHOUSE";
+ String DATABASE_HIVE = "HIVE";
String SCHEMA_INFORMATION = "information_schema"; //MySQL, PostgreSQL, SQL Server 都有的系统模式
String SCHEMA_SYS = "sys"; //SQL Server 系统模式
@@ -38,6 +39,7 @@ public interface SQLConfig {
boolean isOracle();
boolean isDb2();
boolean isClickHouse();
+ boolean isHive();
//暂时只兼容以上 5 种
// boolean isSQL();
// boolean isTSQL();
From a8bad69e007e76ca5bc85634053363fc010c58c9 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 1 Jan 2022 04:16:18 +0800
Subject: [PATCH 037/674] =?UTF-8?q?SQL=20JOIN=EF=BC=9A=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E5=89=AF=E8=A1=A8=E9=99=A4=E4=BA=86=E5=BC=95=E7=94=A8=E8=B5=8B?=
=?UTF-8?q?=E5=80=BC=E9=94=AE=E5=80=BC=E5=AF=B9=E8=BF=98=E6=9C=89=E5=85=B6?=
=?UTF-8?q?=E5=AE=83=E6=9D=A1=E4=BB=B6=E9=94=AE=E5=80=BC=E5=AF=B9=E6=97=B6?=
=?UTF-8?q?=E4=B8=8D=E8=83=BD=E5=91=BD=E4=B8=AD=E7=BC=93=E5=AD=98=EF=BC=8C?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=20=E4=B8=80=E5=AF=B9=E5=A4=9A=E3=80=81?=
=?UTF-8?q?=E5=A4=9A=E5=AF=B9=E5=A4=9A=E5=89=AF=E8=A1=A8=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?=E9=87=8D=E5=A4=8D=20=E4=BB=A5=E5=8F=8A=20=E4=B8=80=E5=AF=B9?=
=?UTF-8?q?=E4=B8=80=E3=80=81=E5=A4=9A=E5=AF=B9=E4=B8=80=20=E6=9F=A5?=
=?UTF-8?q?=E8=AF=A2=E6=80=A7=E8=83=BD=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 | 41 +++++++++++++++----
1 file changed, 33 insertions(+), 8 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index dc6a33647..6798fc0ad 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2197,18 +2197,42 @@ else if (prior && andList.isEmpty() == false) {
String userIdKey = getUserIdKey();
String userIdInKey = userIdKey + "{}";
- if (andList.contains(idKey)) {
- i ++;
+ int lastIndex;
+ if (key.equals(idKey)) {
+ lastIndex = -1;
}
- if (andList.contains(idInKey)) {
- i ++;
+ else if (key.equals(idInKey)) {
+ lastIndex = andList.lastIndexOf(idKey);
}
- if (andList.contains(userIdKey)) {
- i ++;
+ else if (key.equals(userIdKey)) {
+ lastIndex = andList.lastIndexOf(idInKey);
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(idKey);
+ }
}
- if (andList.contains(userIdInKey)) {
- i ++;
+ else if (key.equals(userIdInKey)) {
+ lastIndex = andList.lastIndexOf(userIdKey);
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(idInKey);
+ }
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(idKey);
+ }
+ }
+ else {
+ lastIndex = andList.lastIndexOf(userIdInKey);
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(userIdKey);
+ }
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(idInKey);
+ }
+ if (lastIndex < 0) {
+ lastIndex = andList.lastIndexOf(idKey);
+ }
}
+
+ i = lastIndex + 1;
}
if (prior) {
@@ -2219,6 +2243,7 @@ else if (prior && andList.isEmpty() == false) {
}
combine.put("&", andList);
}
+
return this;
}
From 61883bc24b1dcc3abe0ba60c57e6c057fae8e552 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 1 Jan 2022 04:18:29 +0800
Subject: [PATCH 038/674] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Response=20JSON=20?=
=?UTF-8?q?=E4=B8=AD=E7=9A=84=20debug:info|help,=20trace:stack=20=E7=AD=89?=
=?UTF-8?q?=E8=B0=83=E8=AF=95=E5=AD=97=E6=AE=B5=E5=8F=8A=E7=9B=B8=E5=85=B3?=
=?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../apijson/orm/AbstractObjectParser.java | 17 +-
.../main/java/apijson/orm/AbstractParser.java | 192 ++++++++++++------
2 files changed, 145 insertions(+), 64 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 453db9c5f..590e016dc 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -288,7 +288,10 @@ else if (method == PUT && value instanceof JSONArray && (whereList == null || wh
try {
String db = sqlConfig.getDatabase();
if (db == null) {
- if (sqlConfig.isPostgreSQL()) {
+ if (sqlConfig.isMySQL()) {
+ db = SQLConfig.DATABASE_MYSQL;
+ }
+ else if (sqlConfig.isPostgreSQL()) {
db = SQLConfig.DATABASE_POSTGRESQL;
}
else if (sqlConfig.isSQLServer()) {
@@ -304,18 +307,18 @@ else if (sqlConfig.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE;
}
else {
- db = SQLConfig.DATABASE_MYSQL;
+ db = AbstractSQLConfig.DEFAULT_DATABASE;
}
}
Class extends Exception> clazz = e.getClass();
e = clazz.getConstructor(String.class).newInstance(
e.getMessage()
- + " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n**环境信息** "
- + "\n系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
- + "\n数据库: " + db + " " + sqlConfig.getDBVersion()
- + "\nJDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
- + "\nAPIJSON: " + Log.VERSION
+ + " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n **环境信息** "
+ + " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ + " \n 数据库: " + db + " " + sqlConfig.getDBVersion()
+ + " \n JDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
+ + " \n APIJSON: " + Log.VERSION
);
} catch (Throwable e2) {}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 9be40b0d2..b9c37a1a6 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -94,6 +94,16 @@ public AbstractParser(RequestMethod method, boolean needVerify) {
setMethod(method);
setNeedVerify(needVerify);
}
+
+ protected boolean isRoot = true;
+ public boolean isRoot() {
+ return isRoot;
+ }
+ public AbstractParser setRoot(boolean isRoot) {
+ this.isRoot = isRoot;
+ return this;
+ }
+
@NotNull
protected Visitor visitor;
@@ -336,7 +346,7 @@ public JSONObject parseResponse(String request) {
try {
requestObject = parseRequest(request);
} catch (Exception e) {
- return newErrorResult(e);
+ return newErrorResult(e, isRoot);
}
return parseResponse(requestObject);
@@ -368,7 +378,7 @@ public JSONObject parseResponse(JSONObject request) {
onVerifyContent();
}
} catch (Exception e) {
- return extendErrorResult(requestObject, e, requestMethod, getRequestURL());
+ return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
}
}
@@ -378,7 +388,7 @@ public JSONObject parseResponse(JSONObject request) {
setGlobleRole(requestObject.getString(JSONRequest.KEY_ROLE));
requestObject.remove(JSONRequest.KEY_ROLE);
} catch (Exception e) {
- return extendErrorResult(requestObject, e, requestMethod, getRequestURL());
+ return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
}
}
@@ -397,7 +407,7 @@ public JSONObject parseResponse(JSONObject request) {
requestObject.remove(JSONRequest.KEY_EXPLAIN);
requestObject.remove(JSONRequest.KEY_CACHE);
} catch (Exception e) {
- return extendErrorResult(requestObject, e, requestMethod, getRequestURL());
+ return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
}
final String requestString = JSON.toJSONString(request);//request传进去解析后已经变了
@@ -421,7 +431,7 @@ public JSONObject parseResponse(JSONObject request) {
onRollback();
}
- requestObject = error == null ? extendSuccessResult(requestObject) : extendErrorResult(requestObject, error, requestMethod, getRequestURL());
+ requestObject = error == null ? extendSuccessResult(requestObject, isRoot) : extendErrorResult(requestObject, error, requestMethod, getRequestURL(), isRoot);
JSONObject res = (globleFormat != null && globleFormat) && JSONResponse.isSuccess(requestObject) ? new JSONResponse(requestObject) : requestObject;
@@ -432,26 +442,26 @@ public JSONObject parseResponse(JSONObject request) {
requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount());
requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth());
requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime);
-// TODO 放在 msg 中的调试和提示信息应该单独放一个字段,避免 APIAuto 异常分支不显示提示语或太长,以及 DEBUG 和非 DEBUG 模式下提示语不一致 requestObject.put("debug", debugStr);
+
if (error != null) {
- requestObject.put("throw", error.getClass().getName());
- requestObject.put("trace", error.getStackTrace());
+ requestObject.put("trace:throw", error.getClass().getName());
+ requestObject.put("trace:stack", error.getStackTrace());
}
}
onClose();
//CS304 Issue link: https://github.com/Tencent/APIJSON/issues/232
- if (IS_PRINT_REQUEST_STRING_LOG||Log.DEBUG||error != null) {
- Log.sl("\n\n\n",'<',"");
- Log.fd(TAG , requestMethod + "/parseResponse request = \n" + requestString + "\n\n");
+ if (IS_PRINT_REQUEST_STRING_LOG || Log.DEBUG || error != null) {
+ Log.sl("\n\n\n", '<', "");
+ Log.fd(TAG, requestMethod + "/parseResponse request = \n" + requestString + "\n\n");
}
- if (IS_PRINT_BIG_LOG||Log.DEBUG||error != null) { // 日志仅存服务器,所以不太敏感,而且这些日志虽然量大但非常重要,对排查 bug 很关键
- Log.fd(TAG,requestMethod + "/parseResponse return response = \n" + JSON.toJSONString(requestObject) + "\n\n");
+ if (IS_PRINT_BIG_LOG || Log.DEBUG || error != null) { // 日志仅存服务器,所以不太敏感,而且这些日志虽然量大但非常重要,对排查 bug 很关键
+ Log.fd(TAG, requestMethod + "/parseResponse return response = \n" + JSON.toJSONString(requestObject) + "\n\n");
}
- if (IS_PRINT_REQUEST_ENDTIME_LOG||Log.DEBUG||error != null) {
- Log.fd(TAG , requestMethod + "/parseResponse endTime = " + endTime + "; duration = " + duration);
- Log.sl("",'>',"\n\n\n");
+ if (IS_PRINT_REQUEST_ENDTIME_LOG || Log.DEBUG || error != null) {
+ Log.fd(TAG, requestMethod + "/parseResponse endTime = " + endTime + "; duration = " + duration);
+ Log.sl("", '>', "\n\n\n");
}
return res;
}
@@ -611,21 +621,44 @@ else if (target.containsKey(key) == false) {
* @return
*/
public static JSONObject newResult(int code, String msg) {
- return extendResult(null, code, msg);
+ return newResult(code, msg, false);
}
+ /**新建带状态内容的JSONObject
+ * @param code
+ * @param msg
+ * @param isRoot
+ * @return
+ */
+ public static JSONObject newResult(int code, String msg, boolean isRoot) {
+ return extendResult(null, code, msg, isRoot);
+ }
+
/**添加JSONObject的状态内容,一般用于错误提示结果
* @param object
* @param code
* @param msg
* @return
*/
- public static JSONObject extendResult(JSONObject object, int code, String msg) {
+ public static JSONObject extendResult(JSONObject object, int code, String msg, boolean isRoot) {
+ int index = Log.DEBUG == false || isRoot == false || msg == null ? -1 : msg.lastIndexOf(Log.KEY_SYSTEM_INFO_DIVIDER);
+ String debug = Log.DEBUG == false || isRoot == false ? null : (index >= 0 ? msg.substring(index + Log.KEY_SYSTEM_INFO_DIVIDER.length()).trim()
+ : " \n **环境信息** "
+ + " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ + " \n 数据库: DEFAULT_DATABASE = " + AbstractSQLConfig.DEFAULT_DATABASE
+ + " \n JDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
+ + " \n APIJSON: " + Log.VERSION
+ + " \n | \n 常见问题:https://github.com/Tencent/APIJSON/issues/36"
+ + " \n 通用文档:https://github.com/Tencent/APIJSON/blob/master/Document.md"
+ + " \n 视频教程:https://search.bilibili.com/all?keyword=APIJSON");
+
+ msg = index >= 0 ? msg.substring(0, index) : msg;
+
if (object == null) {
object = new JSONObject(true);
}
- boolean isOk = JSONResponse.isSuccess(code);
+
if (object.containsKey(JSONResponse.KEY_OK) == false) {
- object.put(JSONResponse.KEY_OK, isOk);
+ object.put(JSONResponse.KEY_OK, JSONResponse.isSuccess(code));
}
if (object.containsKey(JSONResponse.KEY_CODE) == false) {
object.put(JSONResponse.KEY_CODE, code);
@@ -635,8 +668,12 @@ public static JSONObject extendResult(JSONObject object, int code, String msg) {
if (m.isEmpty() == false) {
msg = m + " ;\n " + StringUtil.getString(msg);
}
-
+
object.put(JSONResponse.KEY_MSG, msg);
+ if (debug != null) {
+ object.put("debug:info|help", debug);
+ }
+
return object;
}
@@ -646,37 +683,64 @@ public static JSONObject extendResult(JSONObject object, int code, String msg) {
* @return
*/
public static JSONObject extendSuccessResult(JSONObject object) {
- return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED);
+ return extendSuccessResult(object, false);
+ }
+ /**添加请求成功的状态内容
+ * @param object
+ * @param isRoot
+ * @return
+ */
+ public static JSONObject extendSuccessResult(JSONObject object, boolean isRoot) {
+ return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, isRoot);
}
/**获取请求成功的状态内容
* @return
*/
public static JSONObject newSuccessResult() {
- return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED);
+ return newSuccessResult(false);
+ }
+ /**获取请求成功的状态内容
+ * @param isRoot
+ * @return
+ */
+ public static JSONObject newSuccessResult(boolean isRoot) {
+ return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, isRoot);
}
+
/**添加请求成功的状态内容
* @param object
+ * @param e
+ * @param isRoot
* @return
*/
public static JSONObject extendErrorResult(JSONObject object, Exception e) {
- return extendErrorResult(object, e, null, null);
+ return extendErrorResult(object, e, false);
+ }
+ /**添加请求成功的状态内容
+ * @param object
+ * @param e
+ * @param isRoot
+ * @return
+ */
+ public static JSONObject extendErrorResult(JSONObject object, Exception e, boolean isRoot) {
+ return extendErrorResult(object, e, null, null, isRoot);
}
/**添加请求成功的状态内容
* @param object
* @return
*/
- public static JSONObject extendErrorResult(JSONObject object, Exception e, RequestMethod requestMethod, String url) {
+ public static JSONObject extendErrorResult(JSONObject object, Exception e, RequestMethod requestMethod, String url, boolean isRoot) {
String msg = e.getMessage();
- if (Log.DEBUG) {
+ if (Log.DEBUG && isRoot) {
try {
int index = msg.lastIndexOf(Log.KEY_SYSTEM_INFO_DIVIDER);
String info = index >= 0 ? msg.substring(index + Log.KEY_SYSTEM_INFO_DIVIDER.length()).trim()
- : "\n**环境信息** "
- + "\n系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
- + "\n数据库: "
- + "\nJDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
- + "\nAPIJSON: " + Log.VERSION;
+ : " \n **环境信息** "
+ + " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ + " \n 数据库: "
+ + " \n JDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
+ + " \n APIJSON: " + Log.VERSION;
msg = index < 0 ? msg : msg.substring(0, index).trim();
String encodedMsg = URLEncoder.encode(msg, "UTF-8");
@@ -711,30 +775,30 @@ public static JSONObject extendErrorResult(JSONObject object, Exception e, Reque
boolean isSQLException = e instanceof SQLException; // SQL 报错一般都是通用问题,优先搜索引擎
String apiatuoAndGitHubLink = "\n【APIAuto】: \n http://apijson.cn/api?type=JSON&url=" + URLEncoder.encode(url, "UTF-8") + "&json=" + req
+ " \n\n【GitHub】: \n https://www.google.com/search?q=site%3Agithub.com%2FTencent%2FAPIJSON+++" + encodedMsg;
-
- msg += " \n\n\n浏览器打开以下链接查看解答"
+
+ msg += Log.KEY_SYSTEM_INFO_DIVIDER + " \n | \n 浏览器打开以下链接查看解答"
+ (isSQLException ? "" : apiatuoAndGitHubLink)
// GitHub Issue 搜索貌似是精准包含,不易找到答案 + " \n\nGitHub: \n https://github.com/Tencent/APIJSON/issues?q=is%3Aissue+" + encodedMsg
+ " \n\n【Google】:\n https://www.google.com/search?q=" + encodedMsg
+ " \n\n【百度】:\n https://www.baidu.com/s?ie=UTF-8&wd=" + encodedMsg
+ (isSQLException ? apiatuoAndGitHubLink : "")
+ " \n\n都没找到答案?打开这个链接 \n https://github.com/Tencent/APIJSON/issues/new?assignees=&labels=&template=--bug.md "
- + "\n然后提交问题,推荐用以下模板修改,注意要换行保持清晰可读。"
- + "\n【标题】:" + msg
- + "\n【内容】:" + info + "\n\n**问题描述**\n" + msg
- + "\n\n"
- + "\n\nPOST " + url
- + "\n请求 Request JSON:\n ```js"
- + "\n 请填写,例如 { \"Users\":{} }"
- + "\n```"
- + "\n\n返回结果 Response JSON:\n ```js"
- + "\n 请填写,例如 { \"Users\": {}, \"code\": 401, \"msg\": \"Users 不允许 UNKNOWN 用户的 GET 请求!\" }"
- + "\n```";
+ + " \n然后提交问题,推荐用以下模板修改,注意要换行保持清晰可读。"
+ + " \n【标题】:" + msg
+ + " \n【内容】:" + info + "\n\n**问题描述**\n" + msg
+ + " \n\n"
+ + " \n\nPOST " + url
+ + " \n请求 Request JSON:\n ```js"
+ + " \n 请填写,例如 { \"Users\":{} }"
+ + " \n```"
+ + " \n\n返回结果 Response JSON:\n ```js"
+ + " \n 请填写,例如 { \"Users\": {}, \"code\": 401, \"msg\": \"Users 不允许 UNKNOWN 用户的 GET 请求!\" }"
+ + " \n```";
} catch (Throwable e2) {}
}
- JSONObject error = newErrorResult(e);
- return extendResult(object, error.getIntValue(JSONResponse.KEY_CODE), msg);
+ JSONObject error = newErrorResult(e, isRoot);
+ return extendResult(object, error.getIntValue(JSONResponse.KEY_CODE), msg, isRoot);
}
/**新建错误状态内容
@@ -742,6 +806,14 @@ public static JSONObject extendErrorResult(JSONObject object, Exception e, Reque
* @return
*/
public static JSONObject newErrorResult(Exception e) {
+ return newErrorResult(e, false);
+ }
+ /**新建错误状态内容
+ * @param e
+ * @param isRoot
+ * @return
+ */
+ public static JSONObject newErrorResult(Exception e, boolean isRoot) {
if (e != null) {
e.printStackTrace();
@@ -786,10 +858,10 @@ else if (e instanceof NullPointerException) {
code = JSONResponse.CODE_SERVER_ERROR;
}
- return newResult(code, e.getMessage());
+ return newResult(code, e.getMessage(), isRoot);
}
- return newResult(JSONResponse.CODE_SERVER_ERROR, JSONResponse.MSG_SERVER_ERROR);
+ return newResult(JSONResponse.CODE_SERVER_ERROR, JSONResponse.MSG_SERVER_ERROR, isRoot);
}
@@ -1261,7 +1333,6 @@ else if (join != null){
}
-
List joinList = new ArrayList<>();
@@ -1375,6 +1446,9 @@ else if (join != null){
tableObj = newTableObj;
request.put(tableKey, tableObj);
+
+// tableObj.clear();
+// tableObj.putAll(newTableObj);
}
// 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致,生成的 SQL 也就一致 >>>>>>>>>
@@ -1763,7 +1837,8 @@ public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Except
result.put(KEY_EXPLAIN, explainResult);
result.putAll(res);
}
- }else{//如果是更新请求,不执行explain,但可以返回sql
+ }
+ else {//如果是更新请求,不执行explain,但可以返回sql
result = new JSONObject(true);
result.put(KEY_SQL, config.getSQL(false));
result.putAll(res);
@@ -1780,7 +1855,10 @@ public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Except
try {
String db = config.getDatabase();
if (db == null) {
- if (config.isPostgreSQL()) {
+ if (config.isMySQL()) {
+ db = SQLConfig.DATABASE_MYSQL;
+ }
+ else if (config.isPostgreSQL()) {
db = SQLConfig.DATABASE_POSTGRESQL;
}
else if (config.isSQLServer()) {
@@ -1796,18 +1874,18 @@ else if (config.isClickHouse()) {
db = SQLConfig.DATABASE_CLICKHOUSE;
}
else {
- db = SQLConfig.DATABASE_MYSQL;
+ db = AbstractSQLConfig.DEFAULT_DATABASE;
}
}
Class extends Exception> clazz = e.getClass();
e = clazz.getConstructor(String.class).newInstance(
e.getMessage()
- + " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n**环境信息** "
- + "\n系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
- + "\n数据库: " + db + " " + config.getDBVersion()
- + "\nJDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
- + "\nAPIJSON: " + Log.VERSION
+ + " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n **环境信息** "
+ + " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ + " \n 数据库: " + db + " " + config.getDBVersion()
+ + " \n JDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
+ + " \n APIJSON: " + Log.VERSION
);
} catch (Throwable e2) {}
}
From 5d59b3529274d1542bdef90dc78e888c7af8d0f1 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 3 Jan 2022 00:48:35 +0800
Subject: [PATCH 039/674] =?UTF-8?q?=E4=BC=98=E5=8C=96=20JOIN=20=E5=89=AF?=
=?UTF-8?q?=E8=A1=A8=E8=A7=A3=E6=9E=90=E7=BB=93=E6=9E=9C=E9=9B=86=20Result?=
=?UTF-8?q?Set=20=E7=9A=84=E6=80=A7=E8=83=BD=EF=BC=88=E5=87=8F=E5=B0=91?=
=?UTF-8?q?=E5=90=8C=E5=89=AF=E8=A1=A8=E5=AD=97=E6=AE=B5=E7=9A=84=E9=87=8D?=
=?UTF-8?q?=E5=A4=8D=E9=80=BB=E8=BE=91=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/StringUtil.java | 15 ++
.../main/java/apijson/orm/AbstractParser.java | 5 +-
.../java/apijson/orm/AbstractSQLExecutor.java | 148 ++++++++----------
.../src/main/java/apijson/orm/Join.java | 7 +-
4 files changed, 90 insertions(+), 85 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index bd3b87f7b..0d473a6b0 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -8,6 +8,7 @@
import java.io.File;
import java.math.BigDecimal;
import java.text.DecimalFormat;
+import java.util.Objects;
import java.util.regex.Pattern;
/**通用字符串(String)相关类,为null时返回""
@@ -891,4 +892,18 @@ public static String concat(String left, String right, String split, boolean tri
//校正(自动补全等)字符串>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+ public static boolean equals(Object s1, Object s2) {
+ return Objects.equals(s1, s2);
+ }
+ public static boolean equalsIgnoreCase(String s1, String s2) {
+ if (s1 == s2) {
+ return true;
+ }
+ if (s1 == null || s2 == null) {
+ return false;
+ }
+ return s1.equalsIgnoreCase(s2);
+ }
+
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index b9c37a1a6..1bc1bacfb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1851,7 +1851,8 @@ public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Except
return result;
}
catch (Exception e) {
- if (Log.DEBUG && e.getMessage().contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
+ String msg = e.getMessage();
+ if (Log.DEBUG && msg != null && msg.contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
try {
String db = config.getDatabase();
if (db == null) {
@@ -1880,7 +1881,7 @@ else if (config.isClickHouse()) {
Class extends Exception> clazz = e.getClass();
e = clazz.getConstructor(String.class).newInstance(
- e.getMessage()
+ msg
+ " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n **环境信息** "
+ " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ " \n 数据库: " + db + " " + config.getDBVersion()
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 72065302f..8400e429e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -78,7 +78,7 @@ public int getExecutedSQLCount() {
* @param isStatic
*/
@Override
- public synchronized void putCache(String sql, List list, int type) {
+ public void putCache(String sql, List list, int type) {
if (sql == null || list == null) { //空map有效,说明查询过sql了 || list.isEmpty()) {
Log.i(TAG, "saveList sql == null || list == null >> return;");
return;
@@ -90,7 +90,7 @@ public synchronized void putCache(String sql, List list, int type) {
* @param isStatic
*/
@Override
- public synchronized void removeCache(String sql, int type) {
+ public void removeCache(String sql, int type) {
if (sql == null) {
Log.i(TAG, "removeList sql == null >> return;");
return;
@@ -115,7 +115,8 @@ public JSONObject getCacheItem(String sql, int position, int type) {
//只要map不为null,则如果 list.get(position) == null,则返回 {} ,避免再次SQL查询
if (list == null) {
return null;
- }
+ }
+
JSONObject result = position >= list.size() ? null : list.get(position);
return result != null ? result : new JSONObject();
}
@@ -124,20 +125,14 @@ public JSONObject getCacheItem(String sql, int position, int type) {
@Override
public ResultSet executeQuery(@NotNull Statement statement, String sql) throws Exception {
- executedSQLCount ++;
-
return statement.executeQuery(sql);
}
@Override
public int executeUpdate(@NotNull Statement statement, String sql) throws Exception {
- executedSQLCount ++;
-
return statement.executeUpdate(sql);
}
@Override
public ResultSet execute(@NotNull Statement statement, String sql) throws Exception {
- executedSQLCount ++;
-
statement.execute(sql);
return statement.getResultSet();
}
@@ -186,6 +181,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
try {
if (unknowType) {
Statement statement = getStatement(config);
+
+ if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
+ executedSQLCount ++;
+ }
rs = execute(statement, sql);
result = new JSONObject(true);
@@ -199,8 +198,9 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
case POST:
case PUT:
case DELETE:
- executedSQLCount ++;
-
+ if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
+ executedSQLCount ++;
+ }
int updateCount = executeUpdate(config);
if (updateCount <= 0) {
throw new IllegalAccessException("没权限访问或对象不存在!"); // NotExistException 会被 catch 转为成功状态
@@ -222,7 +222,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
case GETS:
case HEAD:
case HEADS:
- result = isHead ? null : getCacheItem(sql, position, config.getCache());
+ result = isHead || isExplain ? null : getCacheItem(sql, position, config.getCache());
Log.i(TAG, ">>> execute result = getCache('" + sql + "', " + position + ") = " + result);
if (result != null) {
cachedSQLCount ++;
@@ -231,11 +231,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
return result;
}
- rs = executeQuery(config); //FIXME SQL Server 是一次返回两个结果集,包括查询结果和执行计划,需要 moreResults
-
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
executedSQLCount ++;
}
+ rs = executeQuery(config); //FIXME SQL Server 是一次返回两个结果集,包括查询结果和执行计划,需要 moreResults
break;
default://OPTIONS, TRACE等
@@ -270,6 +269,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
childMap = new HashMap<>(); //要存到cacheMap
// Map columnIndexAndJoinMap = new HashMap<>(length);
String lastTableName = null; // 默认就是主表 config.getTable();
+ String lastAliasName = null; // 默认就是主表 config.getAlias();
int lastViceTableStart = 0;
int lastViceColumnStart = 0;
Join lastJoin = null;
@@ -289,6 +289,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n execute while (rs.next()){ index = " + index + "\n\n");
JSONObject item = new JSONObject(true);
+ JSONObject curItem = item;
boolean isMain = true;
for (int i = 1; i <= length; i++) {
@@ -301,17 +302,19 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
Join curJoin = columnIndexAndJoinMap == null ? null : columnIndexAndJoinMap[i - 1]; // columnIndexAndJoinMap.get(i);
// 为什么 isExplain == false 不用判断?因为所有字段都在一张 Query Plan 表
- if (index <= 0 && hasJoin && ! isExplain) { // && viceColumnStart > length) {
+ if (index <= 0 && columnIndexAndJoinMap != null) { // && viceColumnStart > length) {
SQLConfig curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getCacheConfig();
List curColumn = curConfig == null ? null : curConfig.getColumn();
String sqlTable = curConfig == null ? null : curConfig.getSQLTable();
+ String sqlAlias = curConfig == null ? null : curConfig.getAlias();
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)) {
+ if (StringUtil.isEmpty(sqlTable, true)) {
if (toFindJoin) { // 在主表字段数量内的都归属主表
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
@@ -323,12 +326,15 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
List c = cfg == null ? null : cfg.getColumn();
- nextViceColumnStart += (
- c != null && ! c.isEmpty() ? c.size()
- : (Objects.equals(sqlTable, lastTableName) || sqlTable.equalsIgnoreCase(lastTableName) ? 1 : 0)
+ nextViceColumnStart += (c != null && ! c.isEmpty() ?
+ c.size() : (
+ StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
+ && StringUtil.equals(sqlAlias, lastAliasName) ? 1 : 0
+ )
);
if (i < nextViceColumnStart) {
sqlTable = cfg.getSQLTable();
+ sqlAlias = cfg.getAlias();
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
curJoin = join;
@@ -345,6 +351,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
// 没有 @column,仍然定位不了,用前一个 table 名。FIXME 如果刚好某个表内第一个字段是就是 SQL 函数?
if (StringUtil.isEmpty(sqlTable, true)) {
sqlTable = lastTableName;
+ sqlAlias = lastAliasName;
toFindJoin = false;
}
}
@@ -353,8 +360,9 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
sqlTable = sqlTable.substring(1, sqlTable.length() - 1);
}
- if ((sqlTable == null && lastTableName != null) || (sqlTable != null && ! sqlTable.equalsIgnoreCase(lastTableName))) {
+ if (StringUtil.equalsIgnoreCase(sqlTable, lastTableName) == false || StringUtil.equals(sqlAlias, lastAliasName) == false) {
lastTableName = sqlTable;
+ lastAliasName = sqlAlias;
lastViceColumnStart = i;
if (toFindJoin) { // 找到对应的副表 JOIN 配置
@@ -362,7 +370,8 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
Join join = joinList.get(j);
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
- if (cfg != null && sqlTable != null && sqlTable.equalsIgnoreCase(cfg.getSQLTable())) {
+ if (cfg != null && StringUtil.equalsIgnoreCase(sqlTable, cfg.getSQLTable())
+ ) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
curJoin = join;
@@ -378,7 +387,7 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
if (isMain) {
lastViceColumnStart ++;
- }
+ }
else {
if (curJoin == null) {
curJoin = lastJoin;
@@ -398,23 +407,42 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
}
}
- columnIndexAndJoinMap[i - 1] = curJoin; // columnIndexAndJoinMap.put(i, curJoin); // TODO columnIndexAndTableMap[i] = sqlTable 提速?
-
-// if (column != null && column.isEmpty() == false) {
-// viceColumnStart = column.size() + 1;
-// }
-// else if (config.getSQLTable().equalsIgnoreCase(sqlTable) == false) {
-// viceColumnStart = i;
-// }
+ columnIndexAndJoinMap[i - 1] = curJoin;
}
+
+ // 如果是主表则直接用主表对应的 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;
+ if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
+ viceConfig.putWhere(curJoin.getKey(), item.get(curJoin.getTargetKey()), true);
+ }
+ String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
- item = onPutColumn(config, rs, rsmd, index, item, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
+ if (StringUtil.isEmpty(viceSql, true)) {
+ Log.i(TAG, "execute StringUtil.isEmpty(viceSql, true) >> 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()); // 缓存固定空数据,避免后续多余查询
+ }
+ else {
+ curItem = (JSONObject) childMap.get(viceSql);
+ if (curItem == null) {
+ curItem = new JSONObject(true);
+ childMap.put(viceSql, curItem);
+ }
+ }
+ }
+
+ curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
}
resultList = onPutTable(config, rs, rsmd, resultList, index, item);
- Log.d(TAG, "\n execute while (rs.next()) { resultList.put( " + index + ", result); "
- + "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
+ Log.d(TAG, "execute while (rs.next()) { resultList.put( " + index + ", result); " + "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
}
}
}
@@ -622,61 +650,21 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
*/
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 {
-
+ 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;");
return table;
}
- //已改为 rsmd.getTableName(columnIndex) 支持副表不传 @column , 但如何判断是副表?childMap != null
- // String lable = rsmd.getColumnLabel(columnIndex);
- // int dotIndex = lable.indexOf(".");
String lable = getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap);
-
-// String childTable = childMap == null ? null : sqlTableName; // rsmd.getTableName(columnIndex); //dotIndex < 0 ? null : lable.substring(0, dotIndex);
-
- JSONObject finalTable = null;
- String childSql = null;
-
- SQLConfig childConfig = join == null || join.isSQLJoin() == false ? null : join.getCacheConfig();
- if (childConfig == null) {
- finalTable = table;
- }
- else {
- // lable = column;
-
- //
-
-// List joinList = config.getJoinList();
-// if (joinList != null) {
-// for (Join j : joinList) {
-// childConfig = j.isAppJoin() ? null : j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
-
- // FIXME 副表的 SQL 函数,甚至普通字段都可能从 rsmd.getTableName(columnIndex) 拿到 ""
-// if (childConfig != null && childTable.equalsIgnoreCase(childConfig.getSQLTable())) {
-
- childConfig.putWhere(join.getKey(), table.get(join.getTargetKey()), true);
- childSql = childConfig.getSQL(false);
-
- if (StringUtil.isEmpty(childSql, true)) {
- return table;
- }
-
- finalTable = (JSONObject) childMap.get(childSql);
-// break;
-// }
-// }
-// }
-
- }
-
Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, lable, childMap);
+
// 必须 put 进去,否则某个字段为 null 可能导致中断后续正常返回值 if (value != null) {
- if (finalTable == null) {
- finalTable = new JSONObject(true);
- childMap.put(childSql, finalTable);
- }
- finalTable.put(lable, value);
+ table.put(lable, value);
// }
return table;
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index 70c4401b9..7d8707393 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -198,12 +198,13 @@ public boolean isLeftOrRightJoin() {
String jt = getJoinType();
return "<".equals(jt) || ">".equals(jt);
}
-
+
public boolean canCacheViceTable() {
String jt = getJoinType();
return "@".equals(jt) || "<".equals(jt) || ">".equals(jt) || "&".equals(jt) || "*".equals(jt) || ")".equals(jt);
+ // 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 return ! isFullJoin(); // ! - OUTER, ( - FOREIGN 都需要缓存空副表数据,避免多余的查询
}
-
+
public boolean isSQLJoin() {
return ! isAppJoin();
}
@@ -215,7 +216,7 @@ public static boolean isSQLJoin(Join j) {
public static boolean isAppJoin(Join j) {
return j != null && j.isAppJoin();
}
-
+
public static boolean isLeftOrRightJoin(Join j) {
return j != null && j.isLeftOrRightJoin();
}
From d41f2a49a6836506ca09b33548477dc5f02df285 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 5 Jan 2022 01:32:20 +0800
Subject: [PATCH 040/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=AF=B9=E6=8E=A5=20Hive=20=E5=92=8C=20Hadoop=20=E7=9A=84=20De?=
=?UTF-8?q?mo=EF=BC=8C=E6=84=9F=E8=B0=A2=20chenyanlann=20=E7=9A=84?=
=?UTF-8?q?=E8=B4=A1=E7=8C=AE~?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
可以点 Star 支持下作者哦
https://github.com/chenyanlann/APIJSONBoot_Hive
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index af6520919..33e7ec713 100644
--- a/README.md
+++ b/README.md
@@ -17,10 +17,12 @@ This source code is licensed under the Apache License Version 2.0
+
+
-
+
@@ -478,6 +480,8 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
[APIJSONDemo_ClickHouse](https://github.com/chenyanlann/APIJSONDemo_ClickHouse) APIJSON + SpringBoot 连接 ClickHouse 使用的 Demo
+[APIJSONBoot_Hive](https://github.com/chenyanlann/APIJSONBoot_Hive) APIJSON + SpringBoot 连接 Hive 使用的 Demo
+
[apijson-sample](https://gitee.com/greyzeng/apijson-sample) APIJSON 简单使用 Demo 及教程
[apijson-examples](https://gitee.com/drone/apijson-examples) APIJSON 的前端、业务后端、管理后端 Demo
From 27d7e0154877e2973dda13aba33a2ab03be965c6 Mon Sep 17 00:00:00 2001
From: chenyanlann <62465397+chenyanlann@users.noreply.github.com>
Date: Tue, 11 Jan 2022 20:30:55 +0800
Subject: [PATCH 041/674] Update AbstractSQLExecutor.java
fix code format
---
APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index bc9547e82..54a77bca1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -711,10 +711,10 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNu
String key = rsmd.getColumnLabel(columnIndex);// dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
if (config.isHive()) {
String table_name = config.getTable();
- if(AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
String pattern = "^" + table_name + "\\." + "[a-zA-Z]+$";
boolean isMatch = Pattern.matches(pattern, key);
- if(isMatch) key = key.split("\\.")[1];
+ if (isMatch) key = key.split("\\.")[1];
}
return key;
}
From 8d16e66b7668761fcd98e545b6f21779ab7d0f20 Mon Sep 17 00:00:00 2001
From: chenyanlann <62465397+chenyanlann@users.noreply.github.com>
Date: Tue, 11 Jan 2022 21:21:42 +0800
Subject: [PATCH 042/674] Update AbstractSQLExecutor.java
fix code format
---
.../main/java/apijson/orm/AbstractSQLExecutor.java | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 54a77bca1..2a8f6d7f8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -711,10 +711,14 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNu
String key = rsmd.getColumnLabel(columnIndex);// dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
if (config.isHive()) {
String table_name = config.getTable();
- if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) {
+ table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ }
String pattern = "^" + table_name + "\\." + "[a-zA-Z]+$";
boolean isMatch = Pattern.matches(pattern, key);
- if (isMatch) key = key.split("\\.")[1];
+ if (isMatch) {
+ key = key.split("\\.")[1];
+ }
}
return key;
}
@@ -972,7 +976,9 @@ public ResultSet executeQuery(@NotNull SQLConfig config) throws Exception {
public int executeUpdate(@NotNull SQLConfig config) throws Exception {
PreparedStatement s = getStatement(config);
int count = s.executeUpdate(); //PreparedStatement 不用传 SQL
- if (config.isHive() && count==0) count = 1;
+ if (config.isHive() && count==0) {
+ count = 1;
+ }
if (config.getMethod() == RequestMethod.POST && config.getId() == null) { //自增id
ResultSet rs = s.getGeneratedKeys();
From dae5ac9709abde6e749fce49cfacbd9b805a8e48 Mon Sep 17 00:00:00 2001
From: chenyanlann <62465397+chenyanlann@users.noreply.github.com>
Date: Wed, 12 Jan 2022 00:00:46 +0800
Subject: [PATCH 043/674] Update AbstractSQLExecutor.java
---
.../src/main/java/apijson/orm/AbstractSQLExecutor.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 2a8f6d7f8..3c7ddc2a7 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -710,11 +710,11 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNu
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map childMap) throws Exception {
String key = rsmd.getColumnLabel(columnIndex);// dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
if (config.isHive()) {
- String table_name = config.getTable();
- if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) {
- table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ String tableName = config.getTable();
+ if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(tableName)) {
+ tableName = AbstractSQLConfig.TABLE_KEY_MAP.get(tableName);
}
- String pattern = "^" + table_name + "\\." + "[a-zA-Z]+$";
+ String pattern = "^" + tableName + "\\." + "[a-zA-Z]+$";
boolean isMatch = Pattern.matches(pattern, key);
if (isMatch) {
key = key.split("\\.")[1];
@@ -976,7 +976,7 @@ public ResultSet executeQuery(@NotNull SQLConfig config) throws Exception {
public int executeUpdate(@NotNull SQLConfig config) throws Exception {
PreparedStatement s = getStatement(config);
int count = s.executeUpdate(); //PreparedStatement 不用传 SQL
- if (config.isHive() && count==0) {
+ if (config.isHive() && count == 0) {
count = 1;
}
From bb58a25354478b917204663aee9c1af21064893c Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 17 Jan 2022 00:39:41 +0800
Subject: [PATCH 044/674] =?UTF-8?q?=E8=B0=83=E8=AF=95=E6=97=B6=E9=97=B4?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=96=B0=E5=A2=9E=20parse=20=E5=92=8C=20sql?=
=?UTF-8?q?=20=E4=B8=A4=E4=B8=AA=E6=97=B6=E9=95=BF=EF=BC=8C=E4=BE=8B?=
=?UTF-8?q?=E5=A6=82=20"time:start|duration|end|parse|sql":=20"16417510485?=
=?UTF-8?q?73|145|1641751048718|50|95"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
.../main/java/apijson/orm/AbstractParser.java | 13 +-
.../java/apijson/orm/AbstractSQLExecutor.java | 134 +++++++++++++++---
.../main/java/apijson/orm/SQLExecutor.java | 5 +-
4 files changed, 130 insertions(+), 24 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index d67f19a71..6dfc96caf 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 = "4.8.0";
+ public static final String VERSION = "4.8.5";
public static final String KEY_SYSTEM_INFO_DIVIDER = "---|-----APIJSON SYSTEM INFO-----|---";
//默认的时间格式
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 1bc1bacfb..529f4025f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -353,6 +353,7 @@ public JSONObject parseResponse(String request) {
}
private int queryDepth;
+ private long executedSQLDuration;
/**解析请求json并获取对应结果
* @param request
@@ -420,6 +421,8 @@ public JSONObject parseResponse(JSONObject request) {
onBegin();
try {
queryDepth = 0;
+ executedSQLDuration = 0;
+
requestObject = onObjectParse(request, null, null, null, false);
onCommit();
@@ -441,7 +444,10 @@ public JSONObject parseResponse(JSONObject request) {
if (Log.DEBUG) {
requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount());
requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth());
- requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime);
+
+ executedSQLDuration += sqlExecutor.getExecutedSQLDuration() + sqlExecutor.getSqlResultDuration();
+ long parseDuration = duration - executedSQLDuration;
+ requestObject.put("time:start|duration|end|parse|sql", startTime + "|" + duration + "|" + endTime + "|" + parseDuration + "|" + executedSQLDuration);
if (error != null) {
requestObject.put("trace:throw", error.getClass().getName());
@@ -1845,7 +1851,10 @@ public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Except
}
}
else {
- result = getSQLExecutor().execute(config, false);
+ sqlExecutor = getSQLExecutor();
+ result = sqlExecutor.execute(config, false);
+ // FIXME 改为直接在 sqlExecutor 内加好,最后 Parser 取结果,可以解决并发执行导致内部计算出错
+// executedSQLDuration += sqlExecutor.getExecutedSQLDuration() + sqlExecutor.getSqlResultDuration();
}
return result;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index bc9547e82..e7f959a93 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -25,7 +25,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -37,7 +36,6 @@
import apijson.NotNull;
import apijson.RequestMethod;
import apijson.StringUtil;
-import apijson.orm.AbstractSQLConfig;
/**executor for query(read) or update(write) MySQL database
* @author Lemon
@@ -67,6 +65,33 @@ public int getCachedSQLCount() {
public int getExecutedSQLCount() {
return executedSQLCount;
}
+
+ // 只要不是并发执行且执行完立刻获取,就不会是错的,否则需要一并返回,可以 JSONObject.put("@EXECUTED_SQL_TIME:START|DURATION|END", )
+ private long executedSQLStartTime;
+ private long executedSQLEndTime;
+ private long executedSQLDuration;
+ private long sqlResultDuration;
+
+ public long getExecutedSQLStartTime() {
+ return executedSQLStartTime;
+ }
+ public long getExecutedSQLEndTime() {
+ return executedSQLEndTime;
+ }
+ @Override
+ public long getExecutedSQLDuration() {
+ if (executedSQLDuration <= 0) {
+ long startTime = getExecutedSQLStartTime();
+ long endTime = getExecutedSQLEndTime();
+ executedSQLDuration = startTime <= 0 || endTime <= 0 ? 0 : endTime - startTime; // FIXME 有时莫名其妙地算出来是负数
+ }
+ return executedSQLDuration < 0 ? 0 : executedSQLDuration;
+ }
+
+ @Override
+ public long getSqlResultDuration() {
+ return sqlResultDuration;
+ }
/**
* 缓存map
@@ -127,16 +152,25 @@ public JSONObject getCacheItem(String sql, int position, int type) {
@Override
public ResultSet executeQuery(@NotNull Statement statement, String sql) throws Exception {
- return statement.executeQuery(sql);
+// executedSQLStartTime = System.currentTimeMillis();
+ ResultSet rs = statement.executeQuery(sql);
+// executedSQLEndTime = System.currentTimeMillis();
+ return rs;
}
@Override
public int executeUpdate(@NotNull Statement statement, String sql) throws Exception {
- return statement.executeUpdate(sql);
+// executedSQLStartTime = System.currentTimeMillis();
+ int c = statement.executeUpdate(sql);
+// executedSQLEndTime = System.currentTimeMillis();
+ return c;
}
@Override
public ResultSet execute(@NotNull Statement statement, String sql) throws Exception {
+// executedSQLStartTime = System.currentTimeMillis();
statement.execute(sql);
- return statement.getResultSet();
+ ResultSet rs = statement.getResultSet();
+// executedSQLEndTime = System.currentTimeMillis();
+ return rs;
}
/**执行SQL
@@ -146,6 +180,11 @@ public ResultSet execute(@NotNull Statement statement, String sql) throws Except
*/
@Override
public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws Exception {
+// executedSQLDuration = 0;
+ executedSQLStartTime = System.currentTimeMillis();
+ executedSQLEndTime = executedSQLStartTime;
+// sqlResultDuration = 0;
+
boolean isPrepared = config.isPrepared();
final String sql = config.getSQL(false);
@@ -182,15 +221,19 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
try {
if (unknowType) {
- Statement statement = getStatement(config);
-
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
executedSQLCount ++;
+ executedSQLStartTime = System.currentTimeMillis();
}
+ Statement statement = getStatement(config);
rs = execute(statement, sql);
-
- result = new JSONObject(true);
int updateCount = statement.getUpdateCount();
+ if (isExplain == false) {
+ executedSQLEndTime = System.currentTimeMillis();
+ executedSQLDuration += executedSQLEndTime - executedSQLStartTime;
+ }
+
+ result = new JSONObject(true);
result.put(JSONResponse.KEY_COUNT, updateCount);
result.put("update", updateCount >= 0);
//导致后面 rs.getMetaData() 报错 Operation not allowed after ResultSet closed result.put("moreResults", statement.getMoreResults());
@@ -202,8 +245,14 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
case DELETE:
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
executedSQLCount ++;
+ executedSQLStartTime = System.currentTimeMillis();
}
int updateCount = executeUpdate(config);
+ if (isExplain == false) {
+ executedSQLEndTime = System.currentTimeMillis();
+ executedSQLDuration += executedSQLEndTime - executedSQLStartTime;
+ }
+
if (updateCount <= 0) {
throw new IllegalAccessException("没权限访问或对象不存在!"); // NotExistException 会被 catch 转为成功状态
}
@@ -235,8 +284,13 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
executedSQLCount ++;
+ executedSQLStartTime = System.currentTimeMillis();
}
rs = executeQuery(config); //FIXME SQL Server 是一次返回两个结果集,包括查询结果和执行计划,需要 moreResults
+ if (isExplain == false) {
+ executedSQLEndTime = System.currentTimeMillis();
+ executedSQLDuration += executedSQLEndTime - executedSQLStartTime;
+ }
break;
default://OPTIONS, TRACE等
@@ -264,8 +318,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
int index = -1;
+ long startTime2 = System.currentTimeMillis();
ResultSetMetaData rsmd = rs.getMetaData();
final int length = rsmd.getColumnCount();
+ sqlResultDuration += System.currentTimeMillis() - startTime2;
//
childMap = new HashMap<>(); //要存到cacheMap
@@ -286,7 +342,14 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
Join[] columnIndexAndJoinMap = isExplain || ! hasJoin ? null : new Join[length];
// int viceColumnStart = length + 1; //第一个副表字段的index
+
+// FIXME 统计游标查找的时长?可能 ResultSet.next() 及 getTableName, getColumnName, getObject 比较耗时,因为不是一次加载到内存,而是边读边发
+
+ long lastCursorTime = System.currentTimeMillis();
while (rs.next()) {
+ sqlResultDuration += System.currentTimeMillis() - lastCursorTime;
+ lastCursorTime = System.currentTimeMillis();
+
index ++;
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n execute while (rs.next()){ index = " + index + "\n\n");
@@ -318,8 +381,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
if (StringUtil.isEmpty(sqlTable, true)) {
if (toFindJoin) { // 在主表字段数量内的都归属主表
+ long startTime3 = System.currentTimeMillis();
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
-
+ sqlResultDuration += System.currentTimeMillis() - startTime3;
+
if (StringUtil.isEmpty(sqlTable, true)) { // hasJoin 已包含这个判断 && joinList != null) {
int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
@@ -588,12 +653,19 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
int index = -1;
+ long startTime2 = System.currentTimeMillis();
ResultSetMetaData rsmd = rs.getMetaData();
final int length = rsmd.getColumnCount();
-
+ sqlResultDuration += System.currentTimeMillis() - startTime2;
+
JSONObject result;
String cacheSql;
+
+ long lastCursorTime = System.currentTimeMillis();
while (rs.next()) { //FIXME 同时有 @ APP JOIN 和 < 等 SQL JOIN 时,next = false 总是无法进入循环,导致缓存失效,可能是连接池或线程问题
+ sqlResultDuration += System.currentTimeMillis() - lastCursorTime;
+ lastCursorTime = System.currentTimeMillis();
+
index ++;
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n executeAppJoin while (rs.next()){ index = " + index + "\n\n");
@@ -708,13 +780,20 @@ protected List onPutTable(@NotNull SQLConfig config, @NotNull Result
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 {
- String key = rsmd.getColumnLabel(columnIndex);// dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ long startTime = System.currentTimeMillis();
+ String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
+ sqlResultDuration += System.currentTimeMillis() - startTime;
+
if (config.isHive()) {
String table_name = config.getTable();
- if(AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ if (AbstractSQLConfig.TABLE_KEY_MAP.containsKey(table_name)) {
+ table_name = AbstractSQLConfig.TABLE_KEY_MAP.get(table_name);
+ }
String pattern = "^" + table_name + "\\." + "[a-zA-Z]+$";
boolean isMatch = Pattern.matches(pattern, key);
- if(isMatch) key = key.split("\\.")[1];
+ if (isMatch) {
+ key = key.split("\\.")[1];
+ }
}
return key;
}
@@ -722,7 +801,10 @@ protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNu
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 {
+ 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, "type:" + rsmd.getColumnType(i));
@@ -799,7 +881,10 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
@Override
public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable) {
try {
+ long startTime = System.currentTimeMillis();
String column = rsmd.getColumnTypeName(position);
+ sqlResultDuration += System.currentTimeMillis() - startTime;
+
//TODO CHAR和JSON类型的字段,getColumnType返回值都是1 ,如果不用CHAR,改用VARCHAR,则可以用上面这行来提高性能。
//return rsmd.getColumnType(position) == 1;
@@ -965,19 +1050,28 @@ public void close() {
@Override
public ResultSet executeQuery(@NotNull SQLConfig config) throws Exception {
- return getStatement(config).executeQuery(); //PreparedStatement 不用传 SQL
+ PreparedStatement s = getStatement(config);
+// 不准,getStatement 有时比 execute sql 更耗时 executedSQLStartTime = System.currentTimeMillis();
+ ResultSet rs = s.executeQuery(); //PreparedStatement 不用传 SQL
+// executedSQLEndTime = System.currentTimeMillis();
+ return rs;
}
@Override
public int executeUpdate(@NotNull SQLConfig config) throws Exception {
PreparedStatement s = getStatement(config);
- int count = s.executeUpdate(); //PreparedStatement 不用传 SQL
- if (config.isHive() && count==0) count = 1;
-
- if (config.getMethod() == RequestMethod.POST && config.getId() == null) { //自增id
+// 不准,getStatement 有时比 execute sql 更耗时 executedSQLStartTime = System.currentTimeMillis();
+ int count = s.executeUpdate(); // PreparedStatement 不用传 SQL
+// executedSQLEndTime = System.currentTimeMillis();
+
+ if (count <= 0 && config.isHive()) {
+ count = 1;
+ }
+
+ if (config.getId() == null && config.getMethod() == RequestMethod.POST) { // 自增id
ResultSet rs = s.getGeneratedKeys();
if (rs != null && rs.next()) {
- config.setId(rs.getLong(1));//返回插入的主键id
+ config.setId(rs.getLong(1)); //返回插入的主键id FIXME Oracle 拿不到
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/SQLExecutor.java
index 9fe5917a7..3cb2bf60a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLExecutor.java
@@ -115,5 +115,8 @@ public interface SQLExecutor {
int getExecutedSQLCount();
-
+ long getExecutedSQLDuration();
+
+ long getSqlResultDuration();
+
}
From a6ac4b726a649f277384f525e0d9f339b7f59862 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jan 2022 18:44:50 +0800
Subject: [PATCH 045/674] Update README.md
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 33e7ec713..3a6f0cf6a 100644
--- a/README.md
+++ b/README.md
@@ -244,7 +244,11 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯科技有限公司](https://www.tencent.com)
-
+ * [腾讯音乐娱乐集团](https://www.tencentmusic.com)
+ * [华能贵成信托有限公司](https://www.hngtrust.com)
+ * [投投科技](https://www.toutou.com.cn)
+ * [圆通科技](https://www.tencentmusic.com)
+ * [乐拼科技](https://www.lepinyongche.com)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个知乎基础研发架构师、1 个圆通工程师 等):
From ff8efebd33eb89eeb997bc8555d527ad4ee449f0 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jan 2022 18:46:49 +0800
Subject: [PATCH 046/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3a6f0cf6a..eaf7445e4 100644
--- a/README.md
+++ b/README.md
@@ -245,7 +245,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯科技有限公司](https://www.tencent.com)
* [腾讯音乐娱乐集团](https://www.tencentmusic.com)
- * [华能贵成信托有限公司](https://www.hngtrust.com)
+ * [华能贵诚信托有限公司](https://www.hngtrust.com)
* [投投科技](https://www.toutou.com.cn)
* [圆通科技](https://www.tencentmusic.com)
* [乐拼科技](https://www.lepinyongche.com)
From bfe4c9d8a1c0b4c1cef510e9ddb3e273ef473378 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jan 2022 18:50:12 +0800
Subject: [PATCH 047/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index eaf7445e4..f63435b13 100644
--- a/README.md
+++ b/README.md
@@ -247,7 +247,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯音乐娱乐集团](https://www.tencentmusic.com)
* [华能贵诚信托有限公司](https://www.hngtrust.com)
* [投投科技](https://www.toutou.com.cn)
- * [圆通科技](https://www.tencentmusic.com)
+ * [圆通科技](https://www.yto.net.cn)
* [乐拼科技](https://www.lepinyongche.com)
### 贡献者们
From 6ef55cb4ca8f01a15f690fa31295fb87e1fd2c33 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 30 Jan 2022 18:52:21 +0800
Subject: [PATCH 048/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f63435b13..d367d6109 100644
--- a/README.md
+++ b/README.md
@@ -247,7 +247,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯音乐娱乐集团](https://www.tencentmusic.com)
* [华能贵诚信托有限公司](https://www.hngtrust.com)
* [投投科技](https://www.toutou.com.cn)
- * [圆通科技](https://www.yto.net.cn)
+ * [圆通速递](https://www.yto.net.cn)
* [乐拼科技](https://www.lepinyongche.com)
### 贡献者们
From 2da22e618585f4009145c69965092ab3987004b3 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Feb 2022 21:08:59 +0800
Subject: [PATCH 049/674] =?UTF-8?q?=E6=8F=90=E5=8D=87=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=B7=E4=B8=BA=204.9.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 f1fdcd63a..377ecc644 100755
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
apijson.orm
apijson-orm
- 4.8.0
+ 4.9.0
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 6dfc96caf..f97fae342 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 = "4.8.5";
+ public static final String VERSION = "4.9.0";
public static final String KEY_SYSTEM_INFO_DIVIDER = "---|-----APIJSON SYSTEM INFO-----|---";
//默认的时间格式
From d6bd9dd4e4725c35acdcddfca5feb7347dd7ef12 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Feb 2022 21:15:24 +0800
Subject: [PATCH 050/674] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index d367d6109..c5f4f9069 100644
--- a/README.md
+++ b/README.md
@@ -245,6 +245,8 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯科技有限公司](https://www.tencent.com)
* [腾讯音乐娱乐集团](https://www.tencentmusic.com)
+ * [深圳市传音通讯有限公司](http://www.transsion.com)
+ * [社宝信息科技(上海)有限公司](http://shebaochina.com)
* [华能贵诚信托有限公司](https://www.hngtrust.com)
* [投投科技](https://www.toutou.com.cn)
* [圆通速递](https://www.yto.net.cn)
From 7531e2e6602a52c51472d0c6722ca7a3bf04c99f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Feb 2022 21:18:14 +0800
Subject: [PATCH 051/674] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index c5f4f9069..7fea8ebd9 100644
--- a/README.md
+++ b/README.md
@@ -245,8 +245,8 @@ https://github.com/Tencent/APIJSON/issues/187
* [腾讯科技有限公司](https://www.tencent.com)
* [腾讯音乐娱乐集团](https://www.tencentmusic.com)
- * [深圳市传音通讯有限公司](http://www.transsion.com)
- * [社宝信息科技(上海)有限公司](http://shebaochina.com)
+ * [深圳市传音通讯有限公司](https://www.transsion.com)
+ * [社宝信息科技(上海)有限公司](https://shebaochina.com)
* [华能贵诚信托有限公司](https://www.hngtrust.com)
* [投投科技](https://www.toutou.com.cn)
* [圆通速递](https://www.yto.net.cn)
From 5b29c96691df01b5a06da51fe58c8d490c9ce439 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 20 Feb 2022 20:45:08 +0800
Subject: [PATCH 052/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E6=BC=94=E7=A4=BA=E8=AF=B4=E6=98=8E=20GIF=20=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README.md b/README.md
index 7fea8ebd9..2a1d989a5 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,7 @@ APIJSON 是一种专为 API 而生的 JSON 网络传输协议 以及 基于这

+
@@ -119,6 +120,8 @@ APIJSON 是一种专为 API 而生的 JSON 网络传输协议 以及 基于这

+
+
From 60f6bbe73f183f08b9b6f02ebd7ea2626f750c61 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 20 Feb 2022 21:01:28 +0800
Subject: [PATCH 053/674] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E=E5=8A=9F=E8=83=BD=E6=BC=94=E7=A4=BA=E5=8F=8A=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E=E7=9A=84=20GIF=20=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Document.md | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/Document.md b/Document.md
index 73f262b15..fdcc24238 100644
--- a/Document.md
+++ b/Document.md
@@ -52,6 +52,8 @@ https://github.com/Tencent/APIJSON
}
+
+
#### 获取用户列表
@@ -95,6 +97,8 @@ https://github.com/Tencent/APIJSON
}
+
+
#### 获取动态及发布者用户
@@ -134,6 +138,8 @@ https://github.com/Tencent/APIJSON
"msg":"success"
}
+
+
@@ -254,6 +260,29 @@ https://github.com/Tencent/APIJSON
}
+
+
+
+ APIJSON 各种 JOIN:< LEFT, > RIGHT, & INNER 等
+
+
+
+
+
+
+
+ APIJSON 各种子查询:@from@ 数据源, key@ =, key{}@ IN, key<>@ CONTAINS, key}{@ EXISTS 等
+
+
+
+
+
+
+
+ APIJSON 部分功能演示集合,由浅入深、由简单到复杂
+
+
+
From 7214c8d66ce48bfd3d48f4dc4dbf2c30213183f8 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 20 Feb 2022 21:54:31 +0800
Subject: [PATCH 054/674] =?UTF-8?q?=E9=80=9A=E7=94=A8=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=EF=BC=9A=E5=AE=8C=E5=96=84=E5=8A=9F=E8=83=BD=E6=BC=94=E7=A4=BA?=
=?UTF-8?q?=E5=8F=8A=E8=AF=B4=E6=98=8E=E7=9A=84=20GIF=20=E5=9B=BE=E6=A0=87?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Document.md | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/Document.md b/Document.md
index fdcc24238..9187c4a43 100644
--- a/Document.md
+++ b/Document.md
@@ -52,6 +52,10 @@ https://github.com/Tencent/APIJSON
}
+
+ APIJSON 各种单表对象查询:简单查询、统计、分组、排序、聚合、比较、筛选字段、字段别名 等
+
+

@@ -97,6 +101,10 @@ https://github.com/Tencent/APIJSON
}
+
+ APIJSON 各种单表数组查询:简单查询、统计、分组、排序、聚合、分页、比较、搜索、正则、条件组合 等
+
+

@@ -139,8 +147,6 @@ https://github.com/Tencent/APIJSON
}
-
-
#### 获取类似微信朋友圈的动态列表
@@ -260,20 +266,26 @@ https://github.com/Tencent/APIJSON
}
+
+ APIJSON 各种多表关联查询:一对一、一对多、多对一、各种条件 等
+
+
+
+
- APIJSON 各种 JOIN:< LEFT, > RIGHT, & INNER 等
+ APIJSON 各种 JOIN:< LEFT JOIN, & INNER JOIN 等
-
+

- APIJSON 各种子查询:@from@ 数据源, key@ =, key{}@ IN, key<>@ CONTAINS, key}{@ EXISTS 等
+ APIJSON 各种子查询:@from@ FROM, key@ =, key>@ >, key{}@ IN, key}{@ EXISTS 等
-
+

@@ -281,7 +293,7 @@ https://github.com/Tencent/APIJSON
APIJSON 部分功能演示集合,由浅入深、由简单到复杂
-
+

From cc34a54e27f467af5b82651d16f15fea6c24ec19 Mon Sep 17 00:00:00 2001
From: fanpocha <289484900@qq.com>
Date: Tue, 22 Feb 2022 10:34:27 +0800
Subject: [PATCH 055/674] Update README.md
add caizu
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 2a1d989a5..61a28a7cf 100644
--- a/README.md
+++ b/README.md
@@ -244,6 +244,7 @@ https://github.com/Tencent/APIJSON/issues/187
+
珠海采筑电子商务有限公司
* [腾讯科技有限公司](https://www.tencent.com)
From 24e5c0b264fbd858bb4af9ab7ece71e548d225cf Mon Sep 17 00:00:00 2001
From: fanpocha <289484900@qq.com>
Date: Tue, 22 Feb 2022 10:37:11 +0800
Subject: [PATCH 056/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 61a28a7cf..a7057b70a 100644
--- a/README.md
+++ b/README.md
@@ -244,7 +244,7 @@ https://github.com/Tencent/APIJSON/issues/187
-
珠海采筑电子商务有限公司
+
* [腾讯科技有限公司](https://www.tencent.com)
From b2059445ab16e94d5a39227c29444fba67a76c06 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 22 Feb 2022 23:43:51 +0800
Subject: [PATCH 057/674] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=99=BB=E8=AE=B0?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E7=8F=A0=E6=B5=B7=E9=87=87=E7=AD=91?=
=?UTF-8?q?=E7=94=B5=E5=AD=90=E5=95=86=E5=8A=A1=E6=9C=89=E9=99=90=E5=85=AC?=
=?UTF-8?q?=E5=8F=B8=EF=BC=8C=E6=96=B0=E5=A2=9E=20=E4=B9=90=E6=8B=BC?=
=?UTF-8?q?=E7=94=A8=E8=BD=A6=20=E7=9A=84=20Logo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://github.com/Tencent/APIJSON#%E4%BD%BF%E7%94%A8%E7%99%BB%E8%AE%B0
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a7057b70a..22390da85 100644
--- a/README.md
+++ b/README.md
@@ -226,7 +226,7 @@ https://github.com/Tencent/APIJSON/issues/36
如果您在使用 APIJSON,请让我们知道,您的使用对我们非常重要(按登记顺序排列):
https://github.com/Tencent/APIJSON/issues/187
-
+
@@ -244,6 +244,7 @@ https://github.com/Tencent/APIJSON/issues/187
+
@@ -255,6 +256,7 @@ https://github.com/Tencent/APIJSON/issues/187
* [投投科技](https://www.toutou.com.cn)
* [圆通速递](https://www.yto.net.cn)
* [乐拼科技](https://www.lepinyongche.com)
+ * [珠海采筑电子商务有限公司](https://www.aupup.com)
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个知乎基础研发架构师、1 个圆通工程师 等):
From dda1120c5d0e16344d4ac905ed79ea7abf947537 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 27 Feb 2022 02:31:16 +0800
Subject: [PATCH 058/674] =?UTF-8?q?JOIN=20=E6=94=AF=E6=8C=81=E5=A4=9A?=
=?UTF-8?q?=E4=B8=AA=E5=AD=97=E6=AE=B5=E5=85=B3=E8=81=94=E5=8F=8A=E5=BC=95?=
=?UTF-8?q?=E7=94=A8=E8=B5=8B=E5=80=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/apijson/orm/AbstractParser.java | 350 ++++++++++--------
.../java/apijson/orm/AbstractSQLConfig.java | 62 ++--
.../java/apijson/orm/AbstractSQLExecutor.java | 48 ++-
.../src/main/java/apijson/orm/Entry.java | 7 +-
.../src/main/java/apijson/orm/Join.java | 204 +++++-----
5 files changed, 384 insertions(+), 287 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 529f4025f..3e3703468 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -1307,8 +1308,25 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
return response;
}
- /**多表同时筛选
- * @param join "&/User/id@, JOIN_COPY_KEY_LIST;
+ static { // TODO 不全
+ 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_SCHEMA);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_DATASOURCE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COLUMN);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COMBINE);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_GROUP);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_ORDER);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_RAW);
+ }
+
+ /**JOIN 多表同时筛选
+ * @param join "&/User, joinList = new ArrayList<>();
-
- JSONObject tableObj;
- String targetPath;
-
- JSONObject targetObj;
- String targetTable;
- String targetKey;
-
- String path;
-
- // List onList = new ArrayList<>();
- for (Entry e : set) {//User/id@
- if (e.getValue() instanceof JSONObject == false) {
+ for (Entry e : set) { // { &/User:{}, ( ) <> () *
// if (StringUtil.isEmpty(joinType, true)) {
@@ -1373,192 +1380,223 @@ else if (join != null){
path = path.substring(index + 1);
index = path.indexOf("/");
- String tableKey = index < 0 ? null : path.substring(0, index); //User:owner
+ String tableKey = index < 0 ? path : path.substring(0, index); // User:owner
apijson.orm.Entry entry = Pair.parseEntry(tableKey, true);
- String table = entry.getKey(); //User
+ String table = entry.getKey(); // User
if (StringUtil.isName(table) == false) {
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 Table 值 " + table + " 不合法!"
- + "必须为 &/Table0/key0,> tableSet = tableObj.entrySet();
+ // 取出所有 join 条件
+ JSONObject requestObj = new JSONObject(true); // (JSONObject) obj.clone();
+
+ boolean matchSingle = false;
+ for (Entry tableEntry : tableSet) {
+ String k = tableEntry.getKey();
+ Object v = k == null ? null : tableEntry.getValue();
+ if (v == null) {
+ continue;
+ }
- // 主表不允许别名
- // apijson.orm.Entry targetEntry = Pair.parseEntry(targetTableKey, true);
- // targetTable = targetEntry.getKey(); //User
- // if (StringUtil.isName(targetTable) == false) {
- // throw new IllegalArgumentException("/" + path + ":'/targetTable/targetKey' 中 targetTable 值 " + targetTable + " 不合法!必须满足大写字母开头的表对象英文单词 key 格式!");
- // }
- //
- // String targetAlias = targetEntry.getValue(); //owner
- // if (StringUtil.isNotEmpty(targetAlias, true) && StringUtil.isName(targetAlias) == false) {
- // throw new IllegalArgumentException("/" + path + ":'/targetTable:targetAlias/targetKey' 中 targetAlias 值 " + targetAlias + " 不合法!必须满足英文单词变量名格式!");
- // }
+ matchSingle = matchSingle == false && k.equals(key);
+ if (matchSingle) {
+ continue;
+ }
- targetTable = targetTableKey; // 主表不允许别名
- if (StringUtil.isName(targetTable) == false) {
- throw new IllegalArgumentException("/" + path + ":'/targetTable/targetKey' 中 targetTable 值 " + targetTable + " 不合法!必须满足大写字母开头的表对象英文单词 key 格式!");
- }
+ if (k.length() > 1 && k.indexOf("@") == k.length() - 1 && v instanceof String) {
+ String sv = (String) v;
+ int ind = sv.endsWith("@") ? -1 : sv.indexOf("/");
+ if (ind == 0 && key == null) { // 指定了某个就只允许一个 ON 条件
+ String p = sv.substring(1);
+ int ind2 = p.indexOf("/");
+ String tk = ind2 < 0 ? null : p.substring(0, ind2);
- //对引用的JSONObject添加条件
- try {
- targetObj = request.getJSONObject(targetTableKey);
- }
- catch (Exception e2) {
- throw new IllegalArgumentException("/" + path + ":'/targetTable/targetKey' 中路径对应的 '" + targetTableKey + "':value 中 value 类型不合法!必须是 {} 这种 JSONObject 格式!" + e2.getMessage());
- }
+ apijson.orm.Entry te = tk == null || p.substring(ind2 + 1).indexOf("/") >= 0 ? null : Pair.parseEntry(tk, true);
- if (targetObj == null) {
- throw new IllegalArgumentException("/" + path + ":'/targetTable/targetKey' 中路径对应的对象 '" + targetTableKey + "':{} 不存在或值为 null !必须是 {} 这种 JSONObject 格式!");
- }
+ if (te != null && JSONRequest.isTableKey(te.getKey()) && request.get(tk) instanceof JSONObject) {
+ refObj.put(k, v);
+ continue;
+ }
+ }
- // 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致,生成的 SQL 也就一致 <<<<<<<<<
- // AbstractSQLConfig.newSQLConfig 中强制把 id, id{}, userId, userId{} 放到了最前面 tableObj.put(key, tableObj.remove(key));
+ Object rv = getValueByPath(sv);
+ if (rv != null && rv.equals(sv) == false) {
+ requestObj.put(k.substring(0, k.length() - 1), rv);
+ continue;
+ }
- if (tableObj.size() > 1) { // 把 key 强制放最前,AbstractSQLExcecutor 中 config.putWhere 也是放尽可能最前
- JSONObject newTableObj = new JSONObject(tableObj.size(), true);
- newTableObj.put(key, tableObj.remove(key));
- newTableObj.putAll(tableObj);
+ throw new UnsupportedOperationException(table + "/" + k + " 不合法!" + JSONRequest.KEY_JOIN + " 关联的 Table 中,"
+ + "join: ?/Table/key 时只能有 1 个 key@:value;join: ?/Table 时所有 key@:value 要么是符合 join 格式,要么能直接解析成具体值!"); // TODO 支持 join on
+ }
- tableObj = newTableObj;
- request.put(tableKey, tableObj);
+ if (k.startsWith("@")) {
+ if (JOIN_COPY_KEY_LIST.contains(k)) {
+ requestObj.put(k, v); // 保留
+ }
+ }
+ else {
+ if (k.endsWith("@")) {
+ throw new UnsupportedOperationException(table + "/" + k + " 不合法!" + JSONRequest.KEY_JOIN + " 关联的 Table 中,"
+ + "join: ?/Table/key 时只能有 1 个 key@:value;join: ?/Table 时所有 key@:value 要么是符合 join 格式,要么能直接解析成具体值!"); // TODO 支持 join on
+ }
-// tableObj.clear();
-// tableObj.putAll(newTableObj);
+ if (k.contains("()") == false) { // 不需要远程函数
+ requestObj.put(k, v); // 保留
+ }
+ }
+ }
+
+ Set> refSet = refObj.entrySet();
+ if (refSet.isEmpty()) {
+ throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 alias 值 " + alias + " 不合法!"
+ + "必须为 &/Table0,>>>>>>>>
Join j = new Join();
- j.setPath(path);
- j.setOriginKey(key);
- j.setOriginValue(targetPath);
+ j.setPath(e.getKey());
j.setJoinType(joinType);
j.setTable(table);
j.setAlias(alias);
- j.setTargetTable(targetTable);
- // j.setTargetAlias(targetAlias);
- j.setTargetKey(targetKey);
- j.setKeyAndType(key);
- j.setRequest(getJoinObject(table, tableObj, key));
- j.setOuter((JSONObject) e.getValue());
-
- if (StringUtil.isName(j.getKey()) == false) {
- throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 key@ 中 key 值 " + j.getKey() + " 不合法!必须满足英文单词变量名格式!");
- }
+ j.setOuter((JSONObject) outer);
+ j.setRequest(requestObj);
- joinList.add(j);
-
- // onList.add(table + "." + key + " = " + targetTable + "." + targetKey); // ON User.id = Moment.userId
+ List onList = new ArrayList<>();
+ for (Entry refEntry : refSet) {
+ String originKey = refEntry.getKey();
- }
-
-
- //拼接多个 SQLConfig 的SQL语句,然后执行,再把结果分别缓存(Moment, User等)到 SQLExecutor 的 cacheMap
- // AbstractSQLConfig config0 = null;
- // String sql = "SELECT " + config0.getColumnString() + " FROM " + config0.getTable() + " INNER JOIN " + targetTable + " ON "
- // + onList.get(0) + config0.getGroupString() + config0.getHavingString() + config0.getOrderString();
-
-
- return joinList;
- }
+ String targetPath = (String) refEntry.getValue();
+ if (StringUtil.isEmpty(targetPath, true)) {
+ throw new IllegalArgumentException(e.getKey() + ":value 中 value 值 " + targetPath + " 不合法!必须为引用赋值的路径 '/targetTable/targetKey' !");
+ }
+ // 取出引用赋值路径 targetPath 对应的 Table 和 key
+ index = targetPath.lastIndexOf("/");
+ String targetKey = index < 0 ? null : targetPath.substring(index + 1);
+ if (StringUtil.isName(targetKey) == false) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中 targetKey 值 " + targetKey + " 不合法!必须满足英文单词变量名格式!");
+ }
+ targetPath = targetPath.substring(0, index);
+ index = targetPath.lastIndexOf("/");
+ String targetTableKey = index < 0 ? targetPath : targetPath.substring(index + 1);
- private static final List JOIN_COPY_KEY_LIST;
- static { // TODO 不全
- 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_SCHEMA);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_DATASOURCE);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COLUMN);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COMBINE);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_GROUP);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_ORDER);
- JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_RAW);
- }
+ // 主表允许别名
+ apijson.orm.Entry targetEntry = Pair.parseEntry(targetTableKey, true);
+ String targetTable = targetEntry.getKey(); //User
+ if (StringUtil.isName(targetTable) == false) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中 targetTable 值 " + targetTable + " 不合法!必须满足大写字母开头的表对象英文单词 key 格式!");
+ }
- /**取指定 JSON 对象的 id 集合
- * @param table
- * @param key
- * @param obj
- * @return null ? 全部 : 有限的数组
- */
- private JSONObject getJoinObject(String table, JSONObject obj, String key) {
- if (obj == null || obj.isEmpty()) {
- Log.e(TAG, "getIdList obj == null || obj.isEmpty() >> return null;");
- return null;
- }
- if (StringUtil.isEmpty(key, true)) {
- Log.e(TAG, "getIdList StringUtil.isEmpty(key, true) >> return null;");
- return null;
- }
+ String targetAlias = targetEntry.getValue(); //owner
+ if (StringUtil.isNotEmpty(targetAlias, true) && StringUtil.isName(targetAlias) == false) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable:targetAlias/targetKey' 中 targetAlias 值 " + targetAlias + " 不合法!必须满足英文单词变量名格式!");
+ }
- // 取出所有 join 条件
- JSONObject requestObj = new JSONObject(true); // (JSONObject) obj.clone();
- Set set = new LinkedHashSet<>(obj.keySet());
- for (String k : set) {
- if (StringUtil.isEmpty(k, true)) {
- continue;
- }
+ targetTable = targetTableKey; // 主表允许别名
+ if (StringUtil.isName(targetTable) == false) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中 targetTable 值 " + targetTable + " 不合法!必须满足大写字母开头的表对象英文单词 key 格式!");
+ }
- if (k.startsWith("@")) {
- if (JOIN_COPY_KEY_LIST.contains(k)) {
- requestObj.put(k, obj.get(k)); //保留
+ //对引用的JSONObject添加条件
+ JSONObject targetObj;
+ try {
+ targetObj = request.getJSONObject(targetTableKey);
}
- }
- else {
- if (k.endsWith("@")) {
- if (k.equals(key)) {
- continue;
- }
- throw new UnsupportedOperationException(table + "." + k + " 不合法!" + JSONRequest.KEY_JOIN
- + " 关联的Table中只能有1个 key@:value !"); // TODO 支持 join on
+ catch (Exception e2) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中路径对应的 '" + targetTableKey + "':value 中 value 类型不合法!必须是 {} 这种 JSONObject 格式!" + e2.getMessage());
}
- if (k.contains("()") == false) { //不需要远程函数
- // requestObj.put(k, obj.remove(k)); //remove是为了避免重复查询副表
- requestObj.put(k, obj.get(k)); //remove是为了避免重复查询副表
+ if (targetObj == null) {
+ throw new IllegalArgumentException(e.getKey() + ":'/targetTable/targetKey' 中路径对应的对象 '" + targetTableKey + "':{} 不存在或值为 null !必须是 {} 这种 JSONObject 格式!");
}
+
+ Join.On on = new Join.On();
+ on.setKeyAndType(j.getJoinType(), j.getTable(), originKey);
+ if (StringUtil.isName(on.getKey()) == false) {
+ throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 key@ 中 key 值 " + on.getKey() + " 不合法!必须满足英文单词变量名格式!");
+ }
+
+ on.setOriginKey(originKey);
+ on.setOriginValue((String) refEntry.getValue());
+ on.setTargetTable(targetTable);
+ on.setTargetAlias(targetAlias);
+ on.setTargetKey(targetKey);
+
+ onList.add(on);
}
+
+ j.setOnList(onList);
+
+ joinList.add(j);
+ // onList.add(table + "." + key + " = " + targetTable + "." + targetKey); // ON User.id = Moment.userId
+
+ // 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致,生成的 SQL 也就一致 <<<<<<<<<
+ // AbstractSQLConfig.newSQLConfig 中强制把 id, id{}, userId, userId{} 放到了最前面 tableObj.put(key, tableObj.remove(key));
+
+ if (refObj.size() != tableObj.size()) { // 把 key 强制放最前,AbstractSQLExcecutor 中 config.putWhere 也是放尽可能最前
+ refObj.putAll(tableObj);
+ request.put(tableKey, refObj);
+
+// tableObj.clear();
+// tableObj.putAll(refObj);
+ }
+ // 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致,生成的 SQL 也就一致 >>>>>>>>>
}
+ //拼接多个 SQLConfig 的SQL语句,然后执行,再把结果分别缓存(Moment, User等)到 SQLExecutor 的 cacheMap
+ // AbstractSQLConfig config0 = null;
+ // String sql = "SELECT " + config0.getColumnString() + " FROM " + config0.getTable() + " INNER JOIN " + targetTable + " ON "
+ // + onList.get(0) + config0.getGroupString() + config0.getHavingString() + config0.getOrderString();
- return requestObj;
+ return joinList;
}
+
+
+
@Override
public int getDefaultQueryCount() {
return DEFAULT_QUERY_COUNT;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index c28d07ad1..eb78e1613 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -56,6 +56,7 @@
import apijson.RequestMethod;
import apijson.SQL;
import apijson.StringUtil;
+import apijson.orm.Join.On;
import apijson.orm.exception.NotExistException;
import apijson.orm.model.Access;
import apijson.orm.model.Column;
@@ -83,7 +84,6 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// * 和 / 不能同时出现,防止 /* */ 段注释! # 和 -- 不能出现,防止行注释! ; 不能出现,防止隔断SQL语句!空格不能出现,防止 CRUD,DROP,SHOW TABLES等语句!
private static final Pattern PATTERN_RANGE;
private static final Pattern PATTERN_FUNCTION;
- private static final Pattern PATTERN_STRING;
/**
* 表名映射,隐藏真实表名,对安全要求很高的表可以这么做
@@ -97,10 +97,9 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// 允许调用的 SQL 函数:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL
public static final Map SQL_FUNCTION_MAP;
- static { // 凡是 SQL 边界符、分隔符、注释符 都不允许,例如 ' " ` ( ) ; # -- ,以免拼接 SQL 时被注入意外可执行指令
+ static { // 凡是 SQL 边界符、分隔符、注释符 都不允许,例如 ' " ` ( ) ; # -- /**/ ,以免拼接 SQL 时被注入意外可执行指令
PATTERN_RANGE = Pattern.compile("^[0-9%,!=\\<\\>/\\.\\+\\-\\*\\^]+$"); // ^[a-zA-Z0-9_*%!=<>(),"]+$ 导致 exists(select*from(Comment)) 通过!
PATTERN_FUNCTION = Pattern.compile("^[A-Za-z0-9%,:_@&~`!=\\<\\>\\|\\[\\]\\{\\} /\\.\\+\\-\\*\\^\\?\\(\\)\\$]+$"); //TODO 改成更好的正则,校验前面为单词,中间为操作符,后面为值
- PATTERN_STRING = Pattern.compile("^[,#;\"`]+$");
TABLE_KEY_MAP = new HashMap();
TABLE_KEY_MAP.put(Table.class.getSimpleName(), Table.TABLE_NAME);
@@ -3378,10 +3377,6 @@ public String getJoinString() throws Exception {
List pvl = new ArrayList<>();
boolean changed = false;
- String sql = null;
- SQLConfig jc;
- String jt;
- String tt;
// 主表不用别名 String ta;
for (Join j : joinList) {
if (j.isAppJoin()) { // APP JOIN,只是作为一个标记,执行完主表的查询后自动执行副表的查询 User.id IN($commentIdList)
@@ -3391,18 +3386,20 @@ public String getJoinString() throws Exception {
//LEFT JOIN sys.apijson_user AS User ON User.id = Moment.userId, 都是用 = ,通过relateType处理缓存
// <"INNER JOIN User ON User.id = Moment.userId", UserConfig> TODO AS 放 getSQLTable 内
- jc = j.getJoinConfig();
+ SQLConfig jc = j.getJoinConfig();
jc.setPrepared(isPrepared());
- jt = StringUtil.isEmpty(jc.getAlias(), true) ? jc.getTable() : jc.getAlias();
- tt = j.getTargetTable();
+ String jt = StringUtil.isEmpty(jc.getAlias(), true) ? jc.getTable() : jc.getAlias();
+ List onList = j.getOnList();
//如果要强制小写,则可在子类重写这个方法再 toLowerCase
// if (DATABASE_POSTGRESQL.equals(getDatabase())) {
// jt = jt.toLowerCase();
// tn = tn.toLowerCase();
// }
-
+
+ String sql;
+
switch (type) {
//前面已跳过 case "@": // APP JOIN
// continue;
@@ -3413,9 +3410,17 @@ 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 + " ON " + quote + jt + quote + "." + quote + j.getKey() + quote + " = "
- + quote + tt + quote + "." + quote + j.getTargetKey() + quote;
+ + " JOIN ( " + jc.getSQL(isPrepared()) + " ) AS " + quote + jt + quote;
+
+ if (onList != null) {
+ boolean first = true;
+ for (On on : onList) {
+ sql += (first ? " ON " : " AND ") + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ first = false;
+ }
+ }
+
jc.setMain(false).setKeyPrefix(true);
pvl.addAll(jc.getPreparedValueList());
@@ -3429,8 +3434,15 @@ public String getJoinString() throws Exception {
case "^": // SIDE JOIN: ! (A & B)
case "(": // ANTI JOIN: A & ! B
case ")": // FOREIGN JOIN: B & ! A
- sql = " INNER JOIN " + jc.getTablePath()
- + " ON " + quote + jt + quote + "." + quote + j.getKey() + quote + " = " + quote + tt + quote + "." + quote + j.getTargetKey() + quote;
+ sql = " INNER JOIN " + jc.getTablePath();
+ if (onList != null) {
+ boolean first = true;
+ for (On on : onList) {
+ sql += (first ? " ON " : " AND ") + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ first = false;
+ }
+ }
break;
default:
throw new UnsupportedOperationException(
@@ -3451,7 +3463,7 @@ public String getJoinString() throws Exception {
}
- return joinOns;
+ return StringUtil.isEmpty(joinOns, true) ? "" : joinOns + " \n";
}
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
@@ -3924,12 +3936,20 @@ else if (joinConfig.getDatabase().equals(config.getDatabase()) == false) {
/* SELECT count(*) AS count FROM sys.Moment AS Moment
LEFT JOIN ( SELECT count(*) AS count FROM sys.Comment ) AS Comment ON Comment.momentId = Moment.id LIMIT 1 OFFSET 0 */
if (RequestMethod.isHeadMethod(method, true)) {
- joinConfig.setMethod(GET); //子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
- joinConfig.setColumn(Arrays.asList(j.getKey())); //优化性能,不取非必要的字段
+ List onList = j.getOnList();
+ List column = onList == null ? null : new ArrayList<>(onList.size());
+ if (column != null) {
+ for (On on : onList) {
+ column.add(on.getKey());
+ }
+ }
+
+ joinConfig.setMethod(GET); // 子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
+ joinConfig.setColumn(column); // 优化性能,不取非必要的字段
if (cacheConfig != null) {
- cacheConfig.setMethod(GET); //子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
- cacheConfig.setColumn(Arrays.asList(j.getKey())); //优化性能,不取非必要的字段
+ cacheConfig.setMethod(GET); // 子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
+ cacheConfig.setColumn(column); // 优化性能,不取非必要的字段
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 85a2fc146..51705dca4 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -23,6 +23,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -37,6 +38,7 @@
import apijson.NotNull;
import apijson.RequestMethod;
import apijson.StringUtil;
+import apijson.orm.Join.On;
/**executor for query(read) or update(write) MySQL database
* @author Lemon
@@ -531,7 +533,14 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
if (curJoin != prevJoin) { // 前后字段不在同一个表对象,即便后面出现 null,也不该是主表数据,而是逻辑 bug 导致
SQLConfig viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;
if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
- viceConfig.putWhere(curJoin.getKey(), item.get(curJoin.getTargetKey()), true);
+ List onList = curJoin.getOnList();
+ if (onList != null) {
+ for (On on : onList) {
+ if (on != null) {
+ viceConfig.putWhere(on.getKey(), item.get(on.getTargetKey()), true);
+ }
+ }
+ }
}
String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
@@ -660,25 +669,27 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
jc = join.getJoinConfig();
- //取出 "id@": "@/User/userId" 中所有 userId 的值
- List targetValueList = new ArrayList<>();
- JSONObject mainTable;
- Object targetValue;
+ List onList = join.getOnList();
+ if (onList != null) {
+ for (On on : onList) {
+ //取出 "id@": "@/User/userId" 中所有 userId 的值
+ List targetValueList = new ArrayList<>();
- for (int i = 0; i < resultList.size(); i++) {
- mainTable = resultList.get(i);
- targetValue = mainTable == null ? null : mainTable.get(join.getTargetKey());
+ for (int i = 0; i < resultList.size(); i++) {
+ 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);
+ //替换为 "id{}": [userId1, userId2, userId3...]
+ jc.putWhere(on.getOriginKey(), null, false); // remove orginKey
+ jc.putWhere(on.getKey() + "{}", targetValueList, true); // add orginKey{}
}
}
-
- //替换为 "id{}": [userId1, userId2, userId3...]
- jc.putWhere(join.getOriginKey(), null, false); // remove orginKey
- jc.putWhere(join.getKey() + "{}", targetValueList, true); // add orginKey{}
-
jc.setMain(true).setPreparedValueList(new ArrayList<>());
boolean prepared = jc.isPrepared();
@@ -721,7 +732,6 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
result = new JSONObject(true);
for (int i = 1; i <= length; i++) {
-
result = onPutColumn(jc, rs, rsmd, index, result, i, null, null);
}
@@ -731,7 +741,11 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
+ "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
//缓存到 childMap
- cc.putWhere(join.getKey(), result.get(join.getKey()), true);
+ if (onList != null) {
+ for (On on : onList) {
+ cc.putWhere(on.getKey(), result.get(on.getKey()), true);
+ }
+ }
cacheSql = cc.getSQL(false);
childMap.put(cacheSql, result);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Entry.java b/APIJSONORM/src/main/java/apijson/orm/Entry.java
index 93e9f0418..a8aaf7bfd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Entry.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Entry.java
@@ -5,6 +5,8 @@
package apijson.orm;
+import java.util.Map;
+
/**自定义Entry
* *java.util.Map.Entry是interface,new Entry(...)不好用,其它的Entry也不好用
* @author Lemon
@@ -13,7 +15,7 @@
* @use new Entry(...)
* @warn K,V都需要基本类型时不建议使用,判空麻烦,不如新建一个Model
*/
-public class Entry {
+public class Entry implements Map.Entry {
public K key;
public V value;
@@ -39,8 +41,9 @@ public void setKey(K key) {
public V getValue() {
return value;
}
- public void setValue(V value) {
+ public V setValue(V value) {
this.value = value;
+ return value;
}
public boolean isEmpty() {
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index 7d8707393..24ad36ec8 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -5,6 +5,8 @@
package apijson.orm;
+import java.util.List;
+
import com.alibaba.fastjson.JSONObject;
import apijson.NotNull;
@@ -13,27 +15,21 @@
* @author Lemon
*/
public class Join {
-
+
private String path;
- private String originKey;
- private String originValue;
-
- private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN
- private String relateType; // "" - 一对一, "{}" - 一对多, "<>" - 多对一
- private JSONObject request; // { "id@":"/Moment/userId" }
- private String table; //User
- private String alias; //owner
- private String key; //id
- private String targetTable; // Moment
- private String targetAlias; //main
- private String targetKey; // userId
-
- private JSONObject outter;
+ private String joinType; // "@" - APP, "<" - LEFT, ">" - RIGHT, "*" - CROSS, "&" - INNER, "|" - FULL, "!" - OUTER, "^" - SIDE, "(" - ANTI, ")" - FOREIGN
+ private String table; // User
+ private String alias; // owner
+ private List onList; // ON User.id = Moment.userId AND ...
+
+ private JSONObject request; // { "id@":"/Moment/userId" }
+ private JSONObject outer; // "join": { " getOnList() {
+ return onList;
+ }
+ public void setOnList(List onList) {
+ this.onList = onList;
+ }
+
public JSONObject getRequest() {
return request;
}
public void setRequest(JSONObject request) {
this.request = request;
}
- public String getKey() {
- return key;
- }
- public void setKey(String key) {
- this.key = key;
- }
- public void setTargetTable(String targetTable) {
- this.targetTable = targetTable;
- }
- public String getTargetTable() {
- return targetTable;
- }
- public void setTargetAlias(String targetAlias) {
- this.targetAlias = targetAlias;
- }
- public String getTargetAlias() {
- return targetAlias;
- }
- public String getTargetKey() {
- return targetKey;
- }
- public void setTargetKey(String targetKey) {
- this.targetKey = targetKey;
- }
-
public JSONObject getOuter() {
- return outter;
+ return outer;
}
- public void setOuter(JSONObject outter) {
- this.outter = outter;
+ public void setOuter(JSONObject outer) {
+ this.outer = outer;
}
public SQLConfig getJoinConfig() {
@@ -130,35 +89,11 @@ public SQLConfig getCacheConfig() {
public void setCacheConfig(SQLConfig cacheConfig) {
this.cacheConfig = cacheConfig;
}
-
public SQLConfig getOuterConfig() {
- return outterConfig;
+ return outerConfig;
}
- public void setOuterConfig(SQLConfig outterConfig) {
- this.outterConfig = outterConfig;
- }
-
-
- public void setKeyAndType(@NotNull String originKey) throws Exception { //id, id@, id{}@, contactIdList<>@ ...
- if (originKey.endsWith("@")) {
- originKey = originKey.substring(0, originKey.length() - 1);
- }
- else { //TODO 暂时只允许 User.id = Moment.userId 字段关联,不允许 User.id = 82001 这种
- throw new IllegalArgumentException(joinType + "/.../" + table + "/" + originKey + " 不合法!join:'.../refKey'" + " 中 refKey 必须以 @ 结尾!");
- }
-
- if (originKey.endsWith("{}")) {
- setRelateType("{}");
- setKey(originKey.substring(0, originKey.length() - 2));
- }
- else if (originKey.endsWith("<>")) {
- setRelateType("<>");
- setKey(originKey.substring(0, originKey.length() - 2));
- }
- else {
- setRelateType("");
- setKey(originKey);
- }
+ public void setOuterConfig(SQLConfig outerConfig) {
+ this.outerConfig = outerConfig;
}
@@ -221,5 +156,92 @@ public static boolean isLeftOrRightJoin(Join j) {
return j != null && j.isLeftOrRightJoin();
}
+
+
+ public static class On {
+
+ private String originKey;
+ private String originValue;
+
+ private String relateType; // "" - 一对一, "{}" - 一对多, "<>" - 多对一
+ private String key; // id
+ private String targetTable; // Moment
+ private String targetAlias; // main
+ private String targetKey; // userId
+
+ public String getOriginKey() {
+ return originKey;
+ }
+ public void setOriginKey(String originKey) {
+ this.originKey = originKey;
+ }
+ public String getOriginValue() {
+ return originValue;
+ }
+ public void setOriginValue(String originValue) {
+ this.originValue = originValue;
+ }
+
+
+ public String getRelateType() {
+ return relateType;
+ }
+ public void setRelateType(String relateType) {
+ this.relateType = relateType;
+ }
+
+
+ public String getKey() {
+ return key;
+ }
+ public void setKey(String key) {
+ this.key = key;
+ }
+ public void setTargetTable(String targetTable) {
+ this.targetTable = targetTable;
+ }
+ public String getTargetTable() {
+ return targetTable;
+ }
+ public void setTargetAlias(String targetAlias) {
+ this.targetAlias = targetAlias;
+ }
+ public String getTargetAlias() {
+ return targetAlias;
+ }
+ public String getTargetKey() {
+ return targetKey;
+ }
+ public void setTargetKey(String targetKey) {
+ this.targetKey = targetKey;
+ }
+
+
+ public void setKeyAndType(String joinType, String table, @NotNull String originKey) throws Exception { //id, id@, id{}@, contactIdList<>@ ...
+ if (originKey.endsWith("@")) {
+ originKey = originKey.substring(0, originKey.length() - 1);
+ }
+ else { //TODO 暂时只允许 User.id = Moment.userId 字段关联,不允许 User.id = 82001 这种
+ throw new IllegalArgumentException(joinType + "/.../" + table + "/" + originKey + " 不合法!join:'.../refKey'" + " 中 refKey 必须以 @ 结尾!");
+ }
+
+ if (originKey.endsWith("{}")) {
+ setRelateType("{}");
+ setKey(originKey.substring(0, originKey.length() - 2));
+ }
+ else if (originKey.endsWith("<>")) {
+ setRelateType("<>");
+ setKey(originKey.substring(0, originKey.length() - 2));
+ }
+ else {
+ setRelateType("");
+ setKey(originKey);
+ }
+ }
+
+ }
+
+
+
}
From 5e709edcff434e8dd115dde1c0e7e77bf8aa0405 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 27 Feb 2022 03:05:08 +0800
Subject: [PATCH 059/674] =?UTF-8?q?JOIN=20ON=20=E6=94=AF=E6=8C=81=E5=B8=A6?=
=?UTF-8?q?=E9=9D=9E=E5=BC=95=E7=94=A8=E8=B5=8B=E5=80=BC=E5=85=B3=E8=81=94?=
=?UTF-8?q?=E7=9A=84=E6=99=AE=E9=80=9A=E6=9D=A1=E4=BB=B6?=
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 eb78e1613..ed95a0682 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -3451,8 +3451,20 @@ public String getJoinString() throws Exception {
+ ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
);
}
+
+ SQLConfig oc = j.getOuterConfig();
+ String ow = null;
+ if (oc != null) {
+ oc.setPrepared(isPrepared());
+ oc.setPreparedValueList(new ArrayList<>());
+ oc.setMain(false).setKeyPrefix(true);
+ ow = oc.getWhereString(false);
+
+ pvl.addAll(oc.getPreparedValueList());
+ changed = true;
+ }
- joinOns += " \n " + sql;
+ joinOns += " \n " + sql + (StringUtil.isEmpty(ow, true) ? "" : " AND ( " + ow + " ) ");
}
@@ -3925,7 +3937,7 @@ else if (joinConfig.getDatabase().equals(config.getDatabase()) == false) {
joinConfig.setMain(false).setKeyPrefix(true);
- if (j.isLeftOrRightJoin()) {
+ if (j.getOuter() != null) {
SQLConfig outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
outterConfig.setMain(false).setKeyPrefix(true).setDatabase(joinConfig.getDatabase()).setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
j.setOuterConfig(outterConfig);
From f7b82fd90907b954b9ef90a4bfb8b8f05c72f513 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 27 Feb 2022 04:17:05 +0800
Subject: [PATCH 060/674] =?UTF-8?q?&=20INNER=20JOIN=20=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=8D=95=E7=8B=AC=E8=AE=BE=E7=BD=AE=20JOIN=20=E8=AF=AD?=
=?UTF-8?q?=E5=8F=A5=E4=B8=AD=E7=9A=84=E5=AD=97=E6=AE=B5=E3=80=81=E6=9D=A1?=
=?UTF-8?q?=E4=BB=B6=E3=80=81=E5=88=86=E7=BB=84=E3=80=81=E8=81=9A=E5=90=88?=
=?UTF-8?q?=E3=80=81=E6=8E=92=E5=BA=8F=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
和 < LEFT JOIN, > RIGHT JOIN 一样,例如 "join": { "&/User/id": { "id>": 82001, "@order": "id+" } }
---
.../java/apijson/orm/AbstractSQLConfig.java | 111 +++++++++---------
1 file changed, 57 insertions(+), 54 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index ed95a0682..8e4cac06c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1036,25 +1036,27 @@ public String getGroupString(boolean hasPrefix) {
//加上子表的 group
String joinGroup = "";
if (joinList != null) {
- SQLConfig cfg;
- String c;
boolean first = true;
for (Join j : joinList) {
if (j.isAppJoin()) {
continue;
}
- cfg = j.isLeftOrRightJoin() ? j.getOuterConfig() : j.getJoinConfig();
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getGroup() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
- c = ((AbstractSQLConfig) cfg).getGroupString(false);
- if (StringUtil.isEmpty(c, true) == false) {
- joinGroup += (first ? "" : ", ") + c;
- first = false;
- }
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ cfg.setAlias(cfg.getTable());
+ }
+ String c = ((AbstractSQLConfig) cfg).getGroupString(false);
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinGroup += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
}
}
@@ -1098,25 +1100,27 @@ public String getHavingString(boolean hasPrefix) {
//加上子表的 having
String joinHaving = "";
if (joinList != null) {
- SQLConfig cfg;
- String c;
boolean first = true;
for (Join j : joinList) {
if (j.isAppJoin()) {
continue;
}
- cfg = j.isLeftOrRightJoin() ? j.getOuterConfig() : j.getJoinConfig();
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getHaving() != 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).getHavingString(false);
- c = ((AbstractSQLConfig) cfg).getHavingString(false);
- if (StringUtil.isEmpty(c, true) == false) {
- joinHaving += (first ? "" : ", ") + c;
- first = false;
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinHaving += (first ? "" : ", ") + c;
+ first = false;
+ }
}
-
}
}
@@ -1255,25 +1259,27 @@ public String getOrderString(boolean hasPrefix) {
//加上子表的 order
String joinOrder = "";
if (joinList != null) {
- SQLConfig cfg;
- String c;
boolean first = true;
for (Join j : joinList) {
if (j.isAppJoin()) {
continue;
}
- cfg = j.isLeftOrRightJoin() ? j.getOuterConfig() : j.getJoinConfig();
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
+ SQLConfig ocfg = j.getOuterConfig();
+ SQLConfig cfg = (ocfg != null && ocfg.getOrder() != null) || j.isLeftOrRightJoin() ? ocfg : j.getJoinConfig();
- c = ((AbstractSQLConfig) cfg).getOrderString(false);
- if (StringUtil.isEmpty(c, true) == false) {
- joinOrder += (first ? "" : ", ") + c;
- first = false;
- }
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ cfg.setAlias(cfg.getTable());
+ }
+ String c = ((AbstractSQLConfig) cfg).getOrderString(false);
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinOrder += (first ? "" : ", ") + c;
+ first = false;
+ }
+ }
}
}
@@ -1499,41 +1505,38 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
case GETS:
String joinColumn = "";
if (joinList != null) {
- SQLConfig ecfg;
- SQLConfig cfg;
- String c;
boolean first = true;
for (Join j : joinList) {
if (j.isAppJoin()) {
continue;
}
- if (j.isLeftOrRightJoin()) {
+ SQLConfig ocfg = j.getOuterConfig();
+ boolean isEmpty = ocfg == null || ocfg.getColumn() == null;
+ boolean isLeftOrRightJoin = j.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 + ".*";
first = false;
} else {
- ecfg = j.getOuterConfig();
- if (ecfg != null && ecfg.getColumn() != null) { //优先级更高
- cfg = ecfg;
- }
- else {
- cfg = j.getJoinConfig();
- }
-
- if (StringUtil.isEmpty(cfg.getAlias(), true)) {
- cfg.setAlias(cfg.getTable());
- }
-
- c = ((AbstractSQLConfig) cfg).getColumnString(true);
- if (StringUtil.isEmpty(c, true) == false) {
- joinColumn += (first ? "" : ", ") + c;
- first = false;
+ SQLConfig cfg = isLeftOrRightJoin == false && isEmpty ? j.getJoinConfig() : ocfg;
+ if (cfg != null) {
+ cfg.setMain(false).setKeyPrefix(true);
+ if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ cfg.setAlias(cfg.getTable());
+ }
+
+ String c = ((AbstractSQLConfig) cfg).getColumnString(true);
+ if (StringUtil.isEmpty(c, true) == false) {
+ joinColumn += (first ? "" : ", ") + c;
+ first = false;
+ }
}
}
-
+
inSQLJoin = true;
}
}
From 9d2c95e0653dd4aec7aef022f50b5c7e432a6e25 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 1 Mar 2022 20:30:24 +0800
Subject: [PATCH 061/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8F=90=E9=97=AE?=
=?UTF-8?q?=E6=B3=A8=E6=84=8F=E4=BA=8B=E9=A1=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/orm/AbstractParser.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 3e3703468..020406326 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -649,7 +649,11 @@ public static JSONObject newResult(int code, String msg, boolean isRoot) {
public static JSONObject extendResult(JSONObject object, int code, String msg, boolean isRoot) {
int index = Log.DEBUG == false || isRoot == false || msg == null ? -1 : msg.lastIndexOf(Log.KEY_SYSTEM_INFO_DIVIDER);
String debug = Log.DEBUG == false || isRoot == false ? null : (index >= 0 ? msg.substring(index + Log.KEY_SYSTEM_INFO_DIVIDER.length()).trim()
- : " \n **环境信息** "
+ : " \n 提 bug 请发请求和响应的【完整截屏】,没图的自行解决!"
+ + " \n 开发者有限的时间和精力主要放在【维护项目源码和文档】上!"
+ + " \n 【描述不详细】 或 【文档/常见问题 已有答案】 的问题可能会被忽略!!"
+ + " \n 【态度 不文明/不友善】的可能会被踢出群,问题也可能不予解答!!!"
+ + " \n\n **环境信息** "
+ " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
+ " \n 数据库: DEFAULT_DATABASE = " + AbstractSQLConfig.DEFAULT_DATABASE
+ " \n JDK: " + System.getProperty("java.version") + " " + System.getProperty("os.arch")
From 028093d1fb43d6bc802d3b9aa00846313776f7ab Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 1 Mar 2022 20:34:52 +0800
Subject: [PATCH 062/674] Update Document.md
---
Document.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Document.md b/Document.md
index 9187c4a43..46c090233 100644
--- a/Document.md
+++ b/Document.md
@@ -1,5 +1,5 @@
# APIJSON 通用文档
-本文是通用文档,只和 APIJSON 协议有关,和 C#, Go, Java, JavaScript, Python, PHP 等开发语言无关。
+本文是通用文档,只和 APIJSON 协议有关,和 C#, Go, Java, JavaScript, PHP, Python, TypeScript 等开发语言无关。
具体开发语言相关的 配置、运行、部署 等文档见各个相关项目的文档,可以在首页点击对应语言的入口来查看。
https://github.com/Tencent/APIJSON

@@ -53,7 +53,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 各种单表对象查询:简单查询、统计、分组、排序、聚合、比较、筛选字段、字段别名 等
+ [GIF] APIJSON 各种单表对象查询:简单查询、统计、分组、排序、聚合、比较、筛选字段、字段别名 等

@@ -102,7 +102,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 各种单表数组查询:简单查询、统计、分组、排序、聚合、分页、比较、搜索、正则、条件组合 等
+ [GIF] APIJSON 各种单表数组查询:简单查询、统计、分组、排序、聚合、分页、比较、搜索、正则、条件组合 等

@@ -267,7 +267,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 各种多表关联查询:一对一、一对多、多对一、各种条件 等
+ [GIF] APIJSON 各种多表关联查询:一对一、一对多、多对一、各种条件 等

@@ -275,7 +275,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 各种 JOIN:< LEFT JOIN, & INNER JOIN 等
+ [GIF] APIJSON 各种 JOIN:< LEFT JOIN, & INNER JOIN 等

@@ -283,7 +283,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 各种子查询:@from@ FROM, key@ =, key>@ >, key{}@ IN, key}{@ EXISTS 等
+ [GIF] APIJSON 各种子查询:@from@ FROM, key@ =, key>@ >, key{}@ IN, key}{@ EXISTS 等

@@ -291,7 +291,7 @@ https://github.com/Tencent/APIJSON
- APIJSON 部分功能演示集合,由浅入深、由简单到复杂
+ [GIF] APIJSON 部分功能演示集合,由浅入深、由简单到复杂

From 5328809c2d4cc247f8c9cb6211c6fb509eb742be Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 2 Mar 2022 00:47:55 +0800
Subject: [PATCH 063/674] Update README.md
---
README.md | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index 22390da85..8e8f72995 100644
--- a/README.md
+++ b/README.md
@@ -226,26 +226,26 @@ https://github.com/Tencent/APIJSON/issues/36
如果您在使用 APIJSON,请让我们知道,您的使用对我们非常重要(按登记顺序排列):
https://github.com/Tencent/APIJSON/issues/187
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
* [腾讯科技有限公司](https://www.tencent.com)
From 36a5612f86e9b9b557a62f85a0f1487cc8f51ac0 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 5 Mar 2022 21:11:23 +0800
Subject: [PATCH 064/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=20?=
=?UTF-8?q?NULL=20=E5=80=BC=20@null:"tag"=20=E5=92=8C=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E8=BD=AC=E6=8D=A2=20@cast:"date:DATE"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/JSONObject.java | 37 +-
APIJSONORM/src/main/java/apijson/SQL.java | 7 +-
.../main/java/apijson/orm/AbstractParser.java | 4 +-
.../java/apijson/orm/AbstractSQLConfig.java | 431 ++++++++++++------
.../src/main/java/apijson/orm/SQLConfig.java | 21 +-
5 files changed, 337 insertions(+), 163 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 925735bb9..8000e231b 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -134,6 +134,7 @@ public JSONObject setUserIdIn(List
list) {
// public static final String KEY_KEEP = "@keep"; //一定会返回,为 null 或 空对象时,会使用默认值(非空),解决其它对象因为不关联的第一个对为空导致也不返回
public static final String KEY_DEFULT = "@default"; //TODO 自定义默认值 { "@default":true },@default 可完全替代 @keep
public static final String KEY_NULL = "@null"; //TODO 值为 null 的键值对 "@null":"tag,pictureList",允许 is NULL 条件判断, SET tag = NULL 修改值为 NULL 等
+ public static final String KEY_CAST = "@cast"; //TODO 类型转换 cast(date AS DATE)
public static final String KEY_ROLE = "@role"; //角色,拥有对某些数据的某些操作的权限
public static final String KEY_DATABASE = "@database"; //数据库类型,默认为MySQL
@@ -161,6 +162,8 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST.add(KEY_CACHE);
TABLE_KEY_LIST.add(KEY_COLUMN);
TABLE_KEY_LIST.add(KEY_FROM);
+ TABLE_KEY_LIST.add(KEY_NULL);
+ TABLE_KEY_LIST.add(KEY_CAST);
TABLE_KEY_LIST.add(KEY_COMBINE);
TABLE_KEY_LIST.add(KEY_GROUP);
TABLE_KEY_LIST.add(KEY_HAVING);
@@ -275,15 +278,45 @@ public JSONObject setColumn(String keys) {
return puts(KEY_COLUMN, keys);
}
+ /**set keys whose value is null
+ * @param keys key0, key1, key2 ...
+ * @return {@link #setNull(String)}
+ */
+ public JSONObject setNull(String... keys) {
+ return setNull(StringUtil.getString(keys, true));
+ }
+ /**set keys whose value is null
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setNull(String keys) {
+ return puts(KEY_NULL, keys);
+ }
+
+ /**set keys and types whose value should be cast to type, cast(value AS DATE)
+ * @param keyTypes key0:type0, key1:type1, key2:type2 ...
+ * @return {@link #setCast(String)}
+ */
+ public JSONObject setCast(String... keyTypes) {
+ return setCast(StringUtil.getString(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..."
+ * @return
+ */
+ public JSONObject setCast(String keyTypes) {
+ return puts(KEY_CAST, keyTypes);
+ }
+
/**set combination of keys for conditions
- * @param keys key0,&key1,|key2,!kye3 ...
+ * @param keys key0,&key1,|key2,!key3 ... TODO or key0> | (key1{} & !key2)...
* @return {@link #setColumn(String)}
*/
public JSONObject setCombine(String... keys) {
return setCombine(StringUtil.getString(keys, true));
}
/**set combination of keys for conditions
- * @param keys key0,&key1,|key2,!kye3 ...
+ * @param keys key0,&key1,|key2,!key3 ... TODO or key0> | (key1{} & !key2)...
* @return
*/
public JSONObject setCombine(String keys) {
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index ce55eab29..397c2915f 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -14,8 +14,11 @@ public class SQL {
public static final String AND = " AND ";
public static final String NOT = " NOT ";
public static final String AS = " AS ";
- public static final String IS = " is ";
- public static final String NULL = " null ";
+ public static final String IS = " IS ";
+ public static final String NULL = " NULL ";
+ public static final String IS_NOT = " IS NOT ";
+ public static final String IS_NULL = " IS NULL ";
+ public static final String IS_NOT_NULL = " IS NOT NULL ";
//括号必须紧跟函数名! count (...) 报错!
public static final String COUNT = "count";
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 020406326..5bfa7da93 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1322,6 +1322,8 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_SCHEMA);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_DATASOURCE);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COLUMN);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_NULL);
+ JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_CAST);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COMBINE);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_GROUP);
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_HAVING);
@@ -1330,7 +1332,7 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
}
/**JOIN 多表同时筛选
- * @param join "&/User,0"}
* @param request
* @return
* @throws Exception
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 8e4cac06c..3f0aafcdd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -8,6 +8,8 @@
import static apijson.JSONObject.KEY_CACHE;
import static apijson.JSONObject.KEY_COLUMN;
import static apijson.JSONObject.KEY_COMBINE;
+import static apijson.JSONObject.KEY_NULL;
+import static apijson.JSONObject.KEY_CAST;
import static apijson.JSONObject.KEY_DATABASE;
import static apijson.JSONObject.KEY_DATASOURCE;
import static apijson.JSONObject.KEY_EXPLAIN;
@@ -32,6 +34,11 @@
import static apijson.SQL.NOT;
import static apijson.SQL.OR;
+import java.math.BigDecimal;
+import java.sql.Array;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -738,6 +745,8 @@ public String getUserIdKey() {
private Subquery from; //子查询临时表
private List column; //表内字段名(或函数名,仅查询操作可用)的字符串数组,','分隔
private List> values; //对应表内字段的值的字符串数组,','分隔
+ private List nulls;
+ private Map cast;
private Map content; //Request内容,key:value形式,column = content.keySet(),values = content.values()
private Map where; //筛选条件,key:value形式
private Map> combine; //条件组合,{ "&":[key], "|":[key], "!":[key] }
@@ -1376,6 +1385,10 @@ public SQLConfig setRaw(List raw) {
*/
@Override
public String getRawSQL(String key, Object value) throws Exception {
+ if (value == null) {
+ return null;
+ }
+
List rawList = getRaw();
boolean containRaw = rawList != null && rawList.contains(key);
if (containRaw && value instanceof String == false) {
@@ -1582,7 +1595,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
throw new UnsupportedOperationException("@column:value 的 value 中字符串 " + expression + " 不合法!"
+ "不允许传超过 100 个字符的函数或表达式!请用 @raw 简化传参!");
}
- keys[i] = getColumnPrase(expression, containRaw);
+ keys[i] = parseColumn(expression, containRaw);
}
String c = StringUtil.getString(keys);
@@ -1602,7 +1615,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
* @param expression
* @return
*/
- public String getColumnPrase(String expression, boolean containRaw) {
+ public String parseColumn(String expression, boolean containRaw) {
String quote = getQuote();
int start = expression.indexOf('(');
if (start < 0) {
@@ -2106,17 +2119,29 @@ public static String getLimitString(int page, int count, boolean isTSQL, boolean
return " LIMIT " + count + (offset <= 0 ? "" : " OFFSET " + offset); // DELETE, UPDATE 不支持 OFFSET
}
+
+ @Override
+ public List getNull() {
+ return nulls;
+ }
+ @Override
+ public SQLConfig setNull(List nulls) {
+ this.nulls = nulls;
+ return this;
+ }
- //WHERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@Override
- public Map getWhere() {
- return where;
+ public Map getCast() {
+ return cast;
}
@Override
- public AbstractSQLConfig setWhere(Map where) {
- this.where = where;
+ public SQLConfig setCast(Map cast) {
+ this.cast = cast;
return this;
}
+
+ //WHERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
@NotNull
@Override
public Map> getCombine() {
@@ -2135,6 +2160,17 @@ public AbstractSQLConfig setCombine(Map> combine) {
this.combine = combine;
return this;
}
+
+ @Override
+ public Map getWhere() {
+ return where;
+ }
+ @Override
+ public AbstractSQLConfig setWhere(Map where) {
+ this.where = where;
+ return this;
+ }
+
/**
* noFunctionChar = false
* @param key
@@ -2307,7 +2343,6 @@ else if ("!".equals(ce.getKey())) {
logic = Logic.TYPE_AND;
}
-
isItemFirst = true;
cs = "";
for (String key : keyList) {
@@ -2477,9 +2512,8 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
protected String getWhereItem(String key, Object value, RequestMethod method, boolean verifyName) throws Exception {
Log.d(TAG, "getWhereItem key = " + key);
//避免筛选到全部 value = key == null ? null : where.get(key);
- if (key == null || value == null || key.endsWith("()") || key.startsWith("@")) { //关键字||方法, +或-直接报错
- Log.d(TAG, "getWhereItem key == null || value == null"
- + " || key.startsWith(@) || key.endsWith(()) >> continue;");
+ if (key == null || key.endsWith("()") || key.startsWith("@")) { //关键字||方法, +或-直接报错
+ Log.d(TAG, "getWhereItem key == null || key.endsWith(()) || key.startsWith(@) >> continue;");
return null;
}
if (key.endsWith("@")) {//引用
@@ -2524,63 +2558,64 @@ else if (key.endsWith("<")) {
keyType = 0;
}
- key = getRealKey(method, key, false, true, verifyName);
+ String column = getRealKey(method, key, false, true, verifyName);
switch (keyType) {
case 1:
- return getSearchString(key, value, rawSQL);
+ return getSearchString(key, column, value, rawSQL);
case -2:
case 2:
- return getRegExpString(key, value, keyType < 0, rawSQL);
+ return getRegExpString(key, column, value, keyType < 0, rawSQL);
case 3:
- return getBetweenString(key, value, rawSQL);
+ return getBetweenString(key, column, value, rawSQL);
case 4:
- return getRangeString(key, value, rawSQL);
+ return getRangeString(key, column, value, rawSQL);
case 5:
- return getExistsString(key, value, rawSQL);
+ return getExistsString(key, column, value, rawSQL);
case 6:
- return getContainString(key, value, rawSQL);
+ return getContainString(key, column, value, rawSQL);
case 7:
- return getCompareString(key, value, ">=", rawSQL);
+ return getCompareString(key, column, value, ">=", rawSQL);
case 8:
- return getCompareString(key, value, "<=", rawSQL);
+ return getCompareString(key, column, value, "<=", rawSQL);
case 9:
- return getCompareString(key, value, ">", rawSQL);
+ return getCompareString(key, column, value, ">", rawSQL);
case 10:
- return getCompareString(key, value, "<", rawSQL);
+ return getCompareString(key, column, value, "<", rawSQL);
default: // TODO MySQL JSON类型的字段对比 key='[]' 会无结果! key LIKE '[1, 2, 3]' //TODO MySQL , 后面有空格!
- return getEqualString(key, value, rawSQL);
+ return getEqualString(key, column, value, rawSQL);
}
}
@JSONField(serialize = false)
- public String getEqualString(String key, Object value, String rawSQL) throws Exception {
- if (JSON.isBooleanOrNumberOrString(value) == false && value instanceof Subquery == false) {
+ public String getEqualString(String key, String column, Object value, String rawSQL) throws Exception {
+ if (value != null && JSON.isBooleanOrNumberOrString(value) == false && value instanceof Subquery == false) {
throw new IllegalArgumentException(key + ":value 中value不合法!非PUT请求只支持 [Boolean, Number, String] 内的类型 !");
}
- boolean not = key.endsWith("!"); // & | 没有任何意义,写法多了不好控制
+ boolean not = column.endsWith("!"); // & | 没有任何意义,写法多了不好控制
if (not) {
- key = key.substring(0, key.length() - 1);
+ column = column.substring(0, column.length() - 1);
}
- if (StringUtil.isName(key) == false) {
+ if (StringUtil.isName(column) == false) {
throw new IllegalArgumentException(key + ":value 中key不合法!不支持 ! 以外的逻辑符 !");
}
-
- return getKey(key) + (not ? " != " : " = ") + (value instanceof Subquery ? getSubqueryString((Subquery) value) : (rawSQL != null ? rawSQL : getValue(value)));
+
+ String logic = value == null && rawSQL == null ? (not ? SQL.IS_NOT : SQL.IS) : (not ? " != " : " = ");
+ return getKey(column) + logic + (value instanceof Subquery ? getSubqueryString((Subquery) value) : (rawSQL != null ? rawSQL : getValue(key, column, value)));
}
@JSONField(serialize = false)
- public String getCompareString(String key, Object value, String type, String rawSQL) throws Exception {
- if (JSON.isBooleanOrNumberOrString(value) == false && value instanceof Subquery == false) {
- throw new IllegalArgumentException(key + type + ":value 中value不合法!比较运算 [>, <, >=, <=] 只支持 [Boolean, Number, String] 内的类型 !");
+ public String getCompareString(String key, String column, Object value, String type, String rawSQL) throws Exception {
+ if (value != null && JSON.isBooleanOrNumberOrString(value) == false && value instanceof Subquery == false) {
+ throw new IllegalArgumentException(key + ":value 中 value 不合法!比较运算 [>, <, >=, <=] 只支持 [Boolean, Number, String] 内的类型 !");
}
- if (StringUtil.isName(key) == false) {
- throw new IllegalArgumentException(key + type + ":value 中key不合法!比较运算 [>, <, >=, <=] 不支持 [&, !, |] 中任何逻辑运算符 !");
+ if (StringUtil.isName(column) == false) {
+ throw new IllegalArgumentException(key + ":value 中 key 不合法!比较运算 [>, <, >=, <=] 不支持 [&, !, |] 中任何逻辑运算符 !");
}
- return getKey(key) + " " + type + " " + (value instanceof Subquery ? getSubqueryString((Subquery) value) : (rawSQL != null ? rawSQL : getValue(value)));
+ return getKey(column) + " " + type + " " + (value instanceof Subquery ? getSubqueryString((Subquery) value) : (rawSQL != null ? rawSQL : getValue(key, column, value)));
}
public String getKey(String key) {
@@ -2601,17 +2636,56 @@ public String getSQLKey(String key) {
/**
* 使用prepareStatement预编译,值为 ? ,后续动态set进去
*/
- protected List preparedValueList = new ArrayList<>();
protected Object getValue(@NotNull Object value) {
+ return getValue(null, null, value);
+ }
+
+ protected List preparedValueList = new ArrayList<>();
+ protected Object getValue(String key, String column, Object value) {
if (isPrepared()) {
+ if (value == null) {
+ return null;
+ }
+
+ Map castMap = getCast();
+ String type = key == null || castMap == null ? null : castMap.get(key);
+
+// if ("DATE".equalsIgnoreCase(type) && value instanceof Date == false) {
+// value = value instanceof Number ? new Date(((Number) value).longValue()) : Date.valueOf((String) value);
+// }
+// else if ("TIME".equalsIgnoreCase(type) && value instanceof Time == false) {
+// value = value instanceof Number ? new Time(((Number) value).longValue()) : Time.valueOf((String) value);
+// }
+// else if ("TIMESTAMP".equalsIgnoreCase(type) && value instanceof Timestamp == false) {
+// value = value instanceof Number ? new Timestamp(((Number) value).longValue()) : Timestamp.valueOf((String) value);
+// }
+// else if ("ARRAY".equalsIgnoreCase(type) && value instanceof Array == false) {
+// value = ((Collection>) value).toArray();
+// }
+// else if (StringUtil.isEmpty(type, true) == false) {
+// preparedValueList.add(value);
+// return "cast(?" + SQL.AS + type + ")";
+// }
+
preparedValueList.add(value);
- return "?";
+ return StringUtil.isEmpty(type, true) ? "?" : "cast(?" + SQL.AS + type + ")";
}
- return getSQLValue(value);
+
+ return key == null ? getSQLValue(value) : getSQLValue(key, column, value);
+ }
+
+ public Object getSQLValue(String key, String column, @NotNull Object value) {
+ Map castMap = getCast();
+ String type = key == null || castMap == null ? null : castMap.get(key);
+ Object val = getSQLValue(value);
+ return StringUtil.isEmpty(type, true) ? val : "cast(" + val + SQL.AS + type + ")";
}
public Object getSQLValue(@NotNull Object value) {
+ if (value == null) {
+ return SQL.NULL;
+ }
// return (value instanceof Number || value instanceof Boolean) && DATABASE_POSTGRESQL.equals(getDatabase()) ? value : "'" + value + "'";
- return (value instanceof Number || value instanceof Boolean) ? value : "'" + value + "'"; //MySQL 隐式转换用不了索引
+ return (value instanceof Number || value instanceof Boolean) ? value : "'" + value.toString().replaceAll("'", "\\'") + "'"; //MySQL 隐式转换用不了索引
}
@Override
@@ -2631,7 +2705,7 @@ public AbstractSQLConfig setPreparedValueList(List preparedValueList) {
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getSearchString(String key, Object value, String rawSQL) throws IllegalArgumentException {
+ public String getSearchString(String key, String column, Object value, String rawSQL) throws IllegalArgumentException {
if (rawSQL != null) {
throw new UnsupportedOperationException("@raw:value 中 " + key + " 不合法!@raw 不支持 key$ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !");
}
@@ -2639,15 +2713,15 @@ public String getSearchString(String key, Object value, String rawSQL) throws Il
return "";
}
- Logic logic = new Logic(key);
- key = logic.getKey();
- Log.i(TAG, "getSearchString key = " + key);
+ Logic logic = new Logic(column);
+ column = logic.getKey();
+ Log.i(TAG, "getSearchString column = " + column);
JSONArray arr = newJSONArray(value);
if (arr.isEmpty()) {
return "";
}
- return getSearchString(key, arr.toArray(), logic.getType());
+ return getSearchString(key, column, arr.toArray(), logic.getType());
}
/**search key match values
* @param in
@@ -2655,7 +2729,7 @@ public String getSearchString(String key, Object value, String rawSQL) throws Il
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getSearchString(String key, Object[] values, int type) throws IllegalArgumentException {
+ public String getSearchString(String key, String column, Object[] values, int type) throws IllegalArgumentException {
if (values == null || values.length <= 0) {
return "";
}
@@ -2664,16 +2738,16 @@ public String getSearchString(String key, Object[] values, int type) throws Ille
for (int i = 0; i < values.length; i++) {
Object v = values[i];
if (v instanceof String == false) {
- throw new IllegalArgumentException(key + "$:value 中 value 的类型只能为 String 或 String[]!");
+ throw new IllegalArgumentException(key + ":value 中 value 的类型只能为 String 或 String[]!");
}
if (((String) v).isEmpty()) { // 允许查空格 StringUtil.isEmpty((String) v, true)
- throw new IllegalArgumentException(key + "$:value 中 value 值 " + v + "是空字符串,没有意义,不允许这样传!");
+ throw new IllegalArgumentException(key + ":value 中 value 值 " + v + "是空字符串,没有意义,不允许这样传!");
}
// if (((String) v).contains("%%")) { // 需要通过 %\%% 来模糊搜索 %
// throw new IllegalArgumentException(key + "$:value 中 value 值 " + v + " 中包含 %% !不允许有连续的 % !");
// }
- condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getLikeString(key, v);
+ condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getLikeString(key, column, v);
}
return getCondition(Logic.isNot(type), condition);
@@ -2681,12 +2755,13 @@ public String getSearchString(String key, Object[] values, int type) throws Ille
/**WHERE key LIKE 'value'
* @param key
+ * @param column
* @param value
* @return key LIKE 'value'
*/
@JSONField(serialize = false)
- public String getLikeString(String key, Object value) {
- return getKey(key) + " LIKE " + getValue(value);
+ public String getLikeString(String key, String column, Object value) {
+ return getKey(column) + " LIKE " + getValue(key, column, value);
}
//$ search >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -2696,13 +2771,14 @@ public String getLikeString(String key, Object value) {
//~ regexp <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**search key match RegExp values
* @param key
+ * @param column
* @param value
* @param ignoreCase
* @return {@link #getRegExpString(String, Object[], int, boolean)}
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getRegExpString(String key, Object value, boolean ignoreCase, String rawSQL) throws IllegalArgumentException {
+ public String getRegExpString(String key, String column, Object value, boolean ignoreCase, String rawSQL) throws IllegalArgumentException {
if (rawSQL != null) {
throw new UnsupportedOperationException("@raw:value 中 " + key + " 不合法!@raw 不支持 key~ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !");
}
@@ -2710,15 +2786,15 @@ public String getRegExpString(String key, Object value, boolean ignoreCase, Stri
return "";
}
- Logic logic = new Logic(key);
- key = logic.getKey();
- Log.i(TAG, "getRegExpString key = " + key);
+ Logic logic = new Logic(column);
+ column = logic.getKey();
+ Log.i(TAG, "getRegExpString column = " + column);
JSONArray arr = newJSONArray(value);
if (arr.isEmpty()) {
return "";
}
- return getRegExpString(key, arr.toArray(), logic.getType(), ignoreCase);
+ return getRegExpString(key, column, arr.toArray(), logic.getType(), ignoreCase);
}
/**search key match RegExp values
* @param key
@@ -2729,7 +2805,7 @@ public String getRegExpString(String key, Object value, boolean ignoreCase, Stri
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getRegExpString(String key, Object[] values, int type, boolean ignoreCase) throws IllegalArgumentException {
+ public String getRegExpString(String key, String column, Object[] values, int type, boolean ignoreCase) throws IllegalArgumentException {
if (values == null || values.length <= 0) {
return "";
}
@@ -2737,9 +2813,9 @@ public String getRegExpString(String key, Object[] values, int type, boolean ign
String condition = "";
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof String == false) {
- throw new IllegalArgumentException(key + "$:value 中value的类型只能为String或String[]!");
+ throw new IllegalArgumentException(key + ":value 中value的类型只能为String或String[]!");
}
- condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getRegExpString(key, (String) values[i], ignoreCase);
+ condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getRegExpString(key, column, (String) values[i], ignoreCase);
}
return getCondition(Logic.isNot(type), condition);
@@ -2752,20 +2828,22 @@ public String getRegExpString(String key, Object[] values, int type, boolean ign
* @return key REGEXP 'value'
*/
@JSONField(serialize = false)
- public String getRegExpString(String key, String value, boolean ignoreCase) {
+ public String getRegExpString(String key, String column, String value, boolean ignoreCase) {
if (isPostgreSQL()) {
- return getKey(key) + " ~" + (ignoreCase ? "* " : " ") + getValue(value);
+ return getKey(column) + " ~" + (ignoreCase ? "* " : " ") + getValue(key, column, value);
}
if (isOracle()) {
- return "regexp_like(" + getKey(key) + ", " + getValue(value) + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
+ return "regexp_like(" + getKey(column) + ", " + getValue(key, column, value) + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
}
if (isClickHouse()) {
- return "match(" + (ignoreCase ? "lower(" : "") + getKey(key) + (ignoreCase ? ")" : "") + ", " + (ignoreCase ? "lower(" : "") + getValue(value) + (ignoreCase ? ")" : "") + ")";
+ return "match(" + (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ + ", " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "") + ")";
}
if (isHive()) {
- return (ignoreCase ? "lower(" : "") + getKey(key) + (ignoreCase ? ")" : "") + " REGEXP " + (ignoreCase ? "lower(" : "") + getValue(value) + (ignoreCase ? ")" : "");
+ return (ignoreCase ? "lower(" : "") + getKey(column) + (ignoreCase ? ")" : "")
+ + " REGEXP " + (ignoreCase ? "lower(" : "") + getValue(key, column, value) + (ignoreCase ? ")" : "");
}
- return getKey(key) + " REGEXP " + (ignoreCase ? "" : "BINARY ") + getValue(value);
+ return getKey(column) + " REGEXP " + (ignoreCase ? "" : "BINARY ") + getValue(key, column, value);
}
//~ regexp >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -2780,7 +2858,7 @@ public String getRegExpString(String key, String value, boolean ignoreCase) {
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getBetweenString(String key, Object value, String rawSQL) throws IllegalArgumentException {
+ public String getBetweenString(String key, String column, Object value, String rawSQL) throws IllegalArgumentException {
if (rawSQL != null) {
throw new UnsupportedOperationException("@raw:value 中 " + key + " 不合法!@raw 不支持 key% 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !");
}
@@ -2788,15 +2866,15 @@ public String getBetweenString(String key, Object value, String rawSQL) throws I
return "";
}
- Logic logic = new Logic(key);
- key = logic.getKey();
- Log.i(TAG, "getBetweenString key = " + key);
+ Logic logic = new Logic(column);
+ column = logic.getKey();
+ Log.i(TAG, "getBetweenString column = " + column);
JSONArray arr = newJSONArray(value);
if (arr.isEmpty()) {
return "";
}
- return getBetweenString(key, arr.toArray(), logic.getType());
+ return getBetweenString(key, column, arr.toArray(), logic.getType());
}
/**WHERE key BETWEEN 'start' AND 'end'
@@ -2806,7 +2884,7 @@ public String getBetweenString(String key, Object value, String rawSQL) throws I
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getBetweenString(String key, Object[] values, int type) throws IllegalArgumentException {
+ public String getBetweenString(String key, String column, Object[] values, int type) throws IllegalArgumentException {
if (values == null || values.length <= 0) {
return "";
}
@@ -2815,15 +2893,15 @@ public String getBetweenString(String key, Object[] values, int type) throws Ill
String[] vs;
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof String == false) {
- throw new IllegalArgumentException(key + "%:value 中 value 的类型只能为 String 或 String[] !");
+ throw new IllegalArgumentException(key + ":value 中 value 的类型只能为 String 或 String[] !");
}
vs = StringUtil.split((String) values[i]);
if (vs == null || vs.length != 2) {
- throw new IllegalArgumentException(key + "%:value 中 value 不合法!类型为 String 时必须包括1个逗号 , 且左右两侧都有值!类型为 String[] 里面每个元素要符合前面类型为 String 的规则 !");
+ throw new IllegalArgumentException(key + ":value 中 value 不合法!类型为 String 时必须包括1个逗号 , 且左右两侧都有值!类型为 String[] 里面每个元素要符合前面类型为 String 的规则 !");
}
- condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + "(" + getBetweenString(key, (Object) vs[0], (Object) vs[1]) + ")";
+ condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + "(" + getBetweenString(key, column, (Object) vs[0], (Object) vs[1]) + ")";
}
return getCondition(Logic.isNot(type), condition);
@@ -2836,11 +2914,11 @@ public String getBetweenString(String key, Object[] values, int type) throws Ill
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getBetweenString(String key, Object start, Object end) throws IllegalArgumentException {
+ public String getBetweenString(String key, String column, Object start, Object end) throws IllegalArgumentException {
if (JSON.isBooleanOrNumberOrString(start) == false || JSON.isBooleanOrNumberOrString(end) == false) {
- throw new IllegalArgumentException(key + "%:value 中 value 不合法!类型为 String 时必须包括1个逗号 , 且左右两侧都有值!类型为 String[] 里面每个元素要符合前面类型为 String 的规则 !");
+ throw new IllegalArgumentException(key + ":value 中 value 不合法!类型为 String 时必须包括1个逗号 , 且左右两侧都有值!类型为 String[] 里面每个元素要符合前面类型为 String 的规则 !");
}
- return getKey(key) + " BETWEEN " + getValue(start) + AND + getValue(end);
+ return getKey(column) + " BETWEEN " + getValue(key, column, start) + AND + getValue(key, column, end);
}
@@ -2858,20 +2936,19 @@ public String getBetweenString(String key, Object start, Object end) throws Ille
* @throws Exception
*/
@JSONField(serialize = false)
- public String getRangeString(String key, Object range, String rawSQL) throws Exception {
- Log.i(TAG, "getRangeString key = " + key);
+ public String getRangeString(String key, String column, Object range, String rawSQL) throws Exception {
+ Log.i(TAG, "getRangeString column = " + column);
if (range == null) {//依赖的对象都没有给出有效值,这个存在无意义。如果是客户端传的,那就能在客户端确定了。
- throw new NotExistException(TAG + "getRangeString(" + key + ", " + range
- + ") range == null");
+ throw new NotExistException(TAG + "getRangeString(" + column + ", " + range + ") range == null");
}
- Logic logic = new Logic(key);
+ Logic logic = new Logic(column);
String k = logic.getKey();
Log.i(TAG, "getRangeString k = " + k);
if (range instanceof List) {
if (rawSQL != null) {
- throw new UnsupportedOperationException("@raw:value 的 value 中 " + key + "{} 不合法!"
+ throw new UnsupportedOperationException("@raw:value 的 value 中 " + key + " 不合法!"
+ "Raw SQL 不支持 key{}:[] 这种键值对!");
}
@@ -2880,9 +2957,9 @@ public String getRangeString(String key, Object range, String rawSQL) throws Exc
if (logic.isNot() && l.isEmpty()) {
return ""; // key!{}: [] 这个条件无效,加到 SQL 语句中 key IN() 会报错,getInString 里不好处理
}
- return getKey(k) + getInString(k, l.toArray(), logic.isNot());
+ return getKey(k) + getInString(k, column, l.toArray(), logic.isNot());
}
- throw new IllegalArgumentException(key + "{}\":[] 中 {} 前面的逻辑运算符错误!只能用'|','!'中的一种 !");
+ throw new IllegalArgumentException(key + ":[] 中 {} 前面的逻辑运算符错误!只能用'|','!'中的一种 !");
}
else if (range instanceof String) {//非Number类型需要客户端拼接成 < 'value0', >= 'value1'这种
String condition = "";
@@ -2929,7 +3006,7 @@ else if ("!=null".equals(c)) {
c = SQL.isNull(false);
}
else if (isPrepared() && (c.contains("--") || PATTERN_RANGE.matcher(c).matches() == false)) {
- throw new UnsupportedOperationException(key + "{}:value 的 value 中 " + c + " 不合法!"
+ throw new UnsupportedOperationException(key + ":value 的 value 中 " + c + " 不合法!"
+ "预编译模式下 key{}:\"condition\" 中 condition 必须 为 =null 或 !=null 或 符合正则表达式 " + PATTERN_RANGE + " !不允许连续减号 -- !不允许空格!");
}
@@ -2949,7 +3026,7 @@ else if (range instanceof Subquery) { //如果在 Parser 解析成 SQL 字符串
return getKey(k) + (logic.isNot() ? NOT : "") + " IN " + getSubqueryString((Subquery) range);
}
- throw new IllegalArgumentException(key + "{}:range 类型为" + range.getClass().getSimpleName()
+ throw new IllegalArgumentException(key + ":range 类型为" + range.getClass().getSimpleName()
+ "!range 只能是 用','分隔条件的字符串 或者 可取选项JSONArray!");
}
/**WHERE key IN ('key0', 'key1', ... )
@@ -2958,16 +3035,15 @@ else if (range instanceof Subquery) { //如果在 Parser 解析成 SQL 字符串
* @throws NotExistException
*/
@JSONField(serialize = false)
- public String getInString(String key, Object[] in, boolean not) throws NotExistException {
+ public String getInString(String key, String column, Object[] in, boolean not) throws NotExistException {
String condition = "";
if (in != null) {//返回 "" 会导致 id:[] 空值时效果和没有筛选id一样!
for (int i = 0; i < in.length; i++) {
- condition += ((i > 0 ? "," : "") + getValue(in[i]));
+ condition += ((i > 0 ? "," : "") + getValue(key, column, in[i]));
}
}
if (condition.isEmpty()) {//条件如果存在必须执行,不能忽略。条件为空会导致出错,又很难保证条件不为空(@:条件),所以还是这样好
- throw new NotExistException(TAG + ".getInString(" + key + ", [], " + not
- + ") >> condition.isEmpty() >> IN()");
+ throw new NotExistException(TAG + ".getInString(" + key + "," + column + ", [], " + not + ") >> condition.isEmpty() >> IN()");
}
return (not ? NOT : "") + " IN (" + condition + ")";
}
@@ -2983,7 +3059,7 @@ public String getInString(String key, Object[] in, boolean not) throws NotExistE
* @throws NotExistException
*/
@JSONField(serialize = false)
- public String getExistsString(String key, Object value, String rawSQL) throws Exception {
+ public String getExistsString(String key, String column, Object value, String rawSQL) throws Exception {
if (rawSQL != null) {
throw new UnsupportedOperationException("@raw:value 中 " + key + " 不合法!@raw 不支持 key}{ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !");
}
@@ -2991,13 +3067,13 @@ public String getExistsString(String key, Object value, String rawSQL) throws Ex
return "";
}
if (value instanceof Subquery == false) {
- throw new IllegalArgumentException(key + "}{:subquery 类型为" + value.getClass().getSimpleName()
+ throw new IllegalArgumentException(key + ":subquery 类型为" + value.getClass().getSimpleName()
+ "!subquery 只能是 子查询JSONObejct!");
}
- Logic logic = new Logic(key);
- key = logic.getKey();
- Log.i(TAG, "getExistsString key = " + key);
+ Logic logic = new Logic(column);
+ column = logic.getKey();
+ Log.i(TAG, "getExistsString column = " + column);
return (logic.isNot() ? NOT : "") + " EXISTS " + getSubqueryString((Subquery) value);
}
@@ -3011,21 +3087,18 @@ public String getExistsString(String key, Object value, String rawSQL) throws Ex
* @throws NotExistException
*/
@JSONField(serialize = false)
- public String getContainString(String key, Object value, String rawSQL) throws IllegalArgumentException {
+ public String getContainString(String key, String column, Object value, String rawSQL) throws IllegalArgumentException {
if (rawSQL != null) {
throw new UnsupportedOperationException("@raw:value 中 " + key + " 不合法!@raw 不支持 key<> 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !");
}
- if (value == null) {
- return "";
- }
- Logic logic = new Logic(key);
- key = logic.getKey();
- Log.i(TAG, "getContainString key = " + key);
+ Logic logic = new Logic(column);
+ column = logic.getKey();
+ Log.i(TAG, "getContainString column = " + column);
- return getContainString(key, newJSONArray(value).toArray(), logic.getType());
+ return getContainString(key, column, newJSONArray(value).toArray(), logic.getType());
}
- /**WHERE key contains childs
+ /**WHERE key contains childs TODO 支持 key<>: { "path":"$[0].name", "value": 82001 } 或者 key<$[0].name>:82001 或者 key$[0].name<>:82001 ? 还是前者好,key 一旦复杂了,包含 , ; : / [] 等就容易和 @combine 其它功能等冲突
* @param key
* @param childs null ? "" : (empty ? no child : contains childs)
* @param type |, &, !
@@ -3034,42 +3107,55 @@ public String getContainString(String key, Object value, String rawSQL) throws I
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getContainString(String key, Object[] childs, int type) throws IllegalArgumentException {
+ public String getContainString(String key, String column, Object[] childs, int type) throws IllegalArgumentException {
boolean not = Logic.isNot(type);
String condition = "";
if (childs != null) {
for (int i = 0; i < childs.length; i++) {
Object c = childs[i];
- if (c != null) {
- if (c instanceof JSON) {
- throw new IllegalArgumentException(key + "<>:value 中value类型不能为JSON!");
+ if (c instanceof Collection) {
+ throw new IllegalArgumentException(key + ":value 中 value 类型不能为 [JSONArray, Collection] 中的任何一个 !");
+ }
+
+ Object path = "";
+ if (c instanceof Map) {
+ path = ((Map, ?>) c).get("path");
+ if (path != null && path instanceof String == false) {
+ throw new IllegalArgumentException(key + ":{ path:path, value:value } 中 path 类型错误,只能是 $, $.key1, $[0].key2 等符合 SQL 中 JSON 路径的 String !");
}
-
- condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
- if (isPostgreSQL()) {
- condition += (getKey(key) + " @> " + getValue(newJSONArray(c))); //operator does not exist: jsonb @> character varying "[" + c + "]");
+
+ c = ((Map, ?>) c).get("value");
+ if (c instanceof Collection || c instanceof Map) {
+ throw new IllegalArgumentException(key + ":{ path:path, value:value } 中 value 类型不能为 [JSONObject, JSONArray, Collection, Map] 中的任何一个 !");
}
- else if (isOracle()) {
- condition += ("json_textcontains(" + getKey(key) + ", '$', " + getValue(c.toString()) + ")");
+ }
+
+ condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR));
+ if (isPostgreSQL()) {
+ condition += (getKey(column) + " @> " + getValue(key, column, newJSONArray(c))); //operator does not exist: jsonb @> character varying "[" + c + "]");
+ }
+ else if (isOracle()) {
+ condition += ("json_textcontains(" + getKey(column) + ", " + (StringUtil.isEmpty(path, true) ? "'$'" : getValue(key, column, path)) + ", " + getValue(key, column, c == null ? null : c.toString()) + ")");
+ }
+ else {
+ String v = c == null ? "null" : (c instanceof Boolean || c instanceof Number ? c.toString() : "\"" + c + "\"");
+ if (isClickHouse()) {
+ condition += (condition + "has(JSONExtractArrayRaw(assumeNotNull(" + getKey(column) + "))" + ", " + getValue(key, column, v) + (StringUtil.isEmpty(path, true) ? "" : ", " + getValue(key, column, path)) + ")");
}
else {
- boolean isNum = c instanceof Number;
- String v = (isNum ? "" : "\"") + childs[i] + (isNum ? "" : "\"");
- if (isClickHouse()) {
- condition += condition + "has(JSONExtractArrayRaw(assumeNotNull(" + getKey(key) + "))" + ", " + getValue(v) + ")";
- }
- else {
- condition += ("json_contains(" + getKey(key) + ", " + getValue(v) + ")");
- }
+ condition += ("json_contains(" + getKey(column) + ", " + getValue(key, column, v) + (StringUtil.isEmpty(path, true) ? "" : ", " + getValue(key, column, path)) + ")");
}
}
}
+
if (condition.isEmpty()) {
- condition = (getKey(key) + SQL.isNull(true) + OR + getLikeString(key, "[]")); // key = '[]' 无结果!
- } else {
- condition = (getKey(key) + SQL.isNull(false) + AND + "(" + condition + ")");
+ condition = getKey(column) + SQL.isNull(true) + OR + getLikeString(key, column, "[]"); // key = '[]' 无结果!
+ }
+ else {
+ condition = getKey(column) + SQL.isNull(false) + AND + "(" + condition + ")";
}
}
+
if (condition.isEmpty()) {
return "";
}
@@ -3083,6 +3169,10 @@ else if (isOracle()) {
@Override
public String getSubqueryString(Subquery subquery) throws Exception {
+ if (subquery == null) {
+ return "";
+ }
+
String range = subquery.getRange();
SQLConfig cfg = subquery.getConfig();
@@ -3171,10 +3261,10 @@ public String getSetString(RequestMethod method, Map content, bo
keyType = 0; //注意重置类型,不然不该加减的字段会跟着加减
}
value = entry.getValue();
- key = getRealKey(method, key, false, true, verifyName);
+ String column = getRealKey(method, key, false, true, verifyName);
- setString += (isFirst ? "" : ", ") + (getKey(key) + " = " + (keyType == 1 ? getAddString(key, value) : (keyType == 2
- ? getRemoveString(key, value) : getValue(value)) ) );
+ setString += (isFirst ? "" : ", ") + (getKey(column) + " = " + (keyType == 1 ? getAddString(key, column, value) : (keyType == 2
+ ? getRemoveString(key, column, value) : getValue(key, column, value)) ) );
isFirst = false;
}
@@ -3193,14 +3283,14 @@ public String getSetString(RequestMethod method, Map content, bo
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getAddString(String key, Object value) throws IllegalArgumentException {
+ public String getAddString(String key, String column, Object value) throws IllegalArgumentException {
if (value instanceof Number) {
- return getKey(key) + " + " + value;
+ return getKey(column) + " + " + value;
}
if (value instanceof String) {
- return SQL.concat(getKey(key), (String) getValue(value));
+ return SQL.concat(getKey(column), (String) getValue(key, column, value));
}
- throw new IllegalArgumentException(key + "+ 对应的值 " + value + " 不是Number,String,Array中的任何一种!");
+ throw new IllegalArgumentException(key + ":value 中 value 类型错误,必须是 Number,String,Array 中的任何一种!");
}
/**SET key = replace(key, 'value', '')
* @param key
@@ -3209,14 +3299,14 @@ public String getAddString(String key, Object value) throws IllegalArgumentExcep
* @throws IllegalArgumentException
*/
@JSONField(serialize = false)
- public String getRemoveString(String key, Object value) throws IllegalArgumentException {
+ public String getRemoveString(String key, String column, Object value) throws IllegalArgumentException {
if (value instanceof Number) {
- return getKey(key) + " - " + value;
+ return getKey(column) + " - " + value;
}
if (value instanceof String) {
- return SQL.replace(getKey(key), (String) getValue(value), "''");// " replace(" + key + ", '" + value + "', '') ";
+ return SQL.replace(getKey(column), (String) getValue(key, column, value), "''");// " replace(" + column + ", '" + value + "', '') ";
}
- throw new IllegalArgumentException(key + "- 对应的值 " + value + " 不是Number,String,Array中的任何一种!");
+ throw new IllegalArgumentException(key + ":value 中 value 类型错误,必须是 Number,String,Array 中的任何一种!");
}
//SET >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -3359,6 +3449,7 @@ private static String getConditionString(String column, String table, AbstractSQ
// return table + " AS t0 INNER JOIN (SELECT id FROM " + condition + ") AS t1 ON t0.id = t1.id";
}
+
private boolean keyPrefix;
@Override
public boolean isKeyPrefix() {
@@ -3371,7 +3462,6 @@ public AbstractSQLConfig setKeyPrefix(boolean keyPrefix) {
}
-
public String getJoinString() throws Exception {
String joinOns = "";
@@ -3604,9 +3694,11 @@ else if (id instanceof Subquery) {}
String role = request.getString(KEY_ROLE);
String cache = request.getString(KEY_CACHE);
- String combine = request.getString(KEY_COMBINE);
Subquery from = (Subquery) request.get(KEY_FROM);
String column = request.getString(KEY_COLUMN);
+ String nulls = request.getString(KEY_NULL);
+ String cast = request.getString(KEY_CAST);
+ String combine = request.getString(KEY_COMBINE);
String group = request.getString(KEY_GROUP);
String having = request.getString(KEY_HAVING);
String order = request.getString(KEY_ORDER);
@@ -3624,14 +3716,52 @@ else if (id instanceof Subquery) {}
request.remove(KEY_DATASOURCE);
request.remove(KEY_DATABASE);
request.remove(KEY_SCHEMA);
- request.remove(KEY_COMBINE);
request.remove(KEY_FROM);
request.remove(KEY_COLUMN);
+ request.remove(KEY_NULL);
+ request.remove(KEY_CAST);
+ request.remove(KEY_COMBINE);
request.remove(KEY_GROUP);
request.remove(KEY_HAVING);
request.remove(KEY_ORDER);
request.remove(KEY_RAW);
request.remove(KEY_JSON);
+
+ String[] nullKeys = StringUtil.split(nulls);
+ if (nullKeys != null && nullKeys.length > 0) {
+ for (String nk : nullKeys) {
+ if (StringUtil.isEmpty(nk, true)) {
+ throw new IllegalArgumentException(table + ":{} 里的 @null: value 中的字符 '" + nk + "' 不合法!不允许为空!");
+ }
+ if (request.get(nk) != null) {
+ throw new IllegalArgumentException(table + ":{} 里的 @null: value 中的字符 '" + nk + "' 已在当前对象有非 null 值!不允许对同一个 JSON key 设置不同值!");
+ }
+
+ request.put(nk, null);
+ }
+ }
+
+ String[] casts = StringUtil.split(cast);
+ Map castMap = null;
+ if (casts != null && casts.length > 0) {
+ castMap = new HashMap<>(casts.length);
+ for (String c : casts) {
+ apijson.orm.Entry p = Pair.parseEntry(c);
+
+ if (StringUtil.isEmpty(p.getKey(), true)) {
+ throw new IllegalArgumentException(table + ":{} 里的 @cast: 'key0:type0,key1:type1..' 中 '" + c + "' 对应的 key 的字符 '" + p.getKey() + "' 不合法!不允许为空!");
+ }
+ if (StringUtil.isName(p.getValue()) == false) {
+ throw new IllegalArgumentException(table + ":{} 里的 @cast: 'key0:type0,key1:type1..' 中 '" + c + "' 对应的 type 的字符 '" + p.getValue() + "' 不合法!必须符合类型名称格式!");
+ }
+ if (castMap.get(p.getKey()) != null) {
+ throw new IllegalArgumentException(table + ":{} 里的 @cast: 'key0:type0,key1:type1..' 中 '" + c + "' 对应的 key 的字符 '" + p.getKey() + "' 已存在!不允许重复设置类型!");
+ }
+
+ castMap.put(p.getKey(), p.getValue());
+ }
+ }
+
String[] rawArr = StringUtil.split(raw);
config.setRaw(rawArr == null || rawArr.length <= 0 ? null : new ArrayList<>(Arrays.asList(rawArr)));
@@ -3642,7 +3772,7 @@ else if (id instanceof Subquery) {}
Set set = request.keySet(); //前面已经判断request是否为空
if (method == POST) { //POST操作
if (idIn != null) {
- throw new IllegalArgumentException("POST 请求中不允许传 " + idInKey + " !");
+ throw new IllegalArgumentException(table + ":{} 里的 " + idInKey + ": value 不合法!POST 请求中不允许传 " + idInKey + " !");
}
if (set != null && set.isEmpty() == false) { //不能直接return,要走完下面的流程
@@ -3841,26 +3971,27 @@ else if (whereList != null && whereList.contains(key)) {
}
}
}
+
config.setExplain(explain);
config.setCache(getCache(cache));
- config.setFrom(from);
config.setDistinct(distinct);
config.setColumn(column == null ? null : cs); //解决总是 config.column != null,总是不能得到 *
- config.setWhere(tableWhere);
+ config.setFrom(from);
+ config.setRole(role);
config.setId(id);
//在 tableWhere 第0个 config.setIdIn(idIn);
- config.setRole(role);
+ config.setNull(nullKeys == null || nullKeys.length <= 0 ? null : new ArrayList<>(Arrays.asList(nullKeys)));
+ config.setCast(castMap);
+ config.setWhere(tableWhere);
config.setGroup(group);
config.setHaving(having);
config.setOrder(order);
- String[] jsonArr = StringUtil.split(json);
- config.setJson(jsonArr == null || jsonArr.length <= 0 ? null : new ArrayList<>(Arrays.asList(jsonArr)));
-
- //TODO 解析JOIN,包括 @column,@group 等要合并
+ String[] jsons = StringUtil.split(json);
+ config.setJson(jsons == null || jsons.length <= 0 ? null : new ArrayList<>(Arrays.asList(jsons)));
}
finally {//后面还可能用到,要还原
@@ -3876,9 +4007,11 @@ else if (whereList != null && whereList.contains(key)) {
request.put(KEY_CACHE, cache);
request.put(KEY_DATASOURCE, datasource);
request.put(KEY_SCHEMA, schema);
- request.put(KEY_COMBINE, combine);
request.put(KEY_FROM, from);
request.put(KEY_COLUMN, column);
+ request.put(KEY_NULL, nulls);
+ request.put(KEY_CAST, cast);
+ request.put(KEY_COMBINE, combine);
request.put(KEY_GROUP, group);
request.put(KEY_HAVING, having);
request.put(KEY_ORDER, order);
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 83eddb301..aca544111 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -131,6 +131,9 @@ public interface SQLConfig {
String getQuote();
+ List getJson();
+ SQLConfig setJson(List json);
+
/**请求传进来的Table名
* @return
* @see {@link #getSQLTable()}
@@ -150,7 +153,6 @@ public interface SQLConfig {
List getRaw();
SQLConfig setRaw(List raw);
-
String getGroup();
SQLConfig setGroup(String group);
@@ -160,9 +162,6 @@ public interface SQLConfig {
String getOrder();
SQLConfig setOrder(String order);
- List getJson();
- SQLConfig setJson(List json);
-
Subquery getFrom();
SQLConfig setFrom(Subquery from);
@@ -175,13 +174,17 @@ public interface SQLConfig {
Map getContent();
SQLConfig setContent(Map content);
- Map getWhere();
- SQLConfig setWhere(Map where);
-
Map> getCombine();
SQLConfig setCombine(Map> combine);
-
-
+
+ Map getCast();
+ SQLConfig setCast(Map cast);
+
+ List getNull();
+ SQLConfig setNull(List nulls);
+
+ Map getWhere();
+ SQLConfig setWhere(Map where);
/**
* exactMatch = false
From 2cc13dab41f658f729eb34684bd6c8f64d8fd0c2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 02:41:09 +0800
Subject: [PATCH 065/674] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88?=
=?UTF-8?q?=EF=BC=9A=E8=A7=A3=E5=86=B3=20@combine:"name*~,tag&$"=20?=
=?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=BC=82=E5=B8=B8=EF=BC=8C=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=20@combine:"name*~=20|=20tag&$"=20=E8=BF=99=E7=A7=8D=E6=9C=80?=
=?UTF-8?q?=E5=90=8E=E6=B2=A1=E6=9C=89=E6=8B=AC=E5=8F=B7=E7=9A=84=E8=A7=A3?=
=?UTF-8?q?=E6=9E=90=E5=90=8E=E7=BC=BA=E5=B0=91=E6=9C=80=E5=90=8E=E7=9A=84?=
=?UTF-8?q?=E6=9D=A1=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 461 +++++++++++++++---
.../src/main/java/apijson/orm/SQLConfig.java | 3 +
2 files changed, 392 insertions(+), 72 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 3f0aafcdd..cb4b0a981 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -6,10 +6,9 @@
package apijson.orm;
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_NULL;
-import static apijson.JSONObject.KEY_CAST;
import static apijson.JSONObject.KEY_DATABASE;
import static apijson.JSONObject.KEY_DATASOURCE;
import static apijson.JSONObject.KEY_EXPLAIN;
@@ -18,6 +17,7 @@
import static apijson.JSONObject.KEY_HAVING;
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_RAW;
import static apijson.JSONObject.KEY_ROLE;
@@ -34,16 +34,14 @@
import static apijson.SQL.NOT;
import static apijson.SQL.OR;
-import java.math.BigDecimal;
-import java.sql.Array;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Deque;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -750,7 +748,7 @@ public String getUserIdKey() {
private Map content; //Request内容,key:value形式,column = content.keySet(),values = content.values()
private Map where; //筛选条件,key:value形式
private Map> combine; //条件组合,{ "&":[key], "|":[key], "!":[key] }
-
+ private String combineExpression;
//array item <<<<<<<<<<
private int count; //Table数量
@@ -2142,6 +2140,16 @@ public SQLConfig setCast(Map cast) {
//WHERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+ @Override
+ public String getCombineExpression() {
+ return combineExpression;
+ }
+ @Override
+ public AbstractSQLConfig setCombineExpression(String combineExpression) {
+ this.combineExpression = combineExpression;
+ return this;
+ }
+
@NotNull
@Override
public Map> getCombine() {
@@ -2300,7 +2308,11 @@ else if (key.equals(userIdInKey)) {
@JSONField(serialize = false)
@Override
public String getWhereString(boolean hasPrefix) throws Exception {
- return getWhereString(hasPrefix, getMethod(), getWhere(), getCombine(), getJoinList(), ! isTest());
+ String ce = getCombineExpression();
+ if (StringUtil.isEmpty(ce, true)) {
+ return getWhereString(hasPrefix, getMethod(), getWhere(), getCombine(), getJoinList(), ! isTest());
+ }
+ return getWhereString(hasPrefix, getMethod(), getWhere(), ce, getJoinList(), ! isTest());
}
/**获取WHERE
* @param method
@@ -2309,6 +2321,301 @@ public String getWhereString(boolean hasPrefix) throws Exception {
* @throws Exception
*/
@JSONField(serialize = false)
+ public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, String combine, List joinList, boolean verifyName) throws Exception {
+ String s = StringUtil.getString(combine);
+ if (s.startsWith(" ") || s.endsWith(" ") ) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s
+ + "' 不合法!不允许首尾有空格,也不允许连续空格!空格不能多也不能少!"
+ + "逻辑连接符 & | 左右必须各一个相邻空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
+ }
+
+
+ String whereString = "";
+
+ int depth = 0;
+ int n = s.length();
+ int i = 0;
+
+ char lastLogic = 0;
+ char last = 0;
+ boolean first = true;
+
+ String key = "";
+ Set usedKeySet = new HashSet<>(where.size());
+ while (i < n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
+ char c = s.charAt(i);
+ boolean isLast = i >= n - 1;
+ boolean isBlankOrRightParenthesis = c == ' ' || c == ')';
+ if (isLast || isBlankOrRightParenthesis) {
+ if (isBlankOrRightParenthesis == false) {
+ key += c;
+ }
+
+ boolean isEmpty = StringUtil.isEmpty(key, true);
+ if (isEmpty && last != ')') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ + "' 不合法!" + (c == ' ' ? "空格 ' ' " : "右括号 ')'") + " 左边缺少条件 key !逻辑连接符 & | 左右必须各一个相邻空格!"
+ + "空格不能多也不能少!不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
+
+ if (isEmpty == false) {
+ if (first == false && lastLogic <= 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i - key.length()) + "' 不合法!左边缺少 & | 其中一个逻辑连接符!");
+ }
+
+ Object value = where.get(key);
+ if (value == null) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + key + "' 对应的条件键值对 " + key + ":value 不存在!");
+ }
+
+ String wi = getWhereItem(key, value, method, verifyName);
+ if (StringUtil.isEmpty(wi, true)) { // 转成 1=1 ?
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + key + "' 对应的 " + key + ":value 不是有效条件键值对!");
+ }
+
+ usedKeySet.add(key);
+ whereString += "( " + wi + " )";
+ first = false;
+ }
+
+ key = "";
+
+ if (isLast) {
+ break;
+ }
+ }
+
+ if (c == ' ') {
+ }
+ else if (c == '&') {
+ if (last == ' ') {
+ if (i >= n || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!逻辑连接符 & 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
+
+ whereString += SQL.AND;
+ lastLogic = c;
+ i ++;
+ }
+ else if (isLast == false) {
+ key += c;
+ }
+ }
+ else if (c == '|') {
+ if (last == ' ') {
+ if (i >= n || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!逻辑连接符 | 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
+ }
+
+ whereString += SQL.OR;
+ lastLogic = c;
+ i ++;
+ }
+ else if (isLast == false) {
+ key += c;
+ }
+ }
+ else if (c == '(') {
+ if (key.isEmpty() == false || (i > 0 && lastLogic <= 0)) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i) + "' 不合法!左边缺少 & | 逻辑连接符!");
+ }
+
+ depth ++;
+ whereString += c;
+ lastLogic = 0;
+ first = true;
+ }
+ else if (c == ')') {
+ depth --;
+ if (depth < 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1) + "' 不合法!左括号 ( 比 右括号 ) 少!数量必须相等从而完整闭合 (...) !");
+ }
+
+ whereString += c;
+ lastLogic = 0;
+ }
+ else if (isLast == false) {
+ key += c;
+ }
+
+ last = c;
+ i ++;
+
+ if (i >= n) {
+ i = n - 1;
+ }
+ }
+
+ if (depth != 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!左括号 ( 比 右括号 ) 多!数量必须相等从而完整闭合 (...) !");
+ }
+
+ Set> set = where.entrySet();
+
+ String andWhere = "";
+ boolean isItemFirst = true;
+
+ for (Entry entry : set) {
+ key = entry == null ? null : entry.getKey();
+ if (key == null || usedKeySet.contains(key)) {
+ continue;
+ }
+
+ String wi = getWhereItem(key, where.get(key), method, verifyName);
+ if (StringUtil.isEmpty(wi, true)) {//避免SQL条件连接错误
+ continue;
+ }
+
+ andWhere += (isItemFirst ? "" : AND) + "(" + wi + ")";
+ isItemFirst = false;
+ }
+
+ if (StringUtil.isEmpty(whereString, true)) {
+ whereString = andWhere;
+ }
+ else if (StringUtil.isNotEmpty(andWhere, true)) {
+ whereString = andWhere + AND + "( " + whereString + " )";
+ }
+
+ if (joinList != null) {
+
+ String newWs = "";
+ String ws = whereString;
+
+ List newPvl = new ArrayList<>();
+ List pvl = new ArrayList<>(preparedValueList);
+
+ SQLConfig jc;
+ String js;
+
+ boolean changed = false;
+ //各种 JOIN 没办法统一用 & | !连接,只能按优先级,和 @combine 一样?
+ for (Join j : joinList) {
+ String jt = j.getJoinType();
+
+ switch (jt) {
+ case "*": // CROSS JOIN
+ case "@": // APP JOIN
+ case "<": // LEFT JOIN
+ case ">": // RIGHT JOIN
+ break;
+
+ case "&": // INNER JOIN: A & B
+ case "": // FULL JOIN: A | B
+ case "|": // FULL JOIN: A | B
+ case "!": // OUTER JOIN: ! (A | B)
+ case "^": // SIDE JOIN: ! (A & B)
+ case "(": // ANTI JOIN: A & ! B
+ case ")": // FOREIGN JOIN: B & ! A
+ jc = j.getJoinConfig();
+ boolean isMain = jc.isMain();
+ jc.setMain(false).setPrepared(isPrepared()).setPreparedValueList(new ArrayList());
+ js = jc.getWhereString(false);
+ jc.setMain(isMain);
+
+ boolean isOuterJoin = "!".equals(jt);
+ boolean isSideJoin = "^".equals(jt);
+ boolean isAntiJoin = "(".equals(jt);
+ boolean isForeignJoin = ")".equals(jt);
+ boolean isWsEmpty = StringUtil.isEmpty(ws, true);
+
+ if (isWsEmpty) {
+ if (isOuterJoin) { // ! OUTER JOIN: ! (A | B)
+ throw new NotExistException("no result for ! OUTER JOIN( ! (A | B) ) when A or B is empty!");
+ }
+ if (isForeignJoin) { // ) FOREIGN JOIN: B & ! A
+ throw new NotExistException("no result for ) FOREIGN JOIN( B & ! A ) when A is empty!");
+ }
+ }
+
+ if (StringUtil.isEmpty(js, true)) {
+ if (isOuterJoin) { // ! OUTER JOIN: ! (A | B)
+ throw new NotExistException("no result for ! OUTER JOIN( ! (A | B) ) when A or B is empty!");
+ }
+ if (isAntiJoin) { // ( ANTI JOIN: A & ! B
+ throw new NotExistException("no result for ( ANTI JOIN( A & ! B ) when B is empty!");
+ }
+
+ if (isWsEmpty) {
+ if (isSideJoin) {
+ throw new NotExistException("no result for ^ SIDE JOIN( ! (A & B) ) when both A and B are empty!");
+ }
+ }
+ else {
+ if (isSideJoin || isForeignJoin) {
+ newWs += " ( " + getCondition(true, ws) + " ) ";
+
+ newPvl.addAll(pvl);
+ newPvl.addAll(jc.getPreparedValueList());
+ changed = true;
+ }
+ }
+
+ continue;
+ }
+
+ if (StringUtil.isEmpty(newWs, true) == false) {
+ newWs += AND;
+ }
+
+ if (isAntiJoin) { // ( ANTI JOIN: A & ! B
+ newWs += " ( " + ( isWsEmpty ? "" : ws + AND ) + NOT + " ( " + js + " ) " + " ) ";
+ }
+ else if (isForeignJoin) { // ) FOREIGN JOIN: (! A) & B // preparedValueList.add 不好反过来 B & ! A
+ newWs += " ( " + NOT + " ( " + ws + " ) ) " + AND + " ( " + js + " ) ";
+ }
+ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
+ //MySQL 因为 NULL 值处理问题,(A & ! B) | (B & ! A) 与 ! (A & B) 返回结果不一样,后者往往更多
+ newWs += " ( " + getCondition(
+ true,
+ ( isWsEmpty ? "" : ws + AND ) + " ( " + js + " ) "
+ ) + " ) ";
+ }
+ else { // & INNER JOIN: A & B; | FULL JOIN: A | B; OUTER JOIN: ! (A | B)
+ int logic = Logic.getType(jt);
+ newWs += " ( "
+ + getCondition(
+ Logic.isNot(logic),
+ ws
+ + ( isWsEmpty ? "" : (Logic.isAnd(logic) ? AND : OR) )
+ + " ( " + js + " ) "
+ )
+ + " ) ";
+ }
+
+ newPvl.addAll(pvl);
+ newPvl.addAll(jc.getPreparedValueList());
+
+ changed = true;
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
+ + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
+ );
+ }
+ }
+
+ if (changed) {
+ whereString = newWs;
+ preparedValueList = newPvl;
+ }
+ }
+
+ String result = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
+
+ if (result.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
+ throw new UnsupportedOperationException("写操作请求必须带条件!!!");
+ }
+
+ return result;
+ }
+
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, Map> combine, List joinList, boolean verifyName) throws Exception {
Set>> combineSet = combine == null ? null : combine.entrySet();
if (combineSet == null || combineSet.isEmpty()) {
@@ -2353,7 +2660,6 @@ else if ("!".equals(ce.getKey())) {
}
cs += (isItemFirst ? "" : (Logic.isAnd(logic) ? AND : OR)) + "(" + c + ")";
-
isItemFirst = false;
}
@@ -3813,74 +4119,80 @@ else if (id instanceof Subquery) {}
//条件<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
List whereList = null;
- Map> combineMap = new LinkedHashMap<>();
- List andList = new ArrayList<>();
- List orList = new ArrayList<>();
- List notList = new ArrayList<>();
-
- //强制作为条件且放在最前面优化性能
- if (id != null) {
- tableWhere.put(idKey, id);
- andList.add(idKey);
- }
- if (idIn != null) {
- tableWhere.put(idInKey, idIn);
- andList.add(idInKey);
- }
-
String[] ws = StringUtil.split(combine);
- if (ws != null) {
- if (method == DELETE || method == GETS || method == HEADS) {
- throw new IllegalArgumentException("DELETE,GETS,HEADS 请求不允许传 @combine:value !");
+ String combineExpression = ws == null || ws.length != 1 ? null : ws[0];
+
+ Map> combineMap = StringUtil.isNotEmpty(combineExpression, true) ? null : new LinkedHashMap<>();
+ List andList = combineMap == null ? null : new ArrayList<>();
+ List orList = combineMap == null ? null : new ArrayList<>();
+ List notList = combineMap == null ? null : new ArrayList<>();
+
+ if (combineMap != null) {
+ //强制作为条件且放在最前面优化性能
+ if (id != null) {
+ tableWhere.put(idKey, id);
+ andList.add(idKey);
}
- whereList = new ArrayList<>();
-
- String w;
- for (int i = 0; i < ws.length; i++) { //去除 &,|,! 前缀
- w = ws[i];
- if (w != null) {
- if (w.startsWith("&")) {
- w = w.substring(1);
- andList.add(w);
- }
- else if (w.startsWith("|")) {
- if (method == PUT) {
- throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
- + "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
+ if (idIn != null) {
+ tableWhere.put(idInKey, idIn);
+ andList.add(idInKey);
+ }
+
+
+ if (ws != null) {
+ if (method == DELETE || method == GETS || method == HEADS) {
+ throw new IllegalArgumentException("DELETE,GETS,HEADS 请求不允许传 @combine:value !");
+ }
+ whereList = new ArrayList<>();
+
+ String w;
+ for (int i = 0; i < ws.length; i++) { //去除 &,|,! 前缀
+ w = ws[i];
+ if (w != null) {
+ if (w.startsWith("&")) {
+ w = w.substring(1);
+ andList.add(w);
}
- w = w.substring(1);
- orList.add(w);
- }
- else if (w.startsWith("!")) {
- if (method == PUT) {
- throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
- + "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
+ else if (w.startsWith("|")) {
+ if (method == PUT) {
+ throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
+ + "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
+ }
+ w = w.substring(1);
+ orList.add(w);
+ }
+ else if (w.startsWith("!")) {
+ if (method == PUT) {
+ throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
+ + "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
+ }
+ w = w.substring(1);
+ notList.add(w);
+ }
+ else {
+ orList.add(w);
}
- w = w.substring(1);
- notList.add(w);
- }
- else {
- orList.add(w);
- }
- if (w.isEmpty()) {
- throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!不允许为空值!");
- }
- else {
- if (idKey.equals(w) || idInKey.equals(w) || userIdKey.equals(w) || userIdInKey.equals(w)) {
- throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 不合法!"
- + "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
+ if (w.isEmpty()) {
+ throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!不允许为空值!");
+ }
+ else {
+ if (idKey.equals(w) || idInKey.equals(w) || userIdKey.equals(w) || userIdInKey.equals(w)) {
+ throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 不合法!"
+ + "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
+ }
}
+
+ whereList.add(w);
}
- whereList.add(w);
+ // 可重写回调方法自定义处理 // 动态设置的场景似乎很少,而且去掉后不方便用户排错!//去掉判断,有时候不在没关系,如果是对增删改等非开放请求强制要求传对应的条件,可以用 Operation.NECESSARY
+ if (request.containsKey(w) == false) { //和 request.get(w) == null 没区别,前面 Parser 已经过滤了 null
+ // throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 对应的 " + w + " 不在它里面!");
+ callback.onMissingKey4Combine(table, request, combine, ws[i], w);
+ }
}
- // 可重写回调方法自定义处理 // 动态设置的场景似乎很少,而且去掉后不方便用户排错!//去掉判断,有时候不在没关系,如果是对增删改等非开放请求强制要求传对应的条件,可以用 Operation.NECESSARY
- if (request.containsKey(w) == false) { //和 request.get(w) == null 没区别,前面 Parser 已经过滤了 null
- // throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 对应的 " + w + " 不在它里面!");
- callback.onMissingKey4Combine(table, request, combine, ws[i], w);
- }
}
}
@@ -3900,7 +4212,9 @@ else if (w.startsWith("!")) {
if (isWhere || (StringUtil.isName(key.replaceFirst("[+-]$", "")) == false)) {
tableWhere.put(key, value);
if (whereList == null || whereList.contains(key) == false) {
- andList.add(key);
+ if (andList != null) {
+ andList.add(key);
+ }
}
}
else if (whereList != null && whereList.contains(key)) {
@@ -3911,10 +4225,13 @@ else if (whereList != null && whereList.contains(key)) {
}
}
- combineMap.put("&", andList);
- combineMap.put("|", orList);
- combineMap.put("!", notList);
+ if (combineMap != null) {
+ combineMap.put("&", andList);
+ combineMap.put("|", orList);
+ combineMap.put("!", notList);
+ }
config.setCombine(combineMap);
+ config.setCombineExpression(combineExpression);
config.setContent(tableContent);
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index aca544111..259788770 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -176,6 +176,9 @@ public interface SQLConfig {
Map> getCombine();
SQLConfig setCombine(Map> combine);
+
+ String getCombineExpression();
+ SQLConfig setCombineExpression(String combineExpression);
Map getCast();
SQLConfig setCast(Map cast);
From d29d079ab867843f4fbedb73dea737663167a024 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 03:27:39 +0800
Subject: [PATCH 066/674] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88?=
=?UTF-8?q?=EF=BC=9A=E8=A7=A3=E5=86=B3=20@combine:"(date>=20|=20tag&$)=20&?=
=?UTF-8?q?=20name*~"=20=E8=A7=A3=E6=9E=90=E5=BC=82=E5=B8=B8=EF=BC=8C?=
=?UTF-8?q?=E8=A7=A3=E5=86=B3=20@combine:"id=20|=20userId{}"=20=E5=8F=AF?=
=?UTF-8?q?=E7=BB=95=E8=BF=87=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 62 +++++++++++--------
1 file changed, 36 insertions(+), 26 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index cb4b0a981..608676769 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2342,25 +2342,22 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map usedKeySet = new HashSet<>(where.size());
- while (i < n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
- char c = s.charAt(i);
- boolean isLast = i >= n - 1;
+ while (i <= n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
+ boolean isOver = i >= n;
+ char c = isOver ? 0 : s.charAt(i);
boolean isBlankOrRightParenthesis = c == ' ' || c == ')';
- if (isLast || isBlankOrRightParenthesis) {
- if (isBlankOrRightParenthesis == false) {
- key += c;
- }
-
+ if (isOver || isBlankOrRightParenthesis) {
boolean isEmpty = StringUtil.isEmpty(key, true);
if (isEmpty && last != ')') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (isOver ? s : s.substring(i))
+ "' 不合法!" + (c == ' ' ? "空格 ' ' " : "右括号 ')'") + " 左边缺少条件 key !逻辑连接符 & | 左右必须各一个相邻空格!"
+ "空格不能多也不能少!不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
}
if (isEmpty == false) {
if (first == false && lastLogic <= 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i - key.length()) + "' 不合法!左边缺少 & | 其中一个逻辑连接符!");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i - key.length() - (isOver ? 1 : 0))
+ + "' 不合法!左边缺少 & | 其中一个逻辑连接符!");
}
Object value = where.get(key);
@@ -2380,7 +2377,7 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map= n || s.charAt(i + 1) != ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ if (i >= n - 1 || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
+ "' 不合法!逻辑连接符 & 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
}
@@ -2399,14 +2396,14 @@ else if (c == '&') {
lastLogic = c;
i ++;
}
- else if (isLast == false) {
+ else {
key += c;
}
}
else if (c == '|') {
if (last == ' ') {
- if (i >= n || s.charAt(i + 1) != ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ if (i >= n - 1 || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
+ "' 不合法!逻辑连接符 | 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ "不允许首尾有空格,也不允许连续空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
}
@@ -2415,7 +2412,7 @@ else if (c == '|') {
lastLogic = c;
i ++;
}
- else if (isLast == false) {
+ else {
key += c;
}
}
@@ -2438,16 +2435,12 @@ else if (c == ')') {
whereString += c;
lastLogic = 0;
}
- else if (isLast == false) {
+ else {
key += c;
}
last = c;
i ++;
-
- if (i >= n) {
- i = n - 1;
- }
}
if (depth != 0) {
@@ -2477,8 +2470,8 @@ else if (isLast == false) {
if (StringUtil.isEmpty(whereString, true)) {
whereString = andWhere;
}
- else if (StringUtil.isNotEmpty(andWhere, true)) {
- whereString = andWhere + AND + "( " + whereString + " )";
+ else if (StringUtil.isNotEmpty(andWhere, true)) { // andWhere 必须放后面,否则 prepared 值顺序错误
+ whereString = "( " + whereString + " )" + AND + andWhere;
}
if (joinList != null) {
@@ -4127,7 +4120,24 @@ else if (id instanceof Subquery) {}
List orList = combineMap == null ? null : new ArrayList<>();
List notList = combineMap == null ? null : new ArrayList<>();
- if (combineMap != null) {
+ if (combineMap == null) {
+ if (StringUtil.isNotEmpty(combineExpression, true)) {
+ List banKeyList = Arrays.asList(idKey, idInKey, userIdKey, userIdInKey);
+
+ for (String key : banKeyList) {
+ int index = combineExpression.indexOf(key);
+ if (index >= 0) {
+ char left = index <= 0 ? ' ' : combineExpression.charAt(index - 1);
+ char right = index >= combineExpression.length() - key.length() ? ' ' : combineExpression.charAt(index + key.length());
+ if ((left == ' ' || left == '(') && (right == ' ' || right == ')')) {
+ throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的 value 里 " + key + " 不合法!"
+ + "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
+ }
+ }
+ }
+ }
+ }
+ else {
//强制作为条件且放在最前面优化性能
if (id != null) {
tableWhere.put(idKey, id);
@@ -4178,7 +4188,7 @@ else if (w.startsWith("!")) {
}
else {
if (idKey.equals(w) || idInKey.equals(w) || userIdKey.equals(w) || userIdInKey.equals(w)) {
- throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 不合法!"
+ throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的 value 里 " + ws[i] + " 不合法!"
+ "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
}
}
From 795c8e9ccb0e4ccc82f3bd5e01d1c865b879c8a1 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 04:38:35 +0800
Subject: [PATCH 067/674] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88?=
=?UTF-8?q?=EF=BC=9A=E9=99=90=E5=88=B6=20@combine:value=20=E4=B8=AD?=
=?UTF-8?q?=E7=9A=84=20value=20=E7=9A=84=E6=8B=AC=E5=8F=B7=E5=B5=8C?=
=?UTF-8?q?=E5=A5=97=E6=B7=B1=E5=BA=A6=E3=80=81key=20=E6=95=B0=E9=87=8F?=
=?UTF-8?q?=E3=80=81key=20=E9=87=8D=E5=A4=8D=E6=AC=A1=E6=95=B0=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 77 +++++++++++++++++--
1 file changed, 70 insertions(+), 7 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 608676769..1541a6be1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -37,11 +37,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Deque;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -81,7 +78,13 @@
*/
public abstract class AbstractSQLConfig implements SQLConfig {
private static final String TAG = "AbstractSQLConfig";
-
+
+ public static int MAX_COMBINE_DEPTH = 2;
+ public static int MAX_WHERE_COUNT = 3;
+ public static int MAX_COMBINE_COUNT = 5;
+ public static int MAX_COMBINE_KEY_COUNT = 2;
+ public static float MAX_COMBINE_RATIO = 1.0f;
+
public static String DEFAULT_DATABASE = DATABASE_MYSQL;
public static String DEFAULT_SCHEMA = "sys";
public static String PREFFIX_DISTINCT = "DISTINCT ";
@@ -102,6 +105,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// 允许调用的 SQL 函数:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL
public static final Map SQL_FUNCTION_MAP;
+
static { // 凡是 SQL 边界符、分隔符、注释符 都不允许,例如 ' " ` ( ) ; # -- /**/ ,以免拼接 SQL 时被注入意外可执行指令
PATTERN_RANGE = Pattern.compile("^[0-9%,!=\\<\\>/\\.\\+\\-\\*\\^]+$"); // ^[a-zA-Z0-9_*%!=<>(),"]+$ 导致 exists(select*from(Comment)) 通过!
PATTERN_FUNCTION = Pattern.compile("^[A-Za-z0-9%,:_@&~`!=\\<\\>\\|\\[\\]\\{\\} /\\.\\+\\-\\*\\^\\?\\(\\)\\$]+$"); //TODO 改成更好的正则,校验前面为单词,中间为操作符,后面为值
@@ -2140,6 +2144,23 @@ public SQLConfig setCast(Map cast) {
//WHERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+ protected int getMaxWhereCount() {
+ return MAX_WHERE_COUNT;
+ }
+ protected int getMaxCombineDepth() {
+ return MAX_COMBINE_DEPTH;
+ }
+ protected int getMaxCombineCount() {
+ return MAX_COMBINE_COUNT;
+ }
+ protected int getMaxCombineKeyCount() {
+ return MAX_COMBINE_KEY_COUNT;
+ }
+ protected float getMaxCombineRatio() {
+ return MAX_COMBINE_RATIO;
+ }
+
+
@Override
public String getCombineExpression() {
return combineExpression;
@@ -2329,10 +2350,26 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map();
+ }
+ int whereSize = where.size();
+
+ int maxWhereCount = getMaxWhereCount();
+ if (maxWhereCount > 0 && whereSize > maxWhereCount) {
+ throw new IllegalArgumentException(table + ":{ key0:value0, key1:value1... } 中条件 key:value 数量 " + whereSize + " 已超过最大数量,必须在 0-" + maxWhereCount + " 内!");
+ }
String whereString = "";
+ int maxDepth = getMaxCombineDepth();
+ int maxCombineCount = getMaxCombineCount();
+ int maxCombineKeyCount = getMaxCombineKeyCount();
+ float maxCombineRatio = getMaxCombineRatio();
+
int depth = 0;
+ int allCount = 0;
+
int n = s.length();
int i = 0;
@@ -2341,7 +2378,7 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map usedKeySet = new HashSet<>(where.size());
+ Map usedKeyCountMap = new HashMap<>(whereSize);
while (i <= n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
boolean isOver = i >= n;
char c = isOver ? 0 : s.charAt(i);
@@ -2359,6 +2396,17 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map maxCombineCount && maxCombineCount > 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
+ + "其中 key 数量 " + allCount + " 已超过最大值,必须在条件键值对数量 0-" + maxCombineCount + " 内!");
+ }
+ if (1.0f*allCount/whereSize > maxCombineRatio && maxCombineRatio > 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
+ + "其中 key 数量 " + allCount + " / 条件键值对数量 " + whereSize + " = " + (1.0f*allCount/whereSize)
+ + " 已超过 最大倍数,必须在条件键值对数量 0-" + maxCombineRatio + " 倍内!");
+ }
Object value = where.get(key);
if (value == null) {
@@ -2370,7 +2418,16 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map maxCombineKeyCount && maxCombineKeyCount > 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!其中 '" + key
+ + "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
+ }
+
+ usedKeyCountMap.put(key, count);
+
+
whereString += "( " + wi + " )";
first = false;
}
@@ -2422,6 +2479,10 @@ else if (c == '(') {
}
depth ++;
+ if (depth > maxDepth && maxDepth > 0) {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1) + "' 不合法!括号 (()) 嵌套层级 " + depth + " 已超过最大值,必须在 0-" + maxDepth + " 内!");
+ }
+
whereString += c;
lastLogic = 0;
first = true;
@@ -2454,7 +2515,7 @@ else if (c == ')') {
for (Entry entry : set) {
key = entry == null ? null : entry.getKey();
- if (key == null || usedKeySet.contains(key)) {
+ if (key == null || usedKeyCountMap.containsKey(key)) {
continue;
}
@@ -2609,6 +2670,8 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
return result;
}
+
+
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, Map> combine, List joinList, boolean verifyName) throws Exception {
Set>> combineSet = combine == null ? null : combine.entrySet();
if (combineSet == null || combineSet.isEmpty()) {
From 66000f747f4bc10e8053b9267e5394eb11f7a050 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 04:42:04 +0800
Subject: [PATCH 068/674] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=9D=A1=E4=BB=B6?=
=?UTF-8?q?=E9=94=AE=E5=80=BC=E5=AF=B9=E7=9A=84=E9=BB=98=E8=AE=A4=E6=9C=80?=
=?UTF-8?q?=E5=A4=A7=E6=95=B0=E9=87=8F?=
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 1541a6be1..5aa4c9f8e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -80,7 +80,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
private static final String TAG = "AbstractSQLConfig";
public static int MAX_COMBINE_DEPTH = 2;
- public static int MAX_WHERE_COUNT = 3;
+ public static int MAX_WHERE_COUNT = 10;
public static int MAX_COMBINE_COUNT = 5;
public static int MAX_COMBINE_KEY_COUNT = 2;
public static float MAX_COMBINE_RATIO = 1.0f;
From 4cf7d985a56f93fc6f9766ae138fca159ae63810 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 17:23:10 +0800
Subject: [PATCH 069/674] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88?=
=?UTF-8?q?=EF=BC=9A@combine:value=20=E4=B8=AD=E7=9A=84=20value=20?=
=?UTF-8?q?=E6=94=AF=E6=8C=81=E9=9D=9E=E9=80=BB=E8=BE=91=E7=AC=A6=20=20!?=
=?UTF-8?q?=20=EF=BC=8C=E8=A7=A3=E5=86=B3=E4=B8=8D=E5=85=81=E8=AE=B8?=
=?UTF-8?q?=E8=BF=9E=E7=BB=AD=E5=B7=A6=E6=8B=AC=E5=8F=B7=20((?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 42 +++++++++++++------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 5aa4c9f8e..4332b3069 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2376,6 +2376,7 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map usedKeyCountMap = new HashMap<>(whereSize);
@@ -2394,7 +2395,7 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map maxCombineKeyCount && maxCombineKeyCount > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!其中 '" + key
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!其中 '" + column
+ "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
}
+ usedKeyCountMap.put(column, count);
- usedKeyCountMap.put(key, count);
-
-
- whereString += "( " + wi + " )";
+ whereString += "( " + getCondition(isNot, wi) + " )";
+ isNot = false;
first = false;
}
@@ -2473,8 +2475,22 @@ else if (c == '|') {
key += c;
}
}
+ else if (c == '!') {
+ last = i < 1 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
+ if (i < n - 1 && s.charAt(i + 1) == '(') {
+ whereString += SQL.NOT;
+ lastLogic = c;
+ }
+ else if (last <= 0 || last == ' ' || last == '(') {
+ isNot = true;
+// lastLogic = c;
+ }
+ else {
+ key += c;
+ }
+ }
else if (c == '(') {
- if (key.isEmpty() == false || (i > 0 && lastLogic <= 0)) {
+ if (key.isEmpty() == false || (i > 0 && lastLogic <= 0 && last != '(')) {
throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i) + "' 不合法!左边缺少 & | 逻辑连接符!");
}
@@ -3555,7 +3571,7 @@ public String getSubqueryString(Subquery subquery) throws Exception {
* @param condition
* @return
*/
- private static String getCondition(boolean not, String condition) {
+ public static String getCondition(boolean not, String condition) {
return not ? NOT + "(" + condition + ")" : condition;
}
From bff0d44c35c91754d7768c32d8683d6f8696f116 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 19:41:25 +0800
Subject: [PATCH 070/674] =?UTF-8?q?@combine:value=20=E5=A4=8D=E6=9D=82?=
=?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88=EF=BC=9A=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E6=9C=80=E7=BB=88=E6=9D=A1=E4=BB=B6=E4=B8=A2=E5=A4=B1=20id,=20?=
=?UTF-8?q?id{}=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=8F=AF=E4=BB=A5=E9=80=9A?=
=?UTF-8?q?=E8=BF=87=20!id,=20!id{}=20=E7=BB=95=E8=BF=87=E6=9D=83=E9=99=90?=
=?UTF-8?q?=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 90 +++++++++++++------
1 file changed, 62 insertions(+), 28 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 4332b3069..5a4675471 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2357,7 +2357,8 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map 0 && whereSize > maxWhereCount) {
- throw new IllegalArgumentException(table + ":{ key0:value0, key1:value1... } 中条件 key:value 数量 " + whereSize + " 已超过最大数量,必须在 0-" + maxWhereCount + " 内!");
+ throw new IllegalArgumentException(table + ":{ key0:value0, key1:value1... } 中条件 key:value 数量 " + whereSize
+ + " 已超过最大数量,必须在 0-" + maxWhereCount + " 内!");
}
String whereString = "";
@@ -2367,6 +2368,9 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map prepreadValues = getPreparedValueList();
+ setPreparedValueList(new ArrayList<>());
+
int depth = 0;
int allCount = 0;
@@ -2394,8 +2398,8 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map maxCombineKeyCount && maxCombineKeyCount > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!其中 '" + column
- + "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
+ + "其中 '" + column + "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
}
usedKeyCountMap.put(column, count);
@@ -2476,8 +2482,24 @@ else if (c == '|') {
}
}
else if (c == '!') {
- last = i < 1 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
- if (i < n - 1 && s.charAt(i + 1) == '(') {
+ char next = i >= n - 1 ? 0 : s.charAt(i + 1);
+ if (next == ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个空格 ' ' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+ if (next == ')' || next == '&' || next == '!') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个字符 '" + next + "' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+
+ last = i <= 0 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
+ if (i > 0 && lastLogic <= 0 && last != '(') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
+
+ if (next == '(') {
whereString += SQL.NOT;
lastLogic = c;
}
@@ -2491,12 +2513,15 @@ else if (last <= 0 || last == ' ' || last == '(') {
}
else if (c == '(') {
if (key.isEmpty() == false || (i > 0 && lastLogic <= 0 && last != '(')) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i) + "' 不合法!左边缺少 & | 逻辑连接符!");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
}
depth ++;
if (depth > maxDepth && maxDepth > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1) + "' 不合法!括号 (()) 嵌套层级 " + depth + " 已超过最大值,必须在 0-" + maxDepth + " 内!");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!括号 (()) 嵌套层级 " + depth + " 已超过最大值,必须在 0-" + maxDepth + " 内!");
}
whereString += c;
@@ -2506,7 +2531,8 @@ else if (c == '(') {
else if (c == ')') {
depth --;
if (depth < 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1) + "' 不合法!左括号 ( 比 右括号 ) 少!数量必须相等从而完整闭合 (...) !");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!左括号 ( 比 右括号 ) 少!数量必须相等从而完整闭合 (...) !");
}
whereString += c;
@@ -2521,7 +2547,8 @@ else if (c == ')') {
}
if (depth != 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!左括号 ( 比 右括号 ) 多!数量必须相等从而完整闭合 (...) !");
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s
+ + "' 不合法!左括号 ( 比 右括号 ) 多!数量必须相等从而完整闭合 (...) !");
}
Set> set = where.entrySet();
@@ -2548,7 +2575,11 @@ else if (c == ')') {
whereString = andWhere;
}
else if (StringUtil.isNotEmpty(andWhere, true)) { // andWhere 必须放后面,否则 prepared 值顺序错误
- whereString = "( " + whereString + " )" + AND + andWhere;
+// whereString = "( " + whereString + " )" + AND + andWhere;
+
+ whereString = andWhere + AND + "( " + whereString + " )"; // 先暂存之前的 prepared 值,然后反向整合
+ prepreadValues.addAll(getPreparedValueList());
+ setPreparedValueList(prepreadValues);
}
if (joinList != null) {
@@ -2557,7 +2588,7 @@ else if (StringUtil.isNotEmpty(andWhere, true)) { // andWhere 必须放后面
String ws = whereString;
List newPvl = new ArrayList<>();
- List pvl = new ArrayList<>(preparedValueList);
+ List pvl = new ArrayList<>(getPreparedValueList());
SQLConfig jc;
String js;
@@ -2673,7 +2704,7 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
if (changed) {
whereString = newWs;
- preparedValueList = newPvl;
+ setPreparedValueList(newPvl);
}
}
@@ -4199,6 +4230,20 @@ else if (id instanceof Subquery) {}
List orList = combineMap == null ? null : new ArrayList<>();
List notList = combineMap == null ? null : new ArrayList<>();
+ //强制作为条件且放在最前面优化性能
+ if (id != null) {
+ tableWhere.put(idKey, id);
+ if (andList != null) {
+ andList.add(idKey);
+ }
+ }
+ if (idIn != null) {
+ tableWhere.put(idInKey, idIn);
+ if (andList != null) {
+ andList.add(idInKey);
+ }
+ }
+
if (combineMap == null) {
if (StringUtil.isNotEmpty(combineExpression, true)) {
List banKeyList = Arrays.asList(idKey, idInKey, userIdKey, userIdInKey);
@@ -4208,7 +4253,7 @@ else if (id instanceof Subquery) {}
if (index >= 0) {
char left = index <= 0 ? ' ' : combineExpression.charAt(index - 1);
char right = index >= combineExpression.length() - key.length() ? ' ' : combineExpression.charAt(index + key.length());
- if ((left == ' ' || left == '(') && (right == ' ' || right == ')')) {
+ if ((left == ' ' || left == '(' || left == '&' || left == '|' || left == '!') && (right == ' ' || right == ')')) {
throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的 value 里 " + key + " 不合法!"
+ "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
}
@@ -4217,17 +4262,6 @@ else if (id instanceof Subquery) {}
}
}
else {
- //强制作为条件且放在最前面优化性能
- if (id != null) {
- tableWhere.put(idKey, id);
- andList.add(idKey);
- }
- if (idIn != null) {
- tableWhere.put(idInKey, idIn);
- andList.add(idInKey);
- }
-
-
if (ws != null) {
if (method == DELETE || method == GETS || method == HEADS) {
throw new IllegalArgumentException("DELETE,GETS,HEADS 请求不允许传 @combine:value !");
From cf7bdd74e4b25bed7b19eff65c1ec4a5051b86d2 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 20:04:26 +0800
Subject: [PATCH 071/674] =?UTF-8?q?@combine:value=20=E5=A4=8D=E6=9D=82?=
=?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88=EF=BC=9A=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=20key!=20=E6=8A=A5=E9=94=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 31 ++++++++++---------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 5a4675471..2bed0dda0 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2441,6 +2441,7 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map= n - 1 ? 0 : s.charAt(i + 1);
- if (next == ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!非逻辑符 '!' 右边多了一个空格 ' ' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
- }
- if (next == ')' || next == '&' || next == '!') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!非逻辑符 '!' 右边多了一个字符 '" + next + "' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
- }
-
last = i <= 0 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
- if (i > 0 && lastLogic <= 0 && last != '(') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
- + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
- + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+
+ char next = i >= n - 1 ? 0 : s.charAt(i + 1);
+ if (last == ' ' || last == '(') {
+ if (next == ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个空格 ' ' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+ if (next == ')' || next == '&' || next == '!') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个字符 '" + next + "' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+ if (i > 0 && lastLogic <= 0 && last != '(') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
}
if (next == '(') {
From 3ea3e6121552965b21e3e79b4bde1bf0f0b1ae2f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 20:53:29 +0800
Subject: [PATCH 072/674] =?UTF-8?q?=E4=BC=98=E5=8C=96=20=20where=20?=
=?UTF-8?q?=E5=92=8C=20JOIN=20=E8=A7=A3=E6=9E=90=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 157 +++---------------
1 file changed, 20 insertions(+), 137 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 2bed0dda0..83e0d85c1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2585,132 +2585,8 @@ else if (StringUtil.isNotEmpty(andWhere, true)) { // andWhere 必须放后面
setPreparedValueList(prepreadValues);
}
- if (joinList != null) {
-
- String newWs = "";
- String ws = whereString;
-
- List newPvl = new ArrayList<>();
- List pvl = new ArrayList<>(getPreparedValueList());
-
- SQLConfig jc;
- String js;
-
- boolean changed = false;
- //各种 JOIN 没办法统一用 & | !连接,只能按优先级,和 @combine 一样?
- for (Join j : joinList) {
- String jt = j.getJoinType();
-
- switch (jt) {
- case "*": // CROSS JOIN
- case "@": // APP JOIN
- case "<": // LEFT JOIN
- case ">": // RIGHT JOIN
- break;
-
- case "&": // INNER JOIN: A & B
- case "": // FULL JOIN: A | B
- case "|": // FULL JOIN: A | B
- case "!": // OUTER JOIN: ! (A | B)
- case "^": // SIDE JOIN: ! (A & B)
- case "(": // ANTI JOIN: A & ! B
- case ")": // FOREIGN JOIN: B & ! A
- jc = j.getJoinConfig();
- boolean isMain = jc.isMain();
- jc.setMain(false).setPrepared(isPrepared()).setPreparedValueList(new ArrayList());
- js = jc.getWhereString(false);
- jc.setMain(isMain);
-
- boolean isOuterJoin = "!".equals(jt);
- boolean isSideJoin = "^".equals(jt);
- boolean isAntiJoin = "(".equals(jt);
- boolean isForeignJoin = ")".equals(jt);
- boolean isWsEmpty = StringUtil.isEmpty(ws, true);
-
- if (isWsEmpty) {
- if (isOuterJoin) { // ! OUTER JOIN: ! (A | B)
- throw new NotExistException("no result for ! OUTER JOIN( ! (A | B) ) when A or B is empty!");
- }
- if (isForeignJoin) { // ) FOREIGN JOIN: B & ! A
- throw new NotExistException("no result for ) FOREIGN JOIN( B & ! A ) when A is empty!");
- }
- }
-
- if (StringUtil.isEmpty(js, true)) {
- if (isOuterJoin) { // ! OUTER JOIN: ! (A | B)
- throw new NotExistException("no result for ! OUTER JOIN( ! (A | B) ) when A or B is empty!");
- }
- if (isAntiJoin) { // ( ANTI JOIN: A & ! B
- throw new NotExistException("no result for ( ANTI JOIN( A & ! B ) when B is empty!");
- }
-
- if (isWsEmpty) {
- if (isSideJoin) {
- throw new NotExistException("no result for ^ SIDE JOIN( ! (A & B) ) when both A and B are empty!");
- }
- }
- else {
- if (isSideJoin || isForeignJoin) {
- newWs += " ( " + getCondition(true, ws) + " ) ";
-
- newPvl.addAll(pvl);
- newPvl.addAll(jc.getPreparedValueList());
- changed = true;
- }
- }
-
- continue;
- }
-
- if (StringUtil.isEmpty(newWs, true) == false) {
- newWs += AND;
- }
-
- if (isAntiJoin) { // ( ANTI JOIN: A & ! B
- newWs += " ( " + ( isWsEmpty ? "" : ws + AND ) + NOT + " ( " + js + " ) " + " ) ";
- }
- else if (isForeignJoin) { // ) FOREIGN JOIN: (! A) & B // preparedValueList.add 不好反过来 B & ! A
- newWs += " ( " + NOT + " ( " + ws + " ) ) " + AND + " ( " + js + " ) ";
- }
- else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
- //MySQL 因为 NULL 值处理问题,(A & ! B) | (B & ! A) 与 ! (A & B) 返回结果不一样,后者往往更多
- newWs += " ( " + getCondition(
- true,
- ( isWsEmpty ? "" : ws + AND ) + " ( " + js + " ) "
- ) + " ) ";
- }
- else { // & INNER JOIN: A & B; | FULL JOIN: A | B; OUTER JOIN: ! (A | B)
- int logic = Logic.getType(jt);
- newWs += " ( "
- + getCondition(
- Logic.isNot(logic),
- ws
- + ( isWsEmpty ? "" : (Logic.isAnd(logic) ? AND : OR) )
- + " ( " + js + " ) "
- )
- + " ) ";
- }
-
- newPvl.addAll(pvl);
- newPvl.addAll(jc.getPreparedValueList());
-
- changed = true;
- break;
- default:
- throw new UnsupportedOperationException(
- "join:value 中 value 里的 " + jt + "/" + j.getPath()
- + "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
- + ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
- );
- }
- }
-
- if (changed) {
- whereString = newWs;
- setPreparedValueList(newPvl);
- }
- }
-
+ whereString = concatJoinWhereString(whereString);
+
String result = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
if (result.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
@@ -2721,7 +2597,6 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
}
-
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, Map> combine, List joinList, boolean verifyName) throws Exception {
Set>> combineSet = combine == null ? null : combine.entrySet();
if (combineSet == null || combineSet.isEmpty()) {
@@ -2776,15 +2651,28 @@ else if ("!".equals(ce.getKey())) {
whereString += (isCombineFirst ? "" : AND) + (Logic.isNot(logic) ? NOT : "") + " ( " + cs + " ) ";
isCombineFirst = false;
}
+
+ whereString = concatJoinWhereString(whereString);
+ String s = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
+ if (s.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
+ throw new UnsupportedOperationException("写操作请求必须带条件!!!");
+ }
+
+ return s;
+ }
+
+
+ protected String concatJoinWhereString(String whereString) throws Exception {
+ List joinList = getJoinList();
if (joinList != null) {
String newWs = "";
String ws = whereString;
List newPvl = new ArrayList<>();
- List pvl = new ArrayList<>(preparedValueList);
+ List pvl = new ArrayList<>(getPreparedValueList());
SQLConfig jc;
String js;
@@ -2873,7 +2761,7 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
) + " ) ";
}
else { // & INNER JOIN: A & B; | FULL JOIN: A | B; OUTER JOIN: ! (A | B)
- logic = Logic.getType(jt);
+ int logic = Logic.getType(jt);
newWs += " ( "
+ getCondition(
Logic.isNot(logic),
@@ -2900,19 +2788,14 @@ else if (isSideJoin) { // ^ SIDE JOIN: ! (A & B)
if (changed) {
whereString = newWs;
- preparedValueList = newPvl;
+ setPreparedValueList(newPvl);
}
}
- String s = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
-
- if (s.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
- throw new UnsupportedOperationException("写操作请求必须带条件!!!");
- }
-
- return s;
+ return whereString;
}
+
/**
* @param key
* @param value
From 0063721354b60ae62244b36429ddaf5c3674b389 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 6 Mar 2022 21:57:26 +0800
Subject: [PATCH 073/674] =?UTF-8?q?JOIN=20ON=20=E6=96=B0=E5=A2=9E=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=20{}=20IN=20=E5=92=8C=20<>=20json=5Fcontains=20?=
=?UTF-8?q?=E4=B8=A4=E7=A7=8D=E5=85=B3=E8=81=94=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/SQL.java | 2 +
.../java/apijson/orm/AbstractSQLConfig.java | 44 +++++++++++++++++--
.../java/apijson/orm/AbstractSQLExecutor.java | 6 ++-
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 397c2915f..e8b7bda6f 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -10,6 +10,8 @@
*/
public class SQL {
+ public static final String JOIN = " JOIN ";
+ public static final String ON = " ON ";
public static final String OR = " OR ";
public static final String AND = " AND ";
public static final String NOT = " NOT ";
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 83e0d85c1..94b65a484 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -33,6 +33,7 @@
import static apijson.SQL.AND;
import static apijson.SQL.NOT;
import static apijson.SQL.OR;
+import static apijson.SQL.ON;
import java.util.ArrayList;
import java.util.Arrays;
@@ -3803,8 +3804,25 @@ public String getJoinString() throws Exception {
if (onList != null) {
boolean first = true;
for (On on : onList) {
- sql += (first ? " ON " : " AND ") + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ String rt = on.getRelateType();
+ if (StringUtil.isEmpty(rt, false)) {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
+ else if ("{}".equals(rt)) {
+ sql += (first ? ON : AND) + "json_contains(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote
+ // + ", concat('\\'', " + quote + jt + quote + "." + quote + on.getKey() + quote + ", '\\''), '$')";
+ + ", cast(" + quote + jt + quote + "." + quote + on.getKey() + quote + " AS CHAR), '$')";
+ }
+ else if ("<>".equals(rt)) {
+ sql += (first ? ON : AND) + "json_contains(" + quote + jt + quote + "." + quote + on.getKey() + quote
+ // + ", concat('\\'', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '\\''), '$')";
+ + ", cast(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + " AS CHAR), '$')";
+ }
+ else {
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
+ }
first = false;
}
}
@@ -3826,8 +3844,26 @@ public String getJoinString() throws Exception {
if (onList != null) {
boolean first = true;
for (On on : onList) {
- sql += (first ? " ON " : " AND ") + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ String rt = on.getRelateType();
+ if (StringUtil.isEmpty(rt, false)) {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
+ else if ("{}".equals(rt)) {
+ sql += (first ? ON : AND) + "json_contains(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote
+ // + ", concat('\\'', " + quote + jt + quote + "." + quote + on.getKey() + quote + ", '\\''), '$')";
+ + ", cast(" + quote + jt + quote + "." + quote + on.getKey() + quote + " AS CHAR), '$')";
+ }
+ else if ("<>".equals(rt)) {
+ sql += (first ? ON : AND) + "json_contains(" + quote + jt + quote + "." + quote + on.getKey() + quote
+ // + ", concat('\\'', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '\\''), '$')";
+ + ", cast(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + " AS CHAR), '$')";
+ }
+ else {
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
+ }
+
first = false;
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 51705dca4..13edbfc26 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -23,7 +23,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -537,7 +536,8 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
if (onList != null) {
for (On on : onList) {
if (on != null) {
- viceConfig.putWhere(on.getKey(), item.get(on.getTargetKey()), true);
+ String ok = on.getOriginKey();
+ viceConfig.putWhere(ok.substring(0, ok.length() - 1), item.get(on.getTargetKey()), true);
}
}
}
@@ -743,6 +743,8 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
//缓存到 childMap
if (onList != null) {
for (On on : onList) {
+ String ok = on.getOriginKey();
+ String vk = ok.substring(0, ok.length() - 1);
cc.putWhere(on.getKey(), result.get(on.getKey()), true);
}
}
From 0dc96b4681a74ebeb35435ac131f5a2978a5e4ed Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 7 Mar 2022 00:48:54 +0800
Subject: [PATCH 074/674] =?UTF-8?q?JOIN=20ON=20=E6=96=B0=E5=A2=9E=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E6=AF=94=E8=BE=83=E8=BF=90=E7=AE=97=E7=AC=A6=20>,=20=
=?UTF-8?q?,=20>=3D,=20<=3D=20=E5=92=8C=E5=AD=97=E7=AC=A6=E5=8C=B9?=
=?UTF-8?q?=E9=85=8D=20$=20LIKE,=20~=20REGEXP?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/SQL.java | 11 +-
.../java/apijson/orm/AbstractSQLConfig.java | 203 +++++++++++++-----
.../java/apijson/orm/AbstractSQLExecutor.java | 1 +
.../src/main/java/apijson/orm/Join.java | 60 +++++-
.../src/main/java/apijson/orm/Logic.java | 5 +
5 files changed, 219 insertions(+), 61 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index e8b7bda6f..4f2a1ccb4 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -388,7 +388,16 @@ public static String search(String s, int type, boolean ignoreCase) {
return "%" + s + "%";
}
}
-
+
//search>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+ public static boolean isBooleanOrNumber(String type) {
+ type = StringUtil.toUpperCase(type, true);
+ return type.isEmpty() || (type.endsWith("INT") && type.endsWith("POINT") == false)
+ || type.endsWith("BOOLEAN") || type.endsWith("ENUM")
+ || type.endsWith("FLOAT") || type.endsWith("DOUBLE") || type.endsWith("DECIMAL");
+ }
+
+
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 94b65a484..9dfc7d9eb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -430,6 +430,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
SQL_FUNCTION_MAP.put("nullif", ""); // NULLIF(expr1, expr2) 比较两个字符串,如果字符串 expr1 与 expr2 相等 返回 NULL,否则返回 expr1
SQL_FUNCTION_MAP.put("group_concat", ""); // GROUP_CONCAT([DISTINCT], s1, s2...) 聚合拼接字符串
SQL_FUNCTION_MAP.put("match", ""); // MATCH (name,tag) AGAINST ('a b' IN NATURAL LANGUAGE MODE) 全文检索
+ SQL_FUNCTION_MAP.put("any_value", ""); // any_value(userId) 解决 ONLY_FULL_GROUP_BY 报错
@@ -3439,7 +3440,7 @@ else if (isOracle()) {
condition += (condition + "has(JSONExtractArrayRaw(assumeNotNull(" + getKey(column) + "))" + ", " + getValue(key, column, v) + (StringUtil.isEmpty(path, true) ? "" : ", " + getValue(key, column, path)) + ")");
}
else {
- condition += ("json_contains(" + getKey(column) + ", " + getValue(key, column, v) + (StringUtil.isEmpty(path, true) ? "" : ", " + getValue(key, column, path)) + ")");
+ condition += ("json_contains(" + getKey(column) + ", " + getValue(key, column, v) + (StringUtil.isEmpty(path, true) ? "" : ", " + getValue(key, column, path)) + ")");
}
}
}
@@ -3490,7 +3491,17 @@ public String getSubqueryString(Subquery subquery) throws Exception {
* @return
*/
public static String getCondition(boolean not, String condition) {
- return not ? NOT + "(" + condition + ")" : condition;
+ return getCondition(not, condition, false);
+ }
+ /**拼接条件
+ * @param not
+ * @param condition
+ * @param outerBreaket
+ * @return
+ */
+ public static String getCondition(boolean not, String condition, boolean addOuterBracket) {
+ String s = not ? NOT + "(" + condition + ")" : condition;
+ return addOuterBracket ? "( " + s + " )" : s;
}
@@ -3800,32 +3811,7 @@ public String getJoinString() throws Exception {
jc.setMain(true).setKeyPrefix(false);
sql = ( "<".equals(type) ? " LEFT" : (">".equals(type) ? " RIGHT" : " CROSS") )
+ " JOIN ( " + jc.getSQL(isPrepared()) + " ) AS " + quote + jt + quote;
-
- if (onList != null) {
- boolean first = true;
- for (On on : onList) {
- String rt = on.getRelateType();
- if (StringUtil.isEmpty(rt, false)) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
- }
- else if ("{}".equals(rt)) {
- sql += (first ? ON : AND) + "json_contains(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote
- // + ", concat('\\'', " + quote + jt + quote + "." + quote + on.getKey() + quote + ", '\\''), '$')";
- + ", cast(" + quote + jt + quote + "." + quote + on.getKey() + quote + " AS CHAR), '$')";
- }
- else if ("<>".equals(rt)) {
- sql += (first ? ON : AND) + "json_contains(" + quote + jt + quote + "." + quote + on.getKey() + quote
- // + ", concat('\\'', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '\\''), '$')";
- + ", cast(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + " AS CHAR), '$')";
- }
- else {
- throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
- + " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
- }
- first = false;
- }
- }
+ sql = concatJoinOn(sql, quote, j, jt, onList);
jc.setMain(false).setKeyPrefix(true);
@@ -3841,32 +3827,7 @@ else if ("<>".equals(rt)) {
case "(": // ANTI JOIN: A & ! B
case ")": // FOREIGN JOIN: B & ! A
sql = " INNER JOIN " + jc.getTablePath();
- if (onList != null) {
- boolean first = true;
- for (On on : onList) {
- String rt = on.getRelateType();
- if (StringUtil.isEmpty(rt, false)) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " = "
- + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
- }
- else if ("{}".equals(rt)) {
- sql += (first ? ON : AND) + "json_contains(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote
- // + ", concat('\\'', " + quote + jt + quote + "." + quote + on.getKey() + quote + ", '\\''), '$')";
- + ", cast(" + quote + jt + quote + "." + quote + on.getKey() + quote + " AS CHAR), '$')";
- }
- else if ("<>".equals(rt)) {
- sql += (first ? ON : AND) + "json_contains(" + quote + jt + quote + "." + quote + on.getKey() + quote
- // + ", concat('\\'', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '\\''), '$')";
- + ", cast(" + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + " AS CHAR), '$')";
- }
- else {
- throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
- + " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
- }
-
- first = false;
- }
- }
+ sql = concatJoinOn(sql, quote, j, jt, onList);
break;
default:
throw new UnsupportedOperationException(
@@ -3902,6 +3863,140 @@ else if ("<>".equals(rt)) {
return StringUtil.isEmpty(joinOns, true) ? "" : joinOns + " \n";
}
+
+ protected String concatJoinOn(@NotNull String sql, @NotNull String quote, @NotNull Join j, @NotNull String jt, List onList) {
+ if (onList != null) {
+ 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);
+ }
+
+ 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;
+ }
+ else {
+ onJoinComplextRelation(sql, quote, j, jt, onList, on);
+
+ if (">=".equals(rt) || "<=".equals(rt) || ">".equals(rt) || "<".equals(rt)) {
+ if (isNot) {
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + " 中 JOIN ON 条件关联逻辑符 " + rt + " 不合法! >, <, >=, <= 不支持与或非逻辑符 & | ! !");
+ }
+
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + " " + rt + " "
+ + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
+ else if ("$".equals(rt)) {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ + " LIKE concat('%', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '%')";
+ }
+ else if (rt.endsWith("~")) {
+ boolean ignoreCase = "*~".equals(rt);
+ if (isPostgreSQL()) {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ + " ~" + (ignoreCase ? "* " : " ") + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
+ else if (isOracle()) {
+ sql += (first ? ON : AND) + "regexp_like(" + quote + jt + quote + "." + quote + on.getKey() + quote
+ + ", " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
+ }
+ else if (isClickHouse()) {
+ sql += (first ? ON : AND) + "match(" + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
+ + ", " + (ignoreCase ? "lower(" : "") + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "") + ")";
+ }
+ else if (isHive()) {
+ sql += (first ? ON : AND) + (ignoreCase ? "lower(" : "") + quote + jt + quote + "." + quote + on.getKey() + quote + (ignoreCase ? ")" : "")
+ + " REGEXP " + (ignoreCase ? "lower(" : "") + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + (ignoreCase ? ")" : "");
+ }
+ else {
+ sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
+ + " REGEXP " + (ignoreCase ? "" : "BINARY ") + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
+ }
+ }
+ else if ("{}".equals(rt) || "<>".equals(rt)) {
+ String tt = on.getTargetTable();
+ String ta = on.getTargetAlias();
+
+ Map cast = null;
+ if (tt.equals(getTable()) && ((ta == null && getAlias() == null) || ta.equals(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()))) {
+ cast = getCast();
+ find = true;
+ break;
+ }
+ }
+
+ if (find == false) {
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + " 中 JOIN ON 条件中找不到对应的 " + rt + " 不合法!只支持 =, {}, <> 这几种!");
+ }
+ }
+
+ boolean isBoolOrNum = SQL.isBooleanOrNumber(cast == null ? null : cast.get(on.getTargetKey()));
+
+ String arrKeyPath;
+ String itemKeyPath;
+ if ("{}".equals(rt)) {
+ arrKeyPath = quote + on.getTargetTable() + 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;
+ }
+
+ if (isPostgreSQL()) { //operator does not exist: jsonb @> character varying "[" + c + "]");
+ sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ + " IS NOT NULL AND " + arrKeyPath + " @> " + itemKeyPath) + (isNot ? ") " : "");
+ }
+ else if (isOracle()) {
+ sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ + " IS NOT NULL AND json_textcontains(" + arrKeyPath
+ + ", '$', " + itemKeyPath + ")") + (isNot ? ") " : "");
+ }
+ else if (isClickHouse()) {
+ sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ + " IS NOT NULL AND has(JSONExtractArrayRaw(assumeNotNull(" + arrKeyPath + "))"
+ + ", " + itemKeyPath + ")") + (isNot ? ") " : "");
+ }
+ else {
+ sql += (first ? ON : AND) + (isNot ? "( " : "") + getCondition(isNot, arrKeyPath
+ + " IS NOT NULL AND json_contains(" + arrKeyPath
+ + (isBoolOrNum ? ", cast(" + itemKeyPath + " AS CHAR), '$')"
+ : ", concat('\"', " + itemKeyPath + ", '\"'), '$')"
+ )
+ ) + (isNot ? ") " : "");
+ }
+ }
+ else {
+ throw new IllegalArgumentException("join:value 中 value 里的 " + jt + "/" + j.getPath()
+ + " 中 JOIN ON 条件关联类型 " + rt + " 不合法!只支持 =, >, <, >=, <=, !=, $, ~, {}, <> 这几种!");
+ }
+ }
+
+ first = false;
+ }
+ }
+
+ return sql;
+ }
+
+ protected void onJoinNotRelation(String sql, String quote, Join j, String jt, List onList, On on) {
+// throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
+ }
+ protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) {
+// throw new UnsupportedOperationException("JOIN 已禁用 {} 和 <> 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
+ }
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 13edbfc26..bd5c0cf00 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -745,6 +745,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
for (On on : onList) {
String ok = on.getOriginKey();
String vk = ok.substring(0, ok.length() - 1);
+ //TODO 兼容复杂关联
cc.putWhere(on.getKey(), result.get(on.getKey()), true);
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index 24ad36ec8..bd10ec8d7 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -10,6 +10,7 @@
import com.alibaba.fastjson.JSONObject;
import apijson.NotNull;
+import apijson.StringUtil;
/**连表 配置
* @author Lemon
@@ -163,7 +164,8 @@ public static class On {
private String originKey;
private String originValue;
- private String relateType; // "" - 一对一, "{}" - 一对多, "<>" - 多对一
+ private Logic logic; // & | !
+ private String relateType; // "" - 一对一, "{}" - 一对多, "<>" - 多对一, > , <= , !=
private String key; // id
private String targetTable; // Moment
private String targetAlias; // main
@@ -183,6 +185,12 @@ public void setOriginValue(String originValue) {
}
+ public Logic getLogic() {
+ return logic;
+ }
+ public void setLogic(Logic logic) {
+ this.logic = logic;
+ }
public String getRelateType() {
return relateType;
}
@@ -190,7 +198,6 @@ public void setRelateType(String relateType) {
this.relateType = relateType;
}
-
public String getKey() {
return key;
}
@@ -222,22 +229,63 @@ public void setKeyAndType(String joinType, String table, @NotNull String originK
originKey = originKey.substring(0, originKey.length() - 1);
}
else { //TODO 暂时只允许 User.id = Moment.userId 字段关联,不允许 User.id = 82001 这种
- throw new IllegalArgumentException(joinType + "/.../" + table + "/" + originKey + " 不合法!join:'.../refKey'" + " 中 refKey 必须以 @ 结尾!");
+ throw new IllegalArgumentException(joinType + "/.../" + table + "/" + originKey + " 中字符 " + originKey + " 不合法!join:'.../refKey'" + " 中 refKey 必须以 @ 结尾!");
}
+ String k;
+
if (originKey.endsWith("{}")) {
setRelateType("{}");
- setKey(originKey.substring(0, originKey.length() - 2));
+ k = originKey.substring(0, originKey.length() - 2);
}
else if (originKey.endsWith("<>")) {
setRelateType("<>");
- setKey(originKey.substring(0, originKey.length() - 2));
+ k = originKey.substring(0, originKey.length() - 2);
+ }
+ else if (originKey.endsWith("$")) {
+ setRelateType("$");
+ k = originKey.substring(0, originKey.length() - 1);
+ }
+ else if (originKey.endsWith("~")) {
+ boolean ignoreCase = originKey.endsWith("*~");
+ setRelateType(ignoreCase ? "*~" : "~");
+ k = originKey.substring(0, originKey.length() - (ignoreCase ? 2 : 1));
+ }
+ else if (originKey.endsWith(">=")) {
+ setRelateType(">=");
+ k = originKey.substring(0, originKey.length() - 2);
+ }
+ else if (originKey.endsWith("<=")) {
+ setRelateType("<=");
+ k = originKey.substring(0, originKey.length() - 2);
+ }
+ else if (originKey.endsWith(">")) {
+ setRelateType(">");
+ k = originKey.substring(0, originKey.length() - 1);
+ }
+ else if (originKey.endsWith("<")) {
+ setRelateType("<");
+ k = originKey.substring(0, originKey.length() - 1);
}
else {
setRelateType("");
- setKey(originKey);
+ k = originKey;
}
+
+ if (k != null && (k.contains("&") || k.contains("|"))) {
+ throw new UnsupportedOperationException(joinType + "/.../" + table + "/" + originKey + " 中字符 " + k + " 不合法!与或非逻辑符仅支持 '!' 非逻辑符 !");
+ }
+
+ Logic l = new Logic(k);
+ setLogic(l);
+
+ if (StringUtil.isName(l.getKey()) == false) {
+ throw new IllegalArgumentException(joinType + "/.../" + table + "/" + originKey + " 中字符 " + l.getKey() + " 不合法!必须符合字段命名格式!");
+ }
+
+ setKey(l.getKey());
}
+
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Logic.java b/APIJSONORM/src/main/java/apijson/orm/Logic.java
index b795ae961..cfc08d016 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Logic.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Logic.java
@@ -30,6 +30,7 @@ public class Logic {
private int type;
private String key;
+ private String originKey;
public Logic() {
super();
@@ -40,6 +41,7 @@ public Logic(int type) {
this.type = type;
}
public Logic(String key) {
+ this.originKey = key;
key = StringUtil.getString(key);
int type = getType(key.isEmpty() ? "" : key.substring(key.length() - 1));
@@ -71,6 +73,9 @@ public String getKey() {
public void setKey(String key) {
this.key = key;
}
+ public String getOriginKey() {
+ return originKey;
+ }
public boolean isOr() {
From 895917ba987e656d7866dd2f9a35b218e43758fb Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 7 Mar 2022 00:51:24 +0800
Subject: [PATCH 075/674] =?UTF-8?q?JOIN=20=E9=BB=98=E8=AE=A4=E7=A6=81?=
=?UTF-8?q?=E7=94=A8=20!=20=E9=9D=9E=E9=80=BB=E8=BE=91=E7=AC=A6=E5=92=8C?=
=?UTF-8?q?=E5=A4=8D=E6=9D=82=E5=85=B3=E8=81=94=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/AbstractSQLConfig.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 9dfc7d9eb..8dfcc425e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -3992,10 +3992,10 @@ else if (isClickHouse()) {
}
protected void onJoinNotRelation(String sql, String quote, Join j, String jt, List onList, On on) {
-// throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
+ throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}
protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) {
-// throw new UnsupportedOperationException("JOIN 已禁用 {} 和 <> 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
+ throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
}
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
From 3c8058ee27113963c56aba968c9815d9e32affd9 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 13 Mar 2022 15:52:29 +0800
Subject: [PATCH 076/674] Update README.md
---
README.md | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 8e8f72995..e0d4110cf 100644
--- a/README.md
+++ b/README.md
@@ -155,22 +155,21 @@ https://www.bilibili.com/video/BV1yv411p7Y4
前后端 关于接口的 开发、文档、联调 等 10 大痛点解析
https://github.com/Tencent/APIJSON/wiki
-* **解决十大痛点** (APIJSON 可帮助用户 提振开发效率、杜绝联调扯皮、规避文档缺陷、节省流量带宽 等)
-* **开发提速很大** (CRUD 零代码热更新自动化,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
+* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽等)
+* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
* **社区影响力大** (GitHub 1W+ Star 在 350W Java 项目中排名前 120,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **多样用户案例** (腾讯内部用户包含 互娱、音乐、云与智慧,外部用户包含 500 强上市公司、数千亿资本国企 等)
* **适用场景广泛** (社交聊天、阅读资讯、影音视频、办公学习 等各种 App、网站、公众号、小程序 等非金融类项目)
* **周边生态丰富** (Android, iOS, Web 等各种 Demo、继承 JSON 的海量生态、零代码 接口测试 和 单元测试 工具等)
* **文档视频齐全** (项目介绍、快速上手、安装部署 等后端、前端、客户端的 图文解说、视频教程、代码注释 等)
-* **功能丰富强大** (增删改查、分页排序、分组聚合、各种 JOIN、各种子查询、跨库跨表、性能分析 等零代码实现)
-* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防SQL注入等)
+* **功能丰富强大** (增删改查、分页排序、分组聚合、各种条件、各种 JOIN、各种子查询、跨库连表 等零代码实现)
+* **使用安全简单** (自动增删改查、自动生成文档、自动管理版本、自动控制权限、自动校验参数、自动防 SQL 注入等)
* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,商业分析软件源伞 Pinpoint 代码扫描报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
-* **多年持续迭代** (自 2016 年开源至今已连续维护 5 年,累计 2000+ Commits、80+ Releases,不断更新迭代中...)
-
+* **多年持续迭代** (自 2016 年开源至今已连续维护 5 年多,累计 2000+ Commits、80+ Releases,不断更新迭代中...)
### 常见问题
#### 1.如何定制业务逻辑?
From 29d8d1ef1f0848bb49dd73a2dc7d5fcf5daa97e5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 13 Mar 2022 21:50:57 +0800
Subject: [PATCH 077/674] =?UTF-8?q?JOIN=20ON=20=E5=8F=8A=E6=99=AE=E9=80=9A?=
=?UTF-8?q?=E6=9D=A1=E4=BB=B6=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E5=9C=A8?=
=?UTF-8?q?=20key$:value=20=E7=9A=84=20key=20=E4=B8=AD=E5=AE=9A=E5=88=B6?=
=?UTF-8?q?=E5=8D=A0=E4=BD=8D=E7=AC=A6=20%,=20=5F=20=E4=B8=8E=20value=20?=
=?UTF-8?q?=E7=9A=84=E6=8B=BC=E6=8E=A5=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 80 +++++++++++++++++--
.../src/main/java/apijson/orm/Join.java | 27 ++++++-
2 files changed, 99 insertions(+), 8 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 8dfcc425e..9e4a328bd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -3891,9 +3891,55 @@ 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;
}
- else if ("$".equals(rt)) {
- sql += (first ? ON : AND) + quote + jt + quote + "." + quote + on.getKey() + quote + (isNot ? NOT : "")
- + " LIKE concat('%', " + quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote + ", '%')";
+ else if (rt.endsWith("$")) {
+ String t = rt.substring(0, rt.length() - 1);
+ char r = t.isEmpty() ? 0 : t.charAt(t.length() - 1);
+
+ char l;
+ if (r == '%' || r == '_' || r == '?') {
+ t = t.substring(0, t.length() - 1);
+
+ if (t.isEmpty()) {
+ if (r == '?') {
+ throw new IllegalArgumentException(on.getOriginKey() + ":value 中字符 " + on.getOriginKey() + " 不合法!key$:value 中不允许只有单独的 '?',必须和 '%', '_' 之一配合使用 !");
+ }
+
+ l = r;
+ }
+ else {
+ l = t.charAt(t.length() - 1);
+ if (l == '%' || l == '_' || l == '?') {
+ if (l == r) {
+ throw new IllegalArgumentException(on.getOriginKey() + ":value 中字符 " + t + " 不合法!key$:value 中不允许 key 中有连续相同的占位符!");
+ }
+
+ t = t.substring(0, t.length() - 1);
+ }
+ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
+ l = r;
+ }
+ }
+
+ if (l == '?') {
+ l = 0;
+ }
+ if (r == '?') {
+ r = 0;
+ }
+ }
+ else {
+ l = r = 0;
+ }
+
+ 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;
+ }
+ 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
+ + "." + quote + on.getTargetKey() + quote + (r <= 0 ? ")" : ", '" + r + "')");
+ }
}
else if (rt.endsWith("~")) {
boolean ignoreCase = "*~".equals(rt);
@@ -3992,10 +4038,10 @@ else if (isClickHouse()) {
}
protected void onJoinNotRelation(String sql, String quote, Join j, String jt, List onList, On on) {
- throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
+// throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}
protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) {
- throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
+// throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
}
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
@@ -4599,7 +4645,27 @@ public static String getRealKey(RequestMethod method, String originKey
String key = new String(originKey);
if (key.endsWith("$")) {//搜索 LIKE,查询时处理
- key = key.substring(0, key.length() - 1);
+ 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%'
+ char c = k.isEmpty() ? 0 : k.charAt(k.length() - 1);
+
+ if (c == '%' || c == '_' || c == '?') {
+ k = k.substring(0, k.length() - 1);
+
+ char c2 = k.isEmpty() ? 0 : k.charAt(k.length() - 1);
+ if (c2 == '%' || c2 == '_' || c2 == '?') {
+ if (c2 == c) {
+ throw new IllegalArgumentException(originKey + ":value 中字符 " + k + " 不合法!key$:value 中不允许 key 中有连续相同的占位符!");
+ }
+
+ k = k.substring(0, k.length() - 1);
+ }
+ else if (c == '?') {
+ throw new IllegalArgumentException(originKey + ":value 中字符 " + originKey + " 不合法!key$:value 中不允许只有单独的 '?',必须和 '%', '_' 之一配合使用 !");
+ }
+ }
+
+ key = k;
}
else if (key.endsWith("~")) {//匹配正则表达式 REGEXP,查询时处理
key = key.substring(0, key.length() - 1);
@@ -4648,6 +4714,8 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理
}
}
+ //TODO if (key.endsWith("-")) { // 表示 key 和 value 顺序反过来: value LIKE key
+
String last = null;//不用Logic优化代码,否则 key 可能变为 key| 导致 key=value 变成 key|=value 而出错
if (RequestMethod.isQueryMethod(method)) {//逻辑运算符仅供GET,HEAD方法使用
last = key.isEmpty() ? "" : key.substring(key.length() - 1);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index bd10ec8d7..4fb34b0a9 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -242,9 +242,30 @@ else if (originKey.endsWith("<>")) {
setRelateType("<>");
k = originKey.substring(0, originKey.length() - 2);
}
- else if (originKey.endsWith("$")) {
- setRelateType("$");
+ else if (originKey.endsWith("$")) { // key%$:"a" -> key LIKE '%a%'; key?%$:"a" -> key LIKE 'a%'; key_?$:"a" -> key LIKE '_a'; key_%$:"a" -> key LIKE '_a%'
k = originKey.substring(0, originKey.length() - 1);
+ char c = k.isEmpty() ? 0 : k.charAt(k.length() - 1);
+
+ String t = "$";
+ if (c == '%' || c == '_' || c == '?') {
+ t = c + t;
+ k = k.substring(0, k.length() - 1);
+
+ char c2 = k.isEmpty() ? 0 : k.charAt(k.length() - 1);
+ if (c2 == '%' || c2 == '_' || c2 == '?') {
+ if (c2 == c) {
+ throw new IllegalArgumentException(originKey + ":value 中字符 " + k + " 不合法!key$:value 中不允许 key 中有连续相同的占位符!");
+ }
+
+ t = c2 + t;
+ k = k.substring(0, k.length() - 1);
+ }
+ else if (c == '?') {
+ throw new IllegalArgumentException(originKey + ":value 中字符 " + originKey + " 不合法!key$:value 中不允许只有单独的 '?',必须和 '%', '_' 之一配合使用 !");
+ }
+ }
+
+ setRelateType(t);
}
else if (originKey.endsWith("~")) {
boolean ignoreCase = originKey.endsWith("*~");
@@ -276,6 +297,8 @@ else if (originKey.endsWith("<")) {
throw new UnsupportedOperationException(joinType + "/.../" + table + "/" + originKey + " 中字符 " + k + " 不合法!与或非逻辑符仅支持 '!' 非逻辑符 !");
}
+ //TODO if (c3 == '-') { // 表示 key 和 value 顺序反过来: value LIKE key
+
Logic l = new Logic(k);
setLogic(l);
From 96ee9dd23c5b0fdec988c377fdcc3f260718b3f7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 13 Mar 2022 23:52:29 +0800
Subject: [PATCH 078/674] =?UTF-8?q?LIKE:=20=E6=94=AF=E6=8C=81=E9=9D=9E=20J?=
=?UTF-8?q?OIN=20ON=20=E5=BC=95=E7=94=A8=E8=B5=8B=E5=80=BC=E4=B9=9F?=
=?UTF-8?q?=E8=83=BD=E7=94=A8=20key%$:value=20=E6=A0=BC=E5=BC=8F=EF=BC=8C?=
=?UTF-8?q?=E5=B9=B6=E4=B8=94=E7=BB=99=20key%$:"%"=20=E4=B8=AD=E7=9A=84?=
=?UTF-8?q?=E7=89=B9=E6=AE=8A=E7=AC=A6=E5=8F=B7=E8=BD=AC=E4=B9=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 52 +++++++++++++++++--
1 file changed, 49 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 9e4a328bd..5598c9685 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2982,7 +2982,7 @@ public Object getSQLValue(@NotNull Object value) {
return SQL.NULL;
}
// return (value instanceof Number || value instanceof Boolean) && DATABASE_POSTGRESQL.equals(getDatabase()) ? value : "'" + value + "'";
- return (value instanceof Number || value instanceof Boolean) ? value : "'" + value.toString().replaceAll("'", "\\'") + "'"; //MySQL 隐式转换用不了索引
+ return (value instanceof Number || value instanceof Boolean) ? value : "'" + value.toString().replaceAll("\\'", "\\\\'") + "'"; //MySQL 隐式转换用不了索引
}
@Override
@@ -3044,7 +3044,7 @@ public String getSearchString(String key, String column, Object[] values, int ty
// throw new IllegalArgumentException(key + "$:value 中 value 值 " + v + " 中包含 %% !不允许有连续的 % !");
// }
- condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getLikeString(key, column, v);
+ condition += (i <= 0 ? "" : (Logic.isAnd(type) ? AND : OR)) + getLikeString(key, column, (String) v);
}
return getCondition(Logic.isNot(type), condition);
@@ -3057,7 +3057,53 @@ public String getSearchString(String key, String column, Object[] values, int ty
* @return key LIKE 'value'
*/
@JSONField(serialize = false)
- public String getLikeString(String key, String column, Object value) {
+ public String getLikeString(@NotNull String key, @NotNull String column, String value) {
+ String k = key.substring(0, key.length() - 1);
+ char r = k.charAt(k.length() - 1);
+
+ char l;
+ if (r == '%' || r == '_' || r == '?') {
+ k = k.substring(0, k.length() - 1);
+
+ l = k.charAt(k.length() - 1);
+ if (l == '%' || l == '_' || l == '?') {
+ if (l == r) {
+ throw new IllegalArgumentException(key + ":value 中字符 " + k + " 不合法!key$:value 中不允许 key 中有连续相同的占位符!");
+ }
+
+ k = k.substring(0, k.length() - 1);
+ }
+ else if (l > 0 && StringUtil.isName(String.valueOf(l))) {
+ l = r;
+ }
+
+ if (l == '?') {
+ l = 0;
+ }
+ if (r == '?') {
+ r = 0;
+ }
+ }
+ else {
+ l = r = 0;
+ }
+
+ if (l > 0 || r > 0) {
+ if (value == null) {
+ throw new IllegalArgumentException(key + ":value 中 value 为 null!key$:value 中 value 不能为 null,且类型必须是 String !");
+ }
+
+ value = value.replaceAll("\\\\", "\\\\\\\\");
+ value = value.replaceAll("\\%", "\\\\%");
+ value = value.replaceAll("\\_", "\\\\_");
+ if (l > 0) {
+ value = l + value;
+ }
+ if (r > 0) {
+ value = value + r;
+ }
+ }
+
return getKey(column) + " LIKE " + getValue(key, column, value);
}
From 38c19975ea67efb07c1ea71a52b4556614e242e5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 14 Mar 2022 00:07:07 +0800
Subject: [PATCH 079/674] =?UTF-8?q?*=20CROSS=20JOIN=20=E5=85=81=E8=AE=B8?=
=?UTF-8?q?=E6=B2=A1=E6=9C=89=20JOIN=20ON=20=E5=BC=95=E7=94=A8=E8=B5=8B?=
=?UTF-8?q?=E5=80=BC=E5=85=B3=E8=81=94=E6=9D=A1=E4=BB=B6=EF=BC=9B=E9=BB=98?=
=?UTF-8?q?=E8=AE=A4=E7=A6=81=E7=94=A8=20JOIN=20ON=20=E5=A4=8D=E6=9D=82?=
=?UTF-8?q?=E5=85=B3=E8=81=94=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 +-
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 5bfa7da93..733414049 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1494,7 +1494,7 @@ else if (join != null){
}
Set> refSet = refObj.entrySet();
- if (refSet.isEmpty()) {
+ if (refSet.isEmpty() && "*".equals(joinType) == false) {
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 alias 值 " + alias + " 不合法!"
+ "必须为 &/Table0, onList, On on) {
-// throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
+ throw new UnsupportedOperationException("JOIN 已禁用 '!' 非逻辑连接符 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}
protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) {
-// throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许等价关联,如要取消禁用可在后端重写相关方法!");
+ throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许 = 等价关联,如要取消禁用可在后端重写相关方法!");
}
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
From c39cd1ec7c5b568cfbeac19e82e4688a9c26a679 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 14 Mar 2022 01:55:32 +0800
Subject: [PATCH 080/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E7=BB=84?=
=?UTF-8?q?=E5=85=B3=E9=94=AE=E8=AF=8D=20compat=20=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E5=AF=B9=E8=81=9A=E5=90=88=E5=87=BD=E6=95=B0=E5=AD=97=E6=AE=B5?=
=?UTF-8?q?=E9=80=9A=E8=BF=87=20query:2=20=E5=88=86=E9=A1=B5=E6=9F=A5?=
=?UTF-8?q?=E6=80=BB=E6=95=B0=E8=BF=94=E5=9B=9E=E5=80=BC=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/JSONRequest.java | 2 +
APIJSONORM/src/main/java/apijson/SQL.java | 33 ++++----
.../main/java/apijson/orm/AbstractParser.java | 35 +++++++-
.../java/apijson/orm/AbstractSQLConfig.java | 81 +++++++++++++++----
.../src/main/java/apijson/orm/SQLConfig.java | 3 +
5 files changed, 120 insertions(+), 34 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java
index 5b49f608e..8707cc2c0 100755
--- a/APIJSONORM/src/main/java/apijson/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java
@@ -87,6 +87,7 @@ public JSONRequest setFormat(Boolean format) {
public static final String SUBQUERY_RANGE_ANY = "ANY";
public static final String KEY_QUERY = "query";
+ public static final String KEY_COMPAT = "compat";
public static final String KEY_COUNT = "count";
public static final String KEY_PAGE = "page";
public static final String KEY_JOIN = "join";
@@ -97,6 +98,7 @@ public JSONRequest setFormat(Boolean format) {
static {
ARRAY_KEY_LIST = new ArrayList();
ARRAY_KEY_LIST.add(KEY_QUERY);
+ ARRAY_KEY_LIST.add(KEY_COMPAT);
ARRAY_KEY_LIST.add(KEY_COUNT);
ARRAY_KEY_LIST.add(KEY_PAGE);
ARRAY_KEY_LIST.add(KEY_JOIN);
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 4f2a1ccb4..391d5db48 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -21,7 +21,7 @@ public class SQL {
public static final String IS_NOT = " IS NOT ";
public static final String IS_NULL = " IS NULL ";
public static final String IS_NOT_NULL = " IS NOT NULL ";
-
+
//括号必须紧跟函数名! count (...) 报错!
public static final String COUNT = "count";
public static final String SUM = "sum";
@@ -191,7 +191,7 @@ public static String indexOf(String s, String c) {
public static String replace(String s, String c1, String c2) {
return "replace(" + s + ", " + c1 + ", " + c2 + ")";
}
-
+
/**
* @param s1
* @param s2
@@ -225,11 +225,11 @@ public static String toLowerCase(String s) {
return "lower(" + s + ")";
}
-
-
+
+
//column and function<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
+
/**字段
* @param column
* @return column.isEmpty() ? "*" : column;
@@ -245,15 +245,16 @@ public static String column(String column) {
public static String columnAs(String column) {
return count(column) + AS;
}
-
+
/**函数
* @param column if (StringUtil.isEmpty(column, true) || column.contains(",")) -> column = null;
* @return " " + fun + "(" + {@link #column(String)} + ") ";
*/
public static String function(String fun, String column) {
- if (StringUtil.isEmpty(column, true) || column.contains(",")) {
- column = null; //解决 count(id,name) 这种多个字段导致的SQL异常
- }
+ // 支持 fun(col1,col2..)
+ // if (StringUtil.isEmpty(column, true) || column.contains(",")) {
+ // column = null; //解决 count(id,name) 这种多个字段导致的SQL异常
+ // }
return " " + fun + "(" + column(column) + ") ";
}
/**有别名的函数
@@ -263,7 +264,7 @@ public static String function(String fun, String column) {
public static String functionAs(String fun, String column) {
return function(fun, column) + AS + fun + " ";
}
-
+
/**计数
* column = null
* @return {@link #count(String)}
@@ -313,9 +314,9 @@ public static String avg(String column) {
}
//column and function>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-
-
-
+
+
+
//search<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public static final int SEARCH_TYPE_CONTAIN_FULL = 0;
@@ -391,13 +392,13 @@ public static String search(String s, int type, boolean ignoreCase) {
//search>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-
+
public static boolean isBooleanOrNumber(String type) {
type = StringUtil.toUpperCase(type, true);
return type.isEmpty() || (type.endsWith("INT") && type.endsWith("POINT") == false)
|| type.endsWith("BOOLEAN") || type.endsWith("ENUM")
|| type.endsWith("FLOAT") || type.endsWith("DOUBLE") || type.endsWith("DECIMAL");
}
-
-
+
+
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 733414049..2d0f7748c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -42,6 +42,7 @@
import apijson.Log;
import apijson.NotNull;
import apijson.RequestMethod;
+import apijson.SQL;
import apijson.StringUtil;
import apijson.orm.exception.ConditionErrorException;
import apijson.orm.exception.ConflictException;
@@ -1051,9 +1052,33 @@ public JSONObject onObjectParse(final JSONObject request
//total 这里不能用arrayConfig.getType(),因为在createObjectParser.onChildParse传到onObjectParse时已被改掉
if (type == SQLConfig.TYPE_ITEM_CHILD_0 && query != JSONRequest.QUERY_TABLE && position == 0) {
- RequestMethod method = op.getMethod();
- JSONObject rp = op.setMethod(RequestMethod.HEAD).setSQLConfig().executeSQL().getSqlReponse();
- op.setMethod(method);
+ JSONObject rp;
+ Boolean compat = arrayConfig.getCompat();
+ if (compat != null && compat) {
+ // 解决对聚合函数字段通过 query:2 分页查总数返回值错误
+ // 这里可能改变了内部的一些数据,下方通过 arrayConfig 还原
+ SQLConfig cfg = op.setSQLConfig(0, 0, 0).getSQLConfig();
+ boolean isExplain = cfg.isExplain();
+ cfg.setExplain(false);
+
+ Subquery subq = new Subquery();
+ subq.setFrom(cfg.getTable());
+ subq.setConfig(cfg);
+
+ SQLConfig countSQLCfg = createSQLConfig();
+ countSQLCfg.setColumn(Arrays.asList("count(*):count"));
+ countSQLCfg.setFrom(subq);
+
+ rp = executeSQL(countSQLCfg, false);
+
+ cfg.setExplain(isExplain);
+ }
+ else {
+ // 对聚合函数字段通过 query:2 分页查总数返回值错误
+ RequestMethod method = op.getMethod();
+ rp = op.setMethod(RequestMethod.HEAD).setSQLConfig().executeSQL().getSqlReponse();
+ op.setMethod(method);
+ }
if (rp != null) {
int index = parentPath.lastIndexOf("]/");
@@ -1147,6 +1172,7 @@ public JSONArray onArrayParse(JSONObject request, String parentPath, String name
//不能改变,因为后面可能继续用到,导致1以上都改变 []:{0:{Comment[]:{0:{Comment:{}},1:{...},...}},1:{...},...}
final String query = request.getString(JSONRequest.KEY_QUERY);
+ final Boolean compat = request.getBoolean(JSONRequest.KEY_COMPAT);
final Integer count = request.getInteger(JSONRequest.KEY_COUNT); //TODO 如果不想用默认数量可以改成 getIntValue(JSONRequest.KEY_COUNT);
final Integer page = request.getInteger(JSONRequest.KEY_PAGE);
final Object join = request.get(JSONRequest.KEY_JOIN);
@@ -1189,6 +1215,7 @@ public JSONArray onArrayParse(JSONObject request, String parentPath, String name
}
request.remove(JSONRequest.KEY_QUERY);
+ request.remove(JSONRequest.KEY_COMPAT);
request.remove(JSONRequest.KEY_COUNT);
request.remove(JSONRequest.KEY_PAGE);
request.remove(JSONRequest.KEY_JOIN);
@@ -1227,6 +1254,7 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
.setCount(size)
.setPage(page2)
.setQuery(query2)
+ .setCompat(compat)
.setTable(arrTableKey)
.setJoinList(onJoinParse(join, request));
@@ -1301,6 +1329,7 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
} finally {
//后面还可能用到,要还原
request.put(JSONRequest.KEY_QUERY, query);
+ request.put(JSONRequest.KEY_COMPAT, compat);
request.put(JSONRequest.KEY_COUNT, count);
request.put(JSONRequest.KEY_PAGE, page);
request.put(JSONRequest.KEY_JOIN, join);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 15482a889..2b5f1760f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -104,6 +104,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// 自定义原始 SQL 片段 Map:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL
public static final Map RAW_MAP;
// 允许调用的 SQL 函数:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL
+ public static final Map SQL_AGGREGATE_FUNCTION_MAP;
public static final Map SQL_FUNCTION_MAP;
@@ -242,6 +243,13 @@ public abstract class AbstractSQLConfig implements SQLConfig {
+ SQL_AGGREGATE_FUNCTION_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况
+ SQL_AGGREGATE_FUNCTION_MAP.put("max", "");
+ SQL_AGGREGATE_FUNCTION_MAP.put("min", "");
+ SQL_AGGREGATE_FUNCTION_MAP.put("avg", "");
+ SQL_AGGREGATE_FUNCTION_MAP.put("count", "");
+ SQL_AGGREGATE_FUNCTION_MAP.put("sum", "");
+
SQL_FUNCTION_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况
//窗口函数
@@ -761,6 +769,7 @@ public String getUserIdKey() {
private int page; //Table所在页码
private int position; //Table在[]中的位置
private int query; //JSONRequest.query
+ private Boolean compat; //JSONRequest.compat query total
private int type; //ObjectParser.type
private int cache;
private boolean explain;
@@ -1458,14 +1467,9 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
case HEAD:
case HEADS: //StringUtil.isEmpty(column, true) || column.contains(",") 时SQL.count(column)会return "*"
if (isPrepared() && column != null) {
-
List raw = getRaw();
boolean containRaw = raw != null && raw.contains(KEY_COLUMN);
-
- String origin;
- String alias;
- int index;
-
+
for (String c : column) {
if (containRaw) {
// 由于 HashMap 对 key 做了 hash 处理,所以 get 比 containsValue 更快
@@ -1476,9 +1480,9 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
}
}
- index = c.lastIndexOf(":"); //StringUtil.split返回数组中,子项不会有null
- origin = index < 0 ? c : c.substring(0, index);
- alias = index < 0 ? null : c.substring(index + 1);
+ int index = c.lastIndexOf(":"); //StringUtil.split返回数组中,子项不会有null
+ String origin = index < 0 ? c : c.substring(0, index);
+ String alias = index < 0 ? null : c.substring(index + 1);
if (alias != null && StringUtil.isName(alias) == false) {
throw new IllegalArgumentException("HEAD请求: 字符 " + alias + " 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项"
@@ -1499,8 +1503,41 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
}
}
}
+
+ boolean onlyOne = column != null && column.size() == 1;
+ String c0 = onlyOne ? column.get(0) : null;
+
+ if (onlyOne) {
+ int index = c0 == null ? -1 : c0.lastIndexOf(":");
+ if (index > 0) {
+ c0 = c0.substring(0, index);
+ }
+
+ int start = c0 == null ? -1 : c0.indexOf("(");
+ int end = start <= 0 ? -1 : c0.lastIndexOf(")");
+ 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`
+ 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);
+ }
- return SQL.count(column != null && column.size() == 1 && StringUtil.isName(column.get(0)) ? getKey(column.get(0)) : "*");
+ List raw = getRaw();
+ boolean containRaw = raw != null && raw.contains(KEY_COLUMN);
+
+ return SQL.count(parseColumn(c0, containRaw));
+ }
+ }
+
+ return SQL.count(onlyOne ? getKey(c0) : "*");
+ // return SQL.count(onlyOne && StringUtil.isName(column.get(0)) ? getKey(column.get(0)) : "*");
case POST:
if (column == null || column.isEmpty()) {
throw new IllegalArgumentException("POST 请求必须在Table内设置要保存的 key:value !");
@@ -1997,6 +2034,16 @@ public AbstractSQLConfig setQuery(int query) {
this.query = query;
return this;
}
+ @Override
+ public Boolean getCompat() {
+ return compat;
+ }
+ @Override
+ public AbstractSQLConfig setCompat(Boolean compat) {
+ this.compat = compat;
+ return this;
+ }
+
@Override
public int getType() {
return type;
@@ -3753,14 +3800,13 @@ private static String getConditionString(String column, String table, AbstractSQ
//根据方法不同,聚合语句不同。GROUP BY 和 HAVING 可以加在 HEAD 上, HAVING 可以加在 PUT, DELETE 上,GET 全加,POST 全都不加
String aggregation = "";
- if (RequestMethod.isGetMethod(config.getMethod(), true)){
- aggregation = config.getGroupString(true) + config.getHavingString(true) +
- config.getOrderString(true);
+ if (RequestMethod.isGetMethod(config.getMethod(), true)) {
+ aggregation = config.getGroupString(true) + config.getHavingString(true) + config.getOrderString(true);
}
- if (RequestMethod.isHeadMethod(config.getMethod(), true)){
+ if (RequestMethod.isHeadMethod(config.getMethod(), true)) { // TODO 加参数 isPagenation 判断是 GET 内分页 query:2 查总数,不用加这些条件
aggregation = config.getGroupString(true) + config.getHavingString(true) ;
}
- if (config.getMethod() == PUT || config.getMethod() == DELETE){
+ if (config.getMethod() == PUT || config.getMethod() == DELETE) {
aggregation = config.getHavingString(true) ;
}
@@ -3825,6 +3871,8 @@ public String getJoinString() throws Exception {
// 主表不用别名 String ta;
for (Join j : joinList) {
+ onGetJoinString(j);
+
if (j.isAppJoin()) { // APP JOIN,只是作为一个标记,执行完主表的查询后自动执行副表的查询 User.id IN($commentIdList)
continue;
}
@@ -4089,6 +4137,9 @@ protected void onJoinNotRelation(String sql, String quote, Join j, String jt, Li
protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) {
throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许 = 等价关联,如要取消禁用可在后端重写相关方法!");
}
+
+ protected void onGetJoinString(Join j) throws UnsupportedOperationException {
+ }
protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 259788770..51a8df36b 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -98,6 +98,9 @@ public interface SQLConfig {
int getQuery();
SQLConfig setQuery(int query);
+ Boolean getCompat();
+ SQLConfig setCompat(Boolean compat);
+
int getPosition();
SQLConfig setPosition(int position);
From fca75602b01a8deb5b3a88f6e39e96ba9806c296 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 14 Mar 2022 01:59:47 +0800
Subject: [PATCH 081/674] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=85=B3=E4=BA=8E?=
=?UTF-8?q?=E5=88=86=E9=A1=B5=E6=9F=A5=E6=80=BB=E6=95=B0=E6=97=B6=20@colum?=
=?UTF-8?q?n:"max(id)"=20=E8=BF=99=E7=A7=8D=E8=81=9A=E5=90=88=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=E7=9A=84=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?=
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, 2 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 2d0f7748c..5e49d5c02 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1052,6 +1052,8 @@ public JSONObject onObjectParse(final JSONObject request
//total 这里不能用arrayConfig.getType(),因为在createObjectParser.onChildParse传到onObjectParse时已被改掉
if (type == SQLConfig.TYPE_ITEM_CHILD_0 && query != JSONRequest.QUERY_TABLE && position == 0) {
+ //TODO 应在这里判断 @column 中是否有聚合函数,而不是 AbstractSQLConfig.getColumnString
+
JSONObject rp;
Boolean compat = arrayConfig.getCompat();
if (compat != null && compat) {
From a3d9c90a8dc2ba2949fa824f08087ea67390952d Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 21 Mar 2022 00:26:22 +0800
Subject: [PATCH 082/674] =?UTF-8?q?=E5=8C=85=E5=90=AB=E9=80=89=E9=A1=B9?=
=?UTF-8?q?=E8=8C=83=E5=9B=B4=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E4=BC=A0?=
=?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=8C=E4=BE=8B=E5=A6=82=20key<>:{=20path:?=
=?UTF-8?q?=20"$",=20value:82001=20}?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractObjectParser.java | 2 +-
APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 590e016dc..003aee026 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -255,7 +255,7 @@ public AbstractObjectParser parse(String name, boolean isReuse) throws Exception
key = entry.getKey();
try {
- if (key.startsWith("@") || key.endsWith("@")) {
+ if (key.startsWith("@") || key.endsWith("@") || (key.endsWith("<>") && value instanceof JSONObject)) {
if (onParse(key, value) == false) {
invalidate();
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index 2b5f1760f..8cd68bacd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -4487,8 +4487,8 @@ else if (w.startsWith("!")) {
for (String key : set) {
value = request.get(key);
- if (value instanceof Map) {//只允许常规Object
- throw new IllegalArgumentException("不允许 " + key + " 等任何key的value类型为 {JSONObject} !");
+ if (key.endsWith("<>") == false && value instanceof Map) {//只允许常规Object
+ throw new IllegalArgumentException(table + ":{ " + key + ":value } 中 value 类型错误!除了 key<>:{} 外,不允许 " + key + " 等其它任何 key 对应 value 的类型为 JSONObject {} !");
}
//解决AccessVerifier新增userId没有作为条件,而是作为内容,导致PUT,DELETE出错
From 9776408d63bc1d768cdd97d910f6b2243b2a94a8 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 21 Mar 2022 00:26:48 +0800
Subject: [PATCH 083/674] =?UTF-8?q?@having=20=E6=94=AF=E6=8C=81=E5=A4=8D?=
=?UTF-8?q?=E6=9D=82=E6=9D=A1=E4=BB=B6=E7=BB=84=E5=90=88=EF=BC=8C=E4=B8=94?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20@having&=20=E7=AE=80=E5=8C=96=20AND=20?=
=?UTF-8?q?=E8=BF=9E=E6=8E=A5=E7=9A=84=E5=86=99=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/JSONObject.java | 11 +-
.../main/java/apijson/orm/AbstractParser.java | 9 +-
.../java/apijson/orm/AbstractSQLConfig.java | 749 +++++++++++-------
.../src/main/java/apijson/orm/SQLConfig.java | 7 +-
4 files changed, 468 insertions(+), 308 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONObject.java b/APIJSONORM/src/main/java/apijson/JSONObject.java
index 8000e231b..6825a02de 100755
--- a/APIJSONORM/src/main/java/apijson/JSONObject.java
+++ b/APIJSONORM/src/main/java/apijson/JSONObject.java
@@ -147,6 +147,7 @@ public JSONObject setUserIdIn(List list) {
public static final String KEY_COMBINE = "@combine"; //条件组合,每个条件key前面可以放&,|,!逻辑关系 "id!{},&sex,!name&$"
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_ORDER = "@order"; //排序方式
public static final String KEY_RAW = "@raw"; // 自定义原始 SQL 片段
public static final String KEY_JSON = "@json"; //SQL Server 把字段转为 JSON 输出
@@ -167,6 +168,7 @@ public JSONObject setUserIdIn(List list) {
TABLE_KEY_LIST.add(KEY_COMBINE);
TABLE_KEY_LIST.add(KEY_GROUP);
TABLE_KEY_LIST.add(KEY_HAVING);
+ TABLE_KEY_LIST.add(KEY_HAVING_AND);
TABLE_KEY_LIST.add(KEY_ORDER);
TABLE_KEY_LIST.add(KEY_RAW);
TABLE_KEY_LIST.add(KEY_JSON);
@@ -350,7 +352,14 @@ public JSONObject setHaving(String... keys) {
* @return
*/
public JSONObject setHaving(String keys) {
- return puts(KEY_HAVING, keys);
+ return setHaving(keys, false);
+ }
+ /**set keys for having
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ public JSONObject setHaving(String keys, boolean isAnd) {
+ return puts(isAnd ? KEY_HAVING_AND : KEY_HAVING, keys);
}
/**set keys for order by
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 5e49d5c02..6e407144e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1063,13 +1063,13 @@ public JSONObject onObjectParse(final JSONObject request
boolean isExplain = cfg.isExplain();
cfg.setExplain(false);
- Subquery subq = new Subquery();
- subq.setFrom(cfg.getTable());
- subq.setConfig(cfg);
+ Subquery subqy = new Subquery();
+ subqy.setFrom(cfg.getTable());
+ subqy.setConfig(cfg);
SQLConfig countSQLCfg = createSQLConfig();
countSQLCfg.setColumn(Arrays.asList("count(*):count"));
- countSQLCfg.setFrom(subq);
+ countSQLCfg.setFrom(subqy);
rp = executeSQL(countSQLCfg, false);
@@ -1358,6 +1358,7 @@ else if (childKeys.length == 1 && JSONRequest.isTableKey(childKeys[0])) { //
JOIN_COPY_KEY_LIST.add(JSONRequest.KEY_COMBINE);
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_ORDER);
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 8cd68bacd..c9c64b61e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -15,6 +15,7 @@
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;
@@ -32,8 +33,8 @@
import static apijson.RequestMethod.PUT;
import static apijson.SQL.AND;
import static apijson.SQL.NOT;
-import static apijson.SQL.OR;
import static apijson.SQL.ON;
+import static apijson.SQL.OR;
import java.util.ArrayList;
import java.util.Arrays;
@@ -80,8 +81,20 @@
public abstract class AbstractSQLConfig implements SQLConfig {
private static final String TAG = "AbstractSQLConfig";
- public static int MAX_COMBINE_DEPTH = 2;
+ /**
+ * 为 true 则兼容 5.0 之前 @having:"toId>0;avg(id)<100000" 默认 AND 连接,为 HAVING toId>0 AND avg(id)<100000;
+ * 否则按 5.0+ 新版默认 OR 连接,为 HAVING toId>0 OR avg(id)<100000,使用 @having& 或 @having:{ @combine: null } 时才用 AND 连接
+ */
+ public static boolean IS_HAVING_DEFAULT_AND = false;
+ /**
+ * 为 true 则兼容 5.0 之前 @having:"toId>0" 这种不包含 SQL 函数的表达式;
+ * 否则按 5.0+ 新版不允许,可以用 @having:"(toId)>0" 替代
+ */
+ public static boolean IS_HAVING_ALLOW_NOT_FUNCTION = false;
+
+ public static int MAX_HAVING_COUNT = 5;
public static int MAX_WHERE_COUNT = 10;
+ public static int MAX_COMBINE_DEPTH = 2;
public static int MAX_COMBINE_COUNT = 5;
public static int MAX_COMBINE_KEY_COUNT = 2;
public static float MAX_COMBINE_RATIO = 1.0f;
@@ -750,7 +763,8 @@ public String getUserIdKey() {
private String table; //表名
private String alias; //表别名
private String group; //分组方式的字符串数组,','分隔
- private String having; //聚合函数的字符串数组,','分隔
+ private String havingCombine; //聚合函数的字符串数组,','分隔
+ private Map having; //聚合函数的字符串数组,','分隔
private String order; //排序方式的字符串数组,','分隔
private List raw; //需要保留原始 SQL 的字段,','分隔
private List json; //需要转为 JSON 的字段,','分隔
@@ -1101,24 +1115,36 @@ public String getGroupString(boolean hasPrefix) {
return (hasPrefix ? " GROUP BY " : "") + StringUtil.concat(StringUtil.getString(keys), joinGroup, ", ");
}
+
+ @Override
+ public String getHavingCombine() {
+ return havingCombine;
+ }
+ @Override
+ public SQLConfig setHavingCombine(String havingCombine) {
+ this.havingCombine = havingCombine;
+ return this;
+ }
@Override
- public String getHaving() {
+ public Map getHaving() {
return having;
}
- public AbstractSQLConfig setHaving(String... conditions) {
- return setHaving(StringUtil.getString(conditions));
- }
@Override
- public AbstractSQLConfig setHaving(String having) {
+ public SQLConfig setHaving(Map having) {
this.having = having;
return this;
}
+ public AbstractSQLConfig setHaving(String... conditions) {
+ return setHaving(StringUtil.getString(conditions));
+ }
+
/**TODO @having 改为默认 | 或连接,且支持 @having: { "key1>": 1, "key{}": "length(key2)>0", "@combine": "key1,key2" }
* @return HAVING conditoin0 AND condition1 OR condition2 ...
+ * @throws Exception
*/
@JSONField(serialize = false)
- public String getHavingString(boolean hasPrefix) {
+ public String getHavingString(boolean hasPrefix) throws Exception {
//加上子表的 having
String joinHaving = "";
if (joinList != null) {
@@ -1146,122 +1172,120 @@ public String getHavingString(boolean hasPrefix) {
}
}
- String[] keys = StringUtil.split(getHaving(), ";");
- if (keys == null || keys.length <= 0) {
+ Map map = getHaving();
+ Set> set = map == null ? null : map.entrySet();
+ if (set == null || set.isEmpty()) {
return StringUtil.isEmpty(joinHaving, true) ? "" : (hasPrefix ? " HAVING " : "") + joinHaving;
}
- String quote = getQuote();
- String tableAlias = getAliasWithQuote();
-
List raw = getRaw();
+ // 提前把 @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);
-
- String expression;
- String method;
- //暂时不允许 String prefix;
- String suffix;
+
+ // 直接把 having 类型从 Map 定改为 Map,避免额外拷贝
+ // Map newMap = new LinkedHashMap<>(map.size());
+ // for (Entry entry : set) {
+ // newMap.put(entry.getKey(), entry.getValue());
+ // }
//fun0(arg0,arg1,...);fun1(arg0,arg1,...)
- for (int i = 0; i < keys.length; i++) {
+ String havingString = parseCombineExpression(getMethod(), getQuote(), getTable(), getAliasWithQuote(), map, getHavingCombine(), true, containRaw, true);
- //fun(arg0,arg1,...)
- expression = keys[i];
- if (containRaw) {
- try {
- String rawSQL = getRawSQL(KEY_HAVING, expression);
- if (rawSQL != null) {
- keys[i] = rawSQL;
- continue;
- }
- } catch (Exception e) {
- Log.e(TAG, "newSQLConfig rawColumnSQL == null >> try { "
- + " String rawSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, fk); ... "
- + "} catch (Exception e) = " + e.getMessage());
+ return (hasPrefix ? " HAVING " : "") + StringUtil.concat(havingString, joinHaving, AND);
+ }
+
+ protected String getHavingItem(String quote, String table, String alias, String key, String expression, boolean containRaw) {
+ //fun(arg0,arg1,...)
+ if (containRaw) {
+ try {
+ String rawSQL = getRawSQL(KEY_HAVING, expression);
+ if (rawSQL != null) {
+ return rawSQL;
}
+ } catch (Exception e) {
+ Log.e(TAG, "newSQLConfig rawColumnSQL == null >> try { "
+ + " String rawSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, fk); ... "
+ + "} catch (Exception e) = " + e.getMessage());
}
+ }
- if (expression.length() > 50) {
- throw new UnsupportedOperationException("@having:value 的 value 中字符串 " + expression + " 不合法!"
- + "不允许传超过 50 个字符的函数或表达式!请用 @raw 简化传参!");
- }
+ if (expression.length() > 100) {
+ throw new UnsupportedOperationException("@having:value 的 value 中字符串 " + expression + " 不合法!"
+ + "不允许传超过 100 个字符的函数或表达式!请用 @raw 简化传参!");
+ }
- int start = expression.indexOf("(");
- if (start < 0) {
- if (isPrepared() && PATTERN_FUNCTION.matcher(expression).matches() == false) {
- throw new UnsupportedOperationException("字符串 " + expression + " 不合法!"
- + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
- + " 中 column?value 必须符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许空格!");
- }
- continue;
+ int start = expression.indexOf("(");
+ if (start < 0) {
+ if (isPrepared() && PATTERN_FUNCTION.matcher(expression).matches() == false) {
+ throw new UnsupportedOperationException("字符串 " + expression + " 不合法!"
+ + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
+ + " 中 column?value 必须符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许空格!");
}
+ return expression;
+ }
- int end = expression.lastIndexOf(")");
- if (start >= end) {
- throw new IllegalArgumentException("字符 " + expression + " 不合法!"
- + "@having:value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式!");
- }
+ int end = expression.lastIndexOf(")");
+ if (start >= end) {
+ throw new IllegalArgumentException("字符 " + expression + " 不合法!"
+ + "@having:value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式!");
+ }
- method = expression.substring(0, start);
- if (method.isEmpty() == false) {
- if (SQL_FUNCTION_MAP == null || SQL_FUNCTION_MAP.isEmpty()) {
- if (StringUtil.isName(method) == false) {
- throw new IllegalArgumentException("字符 " + method + " 不合法!"
- + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
- + " 中 function 必须符合小写英文单词的 SQL 函数名格式!");
- }
- }
- else if (SQL_FUNCTION_MAP.containsKey(method) == false) {
+ String method = expression.substring(0, start);
+ if (method.isEmpty() == false) {
+ if (SQL_FUNCTION_MAP == null || SQL_FUNCTION_MAP.isEmpty()) {
+ if (StringUtil.isName(method) == false) {
throw new IllegalArgumentException("字符 " + method + " 不合法!"
- + "预编译模式下 @column:\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\""
- + " 中 function 必须符合小写英文单词的 SQL 函数名格式!且必须是后端允许调用的 SQL 函数!");
+ + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
+ + " 中 function 必须符合小写英文单词的 SQL 函数名格式!");
}
}
-
- suffix = expression.substring(end + 1, expression.length());
-
- if (isPrepared() && (((String) suffix).contains("--") || ((String) suffix).contains("/*") || PATTERN_RANGE.matcher((String) suffix).matches() == false)) {
- throw new UnsupportedOperationException("字符串 " + suffix + " 不合法!"
- + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
- + " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- 或注释符 /* !不允许多余的空格!");
+ else if (SQL_FUNCTION_MAP.containsKey(method) == false) {
+ throw new IllegalArgumentException("字符 " + method + " 不合法!"
+ + "预编译模式下 @column:\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\""
+ + " 中 function 必须符合小写英文单词的 SQL 函数名格式!且必须是后端允许调用的 SQL 函数!");
}
+ }
+
+ String suffix = expression.substring(end + 1, expression.length());
- String[] ckeys = StringUtil.split(expression.substring(start + 1, end));
+ if (isPrepared() && (((String) suffix).contains("--") || ((String) suffix).contains("/*") || PATTERN_RANGE.matcher((String) suffix).matches() == false)) {
+ throw new UnsupportedOperationException("字符串 " + suffix + " 不合法!"
+ + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
+ + " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- 或注释符 /* !不允许多余的空格!");
+ }
- if (ckeys != null) {
- for (int j = 0; j < ckeys.length; j++) {
- String origin = ckeys[j];
+ String[] ckeys = StringUtil.split(expression.substring(start + 1, end));
- if (isPrepared()) {
- if (origin.startsWith("_") || origin.contains("--") || PATTERN_FUNCTION.matcher(origin).matches() == false) {
- throw new IllegalArgumentException("字符 " + ckeys[j] + " 不合法!"
- + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
- + " 中所有 column, arg 都必须是1个不以 _ 开头的单词 或者 符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许多余的空格!");
- }
- }
+ if (ckeys != null) {
+ for (int j = 0; j < ckeys.length; j++) {
+ String origin = ckeys[j];
- //JOIN 副表不再在外层加副表名前缀 userId AS `Commet.userId`, 而是直接 userId AS `userId`
- boolean isName = false;
- if (StringUtil.isNumer(origin)) {
- //do nothing
- }
- else if (StringUtil.isName(origin)) {
- origin = quote + origin + quote;
- isName = true;
- }
- else {
- origin = getValue(origin).toString();
+ if (isPrepared()) {
+ if (origin.startsWith("_") || origin.contains("--") || PATTERN_FUNCTION.matcher(origin).matches() == false) {
+ throw new IllegalArgumentException("字符 " + ckeys[j] + " 不合法!"
+ + "预编译模式下 @having:\"column?value;function(arg0,arg1,...)?value...\""
+ + " 中所有 column, arg 都必须是1个不以 _ 开头的单词 或者 符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许多余的空格!");
}
+ }
- ckeys[j] = (isName && isKeyPrefix() ? tableAlias + "." : "") + origin;
+ //JOIN 副表不再在外层加副表名前缀 userId AS `Commet.userId`, 而是直接 userId AS `userId`
+ boolean isName = false;
+ if (StringUtil.isNumer(origin)) {
+ //do nothing
+ }
+ else if (StringUtil.isName(origin)) {
+ origin = quote + origin + quote;
+ isName = true;
+ }
+ else {
+ origin = getValue(origin).toString();
}
- }
- keys[i] = method + "(" + StringUtil.getString(ckeys) + ")" + suffix;
+ ckeys[j] = (isName && isKeyPrefix() ? alias + "." : "") + origin;
+ }
}
- //TODO 支持 OR, NOT 参考 @combine:"&key0,|key1,!key2"
- return (hasPrefix ? " HAVING " : "") + StringUtil.concat(StringUtil.getString(keys, AND), joinHaving, AND);
+ return method + "(" + StringUtil.getString(ckeys) + ")" + suffix;
}
@Override
@@ -2193,6 +2217,9 @@ public SQLConfig setCast(Map cast) {
//WHERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+ protected int getMaxHavingCount() {
+ return MAX_HAVING_COUNT;
+ }
protected int getMaxWhereCount() {
return MAX_WHERE_COUNT;
}
@@ -2392,260 +2419,274 @@ 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(), where, combine, verifyName, false, false);
+
+ whereString = concatJoinWhereString(whereString);
+
+ String result = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
+
+ if (result.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
+ throw new UnsupportedOperationException("写操作请求必须带条件!!!");
+ }
+
+ return result;
+ }
+
+
+ protected String parseCombineExpression(RequestMethod method, String quote, String table, String alias
+ , Map conditioinMap, String combine, boolean verifyName, boolean containRaw, boolean isHaving) throws Exception {
+
+ String errPrefix = table + (isHaving ? ":{ @having:{ " : ":{ ") + "@combine:'" + combine + (isHaving ? "' } }" : "' }");
String s = StringUtil.getString(combine);
if (s.startsWith(" ") || s.endsWith(" ") ) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s
+ "' 不合法!不允许首尾有空格,也不允许连续空格!空格不能多也不能少!"
+ "逻辑连接符 & | 左右必须各一个相邻空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
}
- if (where == null) {
- where = new HashMap<>();
+ if (conditioinMap == null) {
+ conditioinMap = new HashMap<>();
}
- int whereSize = where.size();
+ int size = conditioinMap.size();
- int maxWhereCount = getMaxWhereCount();
- if (maxWhereCount > 0 && whereSize > maxWhereCount) {
- throw new IllegalArgumentException(table + ":{ key0:value0, key1:value1... } 中条件 key:value 数量 " + whereSize
- + " 已超过最大数量,必须在 0-" + maxWhereCount + " 内!");
+ int maxCount = isHaving ? getMaxHavingCount() : getMaxWhereCount();
+ if (maxCount > 0 && size > maxCount) {
+ throw new IllegalArgumentException(table + (isHaving ? ":{ @having:{ " : ":{ ") + "key0:value0, key1:value1... " + combine
+ + (isHaving ? " } }" : " }") + " 中条件 key:value 数量 " + size + " 已超过最大数量,必须在 0-" + maxCount + " 内!");
}
-
- String whereString = "";
-
- int maxDepth = getMaxCombineDepth();
- int maxCombineCount = getMaxCombineCount();
- int maxCombineKeyCount = getMaxCombineKeyCount();
- float maxCombineRatio = getMaxCombineRatio();
+ String result = "";
+
List prepreadValues = getPreparedValueList();
- setPreparedValueList(new ArrayList<>());
-
- int depth = 0;
- int allCount = 0;
+ Map usedKeyCountMap = new HashMap<>(size);
+
int n = s.length();
- int i = 0;
-
- char lastLogic = 0;
- char last = 0;
- boolean first = true;
- boolean isNot = false;
-
- String key = "";
- Map usedKeyCountMap = new HashMap<>(whereSize);
- while (i <= n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
- boolean isOver = i >= n;
- char c = isOver ? 0 : s.charAt(i);
- boolean isBlankOrRightParenthesis = c == ' ' || c == ')';
- if (isOver || isBlankOrRightParenthesis) {
- boolean isEmpty = StringUtil.isEmpty(key, true);
- if (isEmpty && last != ')') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (isOver ? s : s.substring(i))
- + "' 不合法!" + (c == ' ' ? "空格 ' ' " : "右括号 ')'") + " 左边缺少条件 key !逻辑连接符 & | 左右必须各一个相邻空格!"
- + "空格不能多也不能少!不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
- }
-
- if (isEmpty == false) {
- if (first == false && lastLogic <= 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 "
- + "'" + s.substring(i - key.length() - (isOver ? 1 : 0)) + "' 不合法!左边缺少 & | 其中一个逻辑连接符!");
- }
-
- allCount ++;
- if (allCount > maxCombineCount && maxCombineCount > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
- + "其中 key 数量 " + allCount + " 已超过最大值,必须在条件键值对数量 0-" + maxCombineCount + " 内!");
- }
- if (1.0f*allCount/whereSize > maxCombineRatio && maxCombineRatio > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
- + "其中 key 数量 " + allCount + " / 条件键值对数量 " + whereSize + " = " + (1.0f*allCount/whereSize)
- + " 已超过 最大倍数,必须在条件键值对数量 0-" + maxCombineRatio + " 倍内!");
- }
-
- String column = key;
-
- Object value = where.get(column);
- if (value == null) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + key
- + "' 对应的条件键值对 " + column + ":value 不存在!");
+ if (n > 0) {
+ setPreparedValueList(new ArrayList<>());
+
+ int maxDepth = getMaxCombineDepth();
+ int maxCombineCount = getMaxCombineCount();
+ int maxCombineKeyCount = getMaxCombineKeyCount();
+ float maxCombineRatio = getMaxCombineRatio();
+
+ int depth = 0;
+ int allCount = 0;
+
+ int i = 0;
+
+ char lastLogic = 0;
+ char last = 0;
+ boolean first = true;
+ boolean isNot = false;
+
+ String key = "";
+ while (i <= n) { // "date> | (contactIdList<> & (name*~ | tag&$))"
+ boolean isOver = i >= n;
+ char c = isOver ? 0 : s.charAt(i);
+ boolean isBlankOrRightParenthesis = c == ' ' || c == ')';
+ if (isOver || isBlankOrRightParenthesis) {
+ boolean isEmpty = StringUtil.isEmpty(key, true);
+ if (isEmpty && last != ')') {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + (isOver ? s : s.substring(i))
+ + "' 不合法!" + (c == ' ' ? "空格 ' ' " : "右括号 ')'") + " 左边缺少条件 key !逻辑连接符 & | 左右必须各一个相邻空格!"
+ + "空格不能多也不能少!不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
}
-
- String wi = getWhereItem(column, value, method, verifyName);
- if (StringUtil.isEmpty(wi, true)) { // 转成 1=1 ?
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + key
- + "' 对应的 " + column + ":value 不是有效条件键值对!");
+
+ if (isEmpty == false) {
+ if (first == false && lastLogic <= 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 "
+ + "'" + s.substring(i - key.length() - (isOver ? 1 : 0)) + "' 不合法!左边缺少 & | 其中一个逻辑连接符!");
+ }
+
+ allCount ++;
+ if (allCount > maxCombineCount && maxCombineCount > 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s + "' 不合法!"
+ + "其中 key 数量 " + allCount + " 已超过最大值,必须在条件键值对数量 0-" + maxCombineCount + " 内!");
+ }
+ if (1.0f*allCount/size > maxCombineRatio && maxCombineRatio > 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s + "' 不合法!"
+ + "其中 key 数量 " + allCount + " / 条件键值对数量 " + size + " = " + (1.0f*allCount/size)
+ + " 已超过 最大倍数,必须在条件键值对数量 0-" + maxCombineRatio + " 倍内!");
+ }
+
+ String column = key;
+
+ Object value = conditioinMap.get(column);
+ if (value == null) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + key
+ + "' 对应的条件键值对 " + column + ":value 不存在!");
+ }
+
+ String wi = isHaving ? getHavingItem(quote, table, alias, column, (String) value, containRaw) : getWhereItem(column, value, method, verifyName);
+ if (StringUtil.isEmpty(wi, true)) { // 转成 1=1 ?
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + key
+ + "' 对应的 " + column + ":value 不是有效条件键值对!");
+ }
+
+ Integer count = usedKeyCountMap.get(column);
+ count = count == null ? 1 : count + 1;
+ if (count > maxCombineKeyCount && maxCombineKeyCount > 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s + "' 不合法!"
+ + "其中 '" + column + "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
+ }
+ usedKeyCountMap.put(column, count);
+
+ result += "( " + getCondition(isNot, wi) + " )";
+ isNot = false;
+ first = false;
}
-
- Integer count = usedKeyCountMap.get(column);
- count = count == null ? 1 : count + 1;
- if (count > maxCombineKeyCount && maxCombineKeyCount > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s + "' 不合法!"
- + "其中 '" + column + "' 重复引用,次数 " + count + " 已超过最大值,必须在 0-" + maxCombineKeyCount + " 内!");
+
+ key = "";
+ lastLogic = 0;
+
+ if (isOver) {
+ break;
}
- usedKeyCountMap.put(column, count);
-
- whereString += "( " + getCondition(isNot, wi) + " )";
- isNot = false;
- first = false;
}
-
- key = "";
- lastLogic = 0;
-
- if (isOver) {
- break;
+
+ if (c == ' ') {
}
- }
-
- if (c == ' ') {
- }
- else if (c == '&') {
- if (last == ' ') {
- if (i >= n - 1 || s.charAt(i + 1) != ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
- + "' 不合法!逻辑连接符 & 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
- + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ else if (c == '&') {
+ if (last == ' ') {
+ if (i >= n - 1 || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
+ + "' 不合法!逻辑连接符 & 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
+
+ result += SQL.AND;
+ lastLogic = c;
+ i ++;
+ }
+ else {
+ key += c;
}
-
- whereString += SQL.AND;
- lastLogic = c;
- i ++;
- }
- else {
- key += c;
}
- }
- else if (c == '|') {
- if (last == ' ') {
- if (i >= n - 1 || s.charAt(i + 1) != ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
- + "' 不合法!逻辑连接符 | 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
- + "不允许首尾有空格,也不允许连续空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
+ else if (c == '|') {
+ if (last == ' ') {
+ if (i >= n - 1 || s.charAt(i + 1) != ' ') {
+ throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + (i >= n - 1 ? s : s.substring(0, i + 1))
+ + "' 不合法!逻辑连接符 | 右边缺少一个空格 !逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 右边和右括号 ) 左边都不允许有相邻空格!");
+ }
+
+ result += SQL.OR;
+ lastLogic = c;
+ i ++;
+ }
+ else {
+ key += c;
}
-
- whereString += SQL.OR;
- lastLogic = c;
- i ++;
- }
- else {
- key += c;
}
- }
- else if (c == '!') {
- last = i <= 0 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
-
- char next = i >= n - 1 ? 0 : s.charAt(i + 1);
- if (last == ' ' || last == '(') {
- if (next == ' ') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!非逻辑符 '!' 右边多了一个空格 ' ' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ else if (c == '!') {
+ last = i <= 0 ? 0 : s.charAt(i - 1); // & | 后面跳过了空格
+
+ char next = i >= n - 1 ? 0 : s.charAt(i + 1);
+ if (last == ' ' || last == '(') {
+ if (next == ' ') {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个空格 ' ' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+ if (next == ')' || next == '&' || next == '!') {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!非逻辑符 '!' 右边多了一个字符 '" + next + "' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ }
+ if (i > 0 && lastLogic <= 0 && last != '(') {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(i)
+ + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
+ }
+ }
+
+ if (next == '(') {
+ result += SQL.NOT;
+ lastLogic = c;
}
- if (next == ')' || next == '&' || next == '!') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!非逻辑符 '!' 右边多了一个字符 '" + next + "' !非逻辑符 '!' 右边不允许任何相邻空格 ' ',也不允许 ')' '&' '|' 中任何一个!");
+ else if (last <= 0 || last == ' ' || last == '(') {
+ isNot = true;
+ // lastLogic = c;
}
- if (i > 0 && lastLogic <= 0 && last != '(') {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
+ else {
+ key += c;
+ }
+ }
+ else if (c == '(') {
+ if (key.isEmpty() == false || (i > 0 && lastLogic <= 0 && last != '(')) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(i)
+ "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
+ "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
}
+
+ depth ++;
+ if (depth > maxDepth && maxDepth > 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!括号 (()) 嵌套层级 " + depth + " 已超过最大值,必须在 0-" + maxDepth + " 内!");
+ }
+
+ result += c;
+ lastLogic = 0;
+ first = true;
}
-
- if (next == '(') {
- whereString += SQL.NOT;
- lastLogic = c;
- }
- else if (last <= 0 || last == ' ' || last == '(') {
- isNot = true;
-// lastLogic = c;
- }
+ else if (c == ')') {
+ depth --;
+ if (depth < 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s.substring(0, i + 1)
+ + "' 不合法!左括号 ( 比 右括号 ) 少!数量必须相等从而完整闭合 (...) !");
+ }
+
+ result += c;
+ lastLogic = 0;
+ }
else {
key += c;
}
+
+ last = c;
+ i ++;
}
- else if (c == '(') {
- if (key.isEmpty() == false || (i > 0 && lastLogic <= 0 && last != '(')) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(i)
- + "' 不合法!左边缺少 & | 逻辑连接符!逻辑连接符 & | 左右必须各一个相邻空格!空格不能多也不能少!"
- + "不允许首尾有空格,也不允许连续空格!左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格!");
- }
-
- depth ++;
- if (depth > maxDepth && maxDepth > 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!括号 (()) 嵌套层级 " + depth + " 已超过最大值,必须在 0-" + maxDepth + " 内!");
- }
-
- whereString += c;
- lastLogic = 0;
- first = true;
- }
- else if (c == ')') {
- depth --;
- if (depth < 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s.substring(0, i + 1)
- + "' 不合法!左括号 ( 比 右括号 ) 少!数量必须相等从而完整闭合 (...) !");
- }
-
- whereString += c;
- lastLogic = 0;
- }
- else {
- key += c;
+
+ if (depth != 0) {
+ throw new IllegalArgumentException(errPrefix + " 中字符 '" + s
+ + "' 不合法!左括号 ( 比 右括号 ) 多!数量必须相等从而完整闭合 (...) !");
}
-
- last = c;
- i ++;
}
- if (depth != 0) {
- throw new IllegalArgumentException(table + ":{ @combine: '" + combine + "' } 中字符 '" + s
- + "' 不合法!左括号 ( 比 右括号 ) 多!数量必须相等从而完整闭合 (...) !");
- }
-
- Set> set = where.entrySet();
+ Set> set = conditioinMap.entrySet();
- String andWhere = "";
+ String andCond = "";
boolean isItemFirst = true;
for (Entry entry : set) {
- key = entry == null ? null : entry.getKey();
+ String key = entry == null ? null : entry.getKey();
if (key == null || usedKeyCountMap.containsKey(key)) {
continue;
}
- String wi = getWhereItem(key, where.get(key), method, verifyName);
+ String wi = isHaving ? getHavingItem(quote, table, alias, key, (String) entry.getValue(), containRaw) : getWhereItem(key, entry.getValue(), method, verifyName);
if (StringUtil.isEmpty(wi, true)) {//避免SQL条件连接错误
continue;
}
- andWhere += (isItemFirst ? "" : AND) + "(" + wi + ")";
+ andCond += (isItemFirst ? "" : AND) + "(" + wi + ")";
isItemFirst = false;
}
- if (StringUtil.isEmpty(whereString, true)) {
- whereString = andWhere;
- }
- else if (StringUtil.isNotEmpty(andWhere, true)) { // andWhere 必须放后面,否则 prepared 值顺序错误
-// whereString = "( " + whereString + " )" + AND + andWhere;
-
- whereString = andWhere + AND + "( " + whereString + " )"; // 先暂存之前的 prepared 值,然后反向整合
- prepreadValues.addAll(getPreparedValueList());
- setPreparedValueList(prepreadValues);
+ if (StringUtil.isEmpty(result, true)) {
+ result = andCond;
+ }
+ else if (StringUtil.isNotEmpty(andCond, true)) { // andWhere 必须放后面,否则 prepared 值顺序错误
+ // result = "( " + result + " )" + AND + andCond;
+ result = andCond + AND + "( " + result + " )"; // 先暂存之前的 prepared 值,然后反向整合
+ if (n > 0) {
+ prepreadValues.addAll(getPreparedValueList());
+ setPreparedValueList(prepreadValues);
+ }
}
-
- whereString = concatJoinWhereString(whereString);
- String result = StringUtil.isEmpty(whereString, true) ? "" : (hasPrefix ? " WHERE " : "") + whereString;
-
- if (result.isEmpty() && RequestMethod.isQueryMethod(method) == false) {
- throw new UnsupportedOperationException("写操作请求必须带条件!!!");
- }
-
return result;
}
-
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, Map> combine, List joinList, boolean verifyName) throws Exception {
Set>> combineSet = combine == null ? null : combine.entrySet();
if (combineSet == null || combineSet.isEmpty()) {
@@ -4269,7 +4310,8 @@ else if (id instanceof Subquery) {}
String cast = request.getString(KEY_CAST);
String combine = request.getString(KEY_COMBINE);
String group = request.getString(KEY_GROUP);
- String having = request.getString(KEY_HAVING);
+ Object having = request.get(KEY_HAVING);
+ String havingAnd = request.getString(KEY_HAVING_AND);
String order = request.getString(KEY_ORDER);
String raw = request.getString(KEY_RAW);
String json = request.getString(KEY_JSON);
@@ -4292,24 +4334,29 @@ else if (id instanceof Subquery) {}
request.remove(KEY_COMBINE);
request.remove(KEY_GROUP);
request.remove(KEY_HAVING);
+ request.remove(KEY_HAVING_AND);
request.remove(KEY_ORDER);
request.remove(KEY_RAW);
request.remove(KEY_JSON);
+
+ // @null <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
String[] nullKeys = StringUtil.split(nulls);
if (nullKeys != null && nullKeys.length > 0) {
for (String nk : nullKeys) {
if (StringUtil.isEmpty(nk, true)) {
- throw new IllegalArgumentException(table + ":{} 里的 @null: value 中的字符 '" + nk + "' 不合法!不允许为空!");
+ throw new IllegalArgumentException(table + ":{ @null: value } 中的字符 '" + nk + "' 不合法!不允许为空!");
}
if (request.get(nk) != null) {
- throw new IllegalArgumentException(table + ":{} 里的 @null: value 中的字符 '" + nk + "' 已在当前对象有非 null 值!不允许对同一个 JSON key 设置不同值!");
+ throw new IllegalArgumentException(table + ":{ @null: value } 中的字符 '" + nk + "' 已在当前对象有非 null 值!不允许对同一个 JSON key 设置不同值!");
}
request.put(nk, null);
}
}
+ // @null >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+ // @cast <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
String[] casts = StringUtil.split(cast);
Map castMap = null;
if (casts != null && casts.length > 0) {
@@ -4330,8 +4377,9 @@ else if (id instanceof Subquery) {}
castMap.put(p.getKey(), p.getValue());
}
}
+ // @cast >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-
+
String[] rawArr = StringUtil.split(raw);
config.setRaw(rawArr == null || rawArr.length <= 0 ? null : new ArrayList<>(Arrays.asList(rawArr)));
@@ -4523,8 +4571,15 @@ else if (whereList != null && whereList.contains(key)) {
List cs = new ArrayList<>();
List rawList = config.getRaw();
- boolean containColumnRaw = rawList != null && rawList.contains(KEY_COLUMN);
+ boolean containColumnHavingAnd = rawList != null && rawList.contains(KEY_HAVING_AND);
+
+ if (containColumnHavingAnd) {
+ throw new IllegalArgumentException(table + ":{ @raw:value } 的 value 里字符 @having& 不合法!"
+ + "@raw 不支持 @having&,请用 @having 替代!");
+ }
+ // TODO 这段是否必要?如果 @column 只支持分段后的 SQL 片段,也没问题
+ boolean containColumnRaw = rawList != null && rawList.contains(KEY_COLUMN);
String rawColumnSQL = null;
if (containColumnRaw) {
try {
@@ -4572,6 +4627,96 @@ else if (whereList != null && whereList.contains(key)) {
}
}
+
+ // @having, @haivng& <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+ Object newHaving = having;
+ boolean isHavingAnd = false;
+
+ Map havingMap = new LinkedHashMap<>();
+ if (havingAnd != null) {
+ if (having != null) {
+ throw new IllegalArgumentException(table + ":{ @having: value1, @having&: value2 } "
+ + "中 value1 与 value2 不合法!不允许同时传 @having 和 @having& ,两者最多传一个!");
+ }
+
+ newHaving = havingAnd;
+ isHavingAnd = true;
+ }
+
+ String havingKey = (isHavingAnd ? KEY_HAVING_AND : KEY_HAVING);
+ String havingCombine = "";
+
+ if (newHaving instanceof String) {
+ String[] havingss = StringUtil.split((String) newHaving, ";");
+ if (havingss != null) {
+ int ind = -1;
+ for (int i = 0; i < havingss.length; i++) {
+
+ String havingsStr = havingss[i];
+ int start = havingsStr == null ? -1 : havingsStr.indexOf("(");
+ int end = havingsStr == null ? -1 : havingsStr.lastIndexOf(")");
+ if (IS_HAVING_ALLOW_NOT_FUNCTION == false && (start < 0 || start >= end)) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":value } 里的 value 中的第 " + i +
+ " 个字符 '" + havingsStr + "' 不合法!里面没有包含 SQL 函数!必须为 fun(col1,col2..)?val 格式!");
+ }
+
+ String[] havings = start >= 0 && end > start ? new String[]{havingsStr} : StringUtil.split(havingsStr);
+ if (havings != null) {
+ for (int j = 0; j < havings.length; j++) {
+ ind ++;
+ String h = havings[j];
+ if (StringUtil.isEmpty(h, true)) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":value } 里的"
+ + " value 中的第 " + ind + " 个字符 '" + h + "' 不合法!不允许为空!");
+ }
+
+ havingMap.put("having" + ind, h);
+
+ if (isHavingAnd == false && IS_HAVING_DEFAULT_AND == false) {
+ havingCombine += (ind <= 0 ? "" : " | ") + "having" + ind;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (newHaving instanceof JSONObject) {
+ if (isHavingAnd) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":value } 里的 value 类型不合法!"
+ + "@having&:value 中 value 只能是 String,@having:value 中 value 只能是 String 或 JSONObject !");
+ }
+
+ JSONObject havingObj = (JSONObject) newHaving;
+ Set> havingSet = havingObj.entrySet();
+ for (Entry entry : havingSet) {
+ String k = entry == null ? null : entry.getKey();
+ Object v = k == null ? null : entry.getValue();
+ if (v == null) {
+ continue;
+ }
+ if (v instanceof String == false) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":{ " + k + ":value } } 里的"
+ + " value 不合法!类型只能是 String,且不允许为空!");
+ }
+
+ if (KEY_COMBINE.equals(k)) {
+ havingCombine = (String) v;
+ }
+ else if (StringUtil.isName(k) == false) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":{ " + k + ":value } } 里的"
+ + " key 对应字符 " + k + " 不合法!必须为 英文字母 开头,且只包含 英文字母、下划线、数字 的合法变量名!");
+ }
+ else {
+ havingMap.put(k, (String) v);
+ }
+ }
+ }
+ else if (newHaving != null) {
+ throw new IllegalArgumentException(table + ":{ " + havingKey + ":value } 里的 value 类型不合法!"
+ + "@having:value 中 value 只能是 String 或 JSONObject,@having&:value 中 value 只能是 String !");
+ }
+ // @having, @haivng& >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
config.setExplain(explain);
config.setCache(getCache(cache));
@@ -4587,7 +4732,8 @@ else if (whereList != null && whereList.contains(key)) {
config.setCast(castMap);
config.setWhere(tableWhere);
config.setGroup(group);
- config.setHaving(having);
+ config.setHaving(havingMap);
+ config.setHavingCombine(havingCombine);
config.setOrder(order);
String[] jsons = StringUtil.split(json);
@@ -4614,6 +4760,7 @@ else if (whereList != null && whereList.contains(key)) {
request.put(KEY_COMBINE, combine);
request.put(KEY_GROUP, group);
request.put(KEY_HAVING, having);
+ request.put(KEY_HAVING_AND, havingAnd);
request.put(KEY_ORDER, order);
request.put(KEY_RAW, raw);
request.put(KEY_JSON, json);
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index 51a8df36b..22acf1461 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -159,8 +159,11 @@ public interface SQLConfig {
String getGroup();
SQLConfig setGroup(String group);
- String getHaving();
- SQLConfig setHaving(String having);
+ Map getHaving();
+ SQLConfig setHaving(Map having);
+
+ String getHavingCombine();
+ SQLConfig setHavingCombine(String havingCombine);
String getOrder();
SQLConfig setOrder(String order);
From 12738bfb6bafb4bdcd91afd58016119f37bd5598 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 21 Mar 2022 00:46:20 +0800
Subject: [PATCH 084/674] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20JOIN=20ON=20?=
=?UTF-8?q?=E4=B8=AD=E7=94=A8=20@combine=20=E7=AD=89=E6=83=85=E5=86=B5?=
=?UTF-8?q?=E4=B8=8B=E9=A2=84=E7=BC=96=E8=AF=91=E5=80=BC=E4=B8=8E=20SQL=20?=
=?UTF-8?q?=E4=B8=AD=20=3F=20=E5=8D=A0=E4=BD=8D=E7=AC=A6=E9=A1=BA=E5=BA=8F?=
=?UTF-8?q?=E5=AF=B9=E4=B8=8D=E4=B8=8A=E5=AF=BC=E8=87=B4=E7=9A=84=E5=BC=82?=
=?UTF-8?q?=E5=B8=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 24 ++++++++++++-------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index c9c64b61e..fd9631c2e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -2463,8 +2463,10 @@ protected String parseCombineExpression(RequestMethod method, String quote, Stri
int n = s.length();
if (n > 0) {
- setPreparedValueList(new ArrayList<>());
-
+ if (isHaving == false) {
+ setPreparedValueList(new ArrayList<>()); // 必须反过来,否则 JOIN ON 内部 @combine 拼接后顺序错误
+ }
+
int maxDepth = getMaxCombineDepth();
int maxCombineCount = getMaxCombineCount();
int maxCombineKeyCount = getMaxCombineKeyCount();
@@ -2675,13 +2677,17 @@ else if (c == ')') {
if (StringUtil.isEmpty(result, true)) {
result = andCond;
}
- else if (StringUtil.isNotEmpty(andCond, true)) { // andWhere 必须放后面,否则 prepared 值顺序错误
- // result = "( " + result + " )" + AND + andCond;
- result = andCond + AND + "( " + result + " )"; // 先暂存之前的 prepared 值,然后反向整合
- if (n > 0) {
- prepreadValues.addAll(getPreparedValueList());
- setPreparedValueList(prepreadValues);
- }
+ else if (StringUtil.isNotEmpty(andCond, true)) { // andCond 必须放后面,否则 prepared 值顺序错误
+ if (isHaving) { // HAVING 前 WHERE 已经有条件 ? 占位,不能反过来,想优化 AND 连接在最前,需要多遍历一次内部的 key,也可以 newSQLConfig 时存到 andList
+ result = "( " + result + " )" + AND + andCond;
+ }
+ else {
+ result = andCond + AND + "( " + result + " )"; // 先暂存之前的 prepared 值,然后反向整合
+ if (n > 0) {
+ prepreadValues.addAll(getPreparedValueList());
+ setPreparedValueList(prepreadValues);
+ }
+ }
}
return result;
From be92b894b5beb6f2b1cdd8ffe71f9fbef11ae79a Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 26 Mar 2022 16:26:27 +0800
Subject: [PATCH 085/674] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e0d4110cf..1aca30303 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
APIJSON
-零代码、热更新、全自动 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
+零代码、全自动、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构
From b248c698887728bd826febd77c4404dd1faecf06 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 26 Mar 2022 23:56:23 +0800
Subject: [PATCH 086/674] =?UTF-8?q?=E4=BC=98=E5=8C=96=20@combine=20?=
=?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E9=BB=98?=
=?UTF-8?q?=E8=AE=A4=20combine=20=E4=B8=BA=E9=80=BB=E8=BE=91=E8=BF=90?=
=?UTF-8?q?=E7=AE=97=E6=A8=A1=E6=9D=BF=EF=BC=8C=E5=8E=9F=E6=9D=A5=E7=9A=84?=
=?UTF-8?q?=20combine=20=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BA=20combineMap?=
=?UTF-8?q?=EF=BC=8CcombineExpression=20=E9=87=8D=E5=91=BD=E5=90=8D?=
=?UTF-8?q?=E4=B8=BA=20combine?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 83 ++++++++++++-------
.../java/apijson/orm/AbstractSQLExecutor.java | 2 +-
.../src/main/java/apijson/orm/SQLConfig.java | 32 +++----
3 files changed, 70 insertions(+), 47 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index fd9631c2e..17aebb59a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -775,8 +775,8 @@ public String getUserIdKey() {
private Map cast;
private Map content; //Request内容,key:value形式,column = content.keySet(),values = content.values()
private Map where; //筛选条件,key:value形式
- private Map> combine; //条件组合,{ "&":[key], "|":[key], "!":[key] }
- private String combineExpression;
+ private String combine; //条件组合, a | (b & c & !(d | !e))
+ private Map> combineMap; //条件组合,{ "&":[key], "|":[key], "!":[key] }
//array item <<<<<<<<<<
private int count; //Table数量
@@ -2238,31 +2238,31 @@ protected float getMaxCombineRatio() {
@Override
- public String getCombineExpression() {
- return combineExpression;
+ public String getCombine() {
+ return combine;
}
@Override
- public AbstractSQLConfig setCombineExpression(String combineExpression) {
- this.combineExpression = combineExpression;
+ public AbstractSQLConfig setCombine(String combine) {
+ this.combine = combine;
return this;
}
@NotNull
@Override
- public Map> getCombine() {
- List andList = combine == null ? null : combine.get("&");
+ public Map> getCombineMap() {
+ List andList = combineMap == null ? null : combineMap.get("&");
if (andList == null) {
andList = where == null ? new ArrayList() : new ArrayList(where.keySet());
- if (combine == null) {
- combine = new HashMap<>();
+ if (combineMap == null) {
+ combineMap = new HashMap<>();
}
- combine.put("&", andList);
+ combineMap.put("&", andList);
}
- return combine;
+ return combineMap;
}
@Override
- public AbstractSQLConfig setCombine(Map> combine) {
- this.combine = combine;
+ public AbstractSQLConfig setCombineMap(Map> combineMap) {
+ this.combineMap = combineMap;
return this;
}
@@ -2329,8 +2329,8 @@ public AbstractSQLConfig putWhere(String key, Object value, boolean prior) {
where.put(key, value);
}
- combine = getCombine();
- List andList = combine.get("&");
+ Map> combineMap = getCombineMap();
+ List andList = combineMap.get("&");
if (value == null) {
if (andList != null) {
andList.remove(key);
@@ -2392,7 +2392,7 @@ else if (key.equals(userIdInKey)) {
andList.add(key); //AbstractSQLExecutor.onPutColumn里getSQL,要保证缓存的SQL和查询的SQL里 where 的 key:value 顺序一致
}
}
- combine.put("&", andList);
+ combineMap.put("&", andList);
}
return this;
@@ -2405,11 +2405,11 @@ else if (key.equals(userIdInKey)) {
@JSONField(serialize = false)
@Override
public String getWhereString(boolean hasPrefix) throws Exception {
- String ce = getCombineExpression();
- if (StringUtil.isEmpty(ce, true)) {
- return getWhereString(hasPrefix, getMethod(), getWhere(), getCombine(), getJoinList(), ! isTest());
+ String combineExpr = getCombine();
+ if (StringUtil.isEmpty(combineExpr, true)) {
+ return getWhereString(hasPrefix, getMethod(), getWhere(), getCombineMap(), getJoinList(), ! isTest());
}
- return getWhereString(hasPrefix, getMethod(), getWhere(), ce, getJoinList(), ! isTest());
+ return getWhereString(hasPrefix, getMethod(), getWhere(), combineExpr, getJoinList(), ! isTest());
}
/**获取WHERE
* @param method
@@ -2432,7 +2432,19 @@ public String getWhereString(boolean hasPrefix, RequestMethod method, Map conditioinMap, String combine, boolean verifyName, boolean containRaw, boolean isHaving) throws Exception {
@@ -2693,6 +2705,17 @@ else if (StringUtil.isNotEmpty(andCond, true)) { // andCond 必须放后面,
return result;
}
+ /**已废弃,最快 6.0 删除,请尽快把前端/客户端 @combine:"a,b" 这种旧方式改为 @combine:"a | b" 这种新方式
+ * @param hasPrefix
+ * @param method
+ * @param where
+ * @param combine
+ * @param joinList
+ * @param verifyName
+ * @return
+ * @throws Exception
+ */
+ @Deprecated
public String getWhereString(boolean hasPrefix, RequestMethod method, Map where, Map> combine, List joinList, boolean verifyName) throws Exception {
Set>> combineSet = combine == null ? null : combine.entrySet();
if (combineSet == null || combineSet.isEmpty()) {
@@ -4437,9 +4460,9 @@ else if (id instanceof Subquery) {}
List whereList = null;
String[] ws = StringUtil.split(combine);
- String combineExpression = ws == null || ws.length != 1 ? null : ws[0];
+ String combineExpr = ws == null || ws.length != 1 ? null : ws[0];
- Map> combineMap = StringUtil.isNotEmpty(combineExpression, true) ? null : new LinkedHashMap<>();
+ Map> combineMap = StringUtil.isNotEmpty(combineExpr, true) ? null : new LinkedHashMap<>();
List andList = combineMap == null ? null : new ArrayList<>();
List