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 001/645] =?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 002/645] =?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 003/645] =?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 004/645] =?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 005/645] =?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
//
-
+
@@ -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 012/645] 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 013/645] 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 014/645] 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 015/645] =?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;
//

+
@@ -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 024/645] =?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 025/645] =?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 026/645] 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 027/645] 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 028/645] =?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 029/645] =?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