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 001/642] =?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 002/642] 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 003/642] =?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 004/642] =?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 005/642] =?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 006/642] =?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 007/642] 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 008/642] 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 009/642] 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 010/642] =?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 019/642] =?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 020/642] =?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 021/642] 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 022/642] 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 023/642] =?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 024/642] =?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