diff --git a/APIJSONORM/src/main/java/apijson/JSON.java b/APIJSONORM/src/main/java/apijson/JSON.java
index c31170c44..0a14e0420 100755
--- a/APIJSONORM/src/main/java/apijson/JSON.java
+++ b/APIJSONORM/src/main/java/apijson/JSON.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -674,4 +674,18 @@ public static String getString(Map map, String key) {
return value.toString();
}
+
+ public static Object getFromObjOrArr(Object parent, String k) {
+ if (parent instanceof Map, ?>) {
+ return ((Map) parent).get(k);
+ }
+
+ if (parent instanceof List>) {
+ int j = Integer.valueOf(k);
+ return ((List>) parent).get(j);
+ }
+
+ return null;
+ }
+
}
diff --git a/APIJSONORM/src/main/java/apijson/JSONCreator.java b/APIJSONORM/src/main/java/apijson/JSONCreator.java
index fcabe2fe0..df0d9066a 100755
--- a/APIJSONORM/src/main/java/apijson/JSONCreator.java
+++ b/APIJSONORM/src/main/java/apijson/JSONCreator.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/JSONList.java b/APIJSONORM/src/main/java/apijson/JSONList.java
index 0aa448fcb..092bf9f39 100644
--- a/APIJSONORM/src/main/java/apijson/JSONList.java
+++ b/APIJSONORM/src/main/java/apijson/JSONList.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/JSONMap.java b/APIJSONORM/src/main/java/apijson/JSONMap.java
index 0bf0b6825..29d88f756 100755
--- a/APIJSONORM/src/main/java/apijson/JSONMap.java
+++ b/APIJSONORM/src/main/java/apijson/JSONMap.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/JSONParser.java b/APIJSONORM/src/main/java/apijson/JSONParser.java
index 6762e2bff..7c38a39df 100755
--- a/APIJSONORM/src/main/java/apijson/JSONParser.java
+++ b/APIJSONORM/src/main/java/apijson/JSONParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java
index 0dccbd3e6..c74dfe349 100755
--- a/APIJSONORM/src/main/java/apijson/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/JSONResponse.java b/APIJSONORM/src/main/java/apijson/JSONResponse.java
index ab0564f99..c39aa1ace 100755
--- a/APIJSONORM/src/main/java/apijson/JSONResponse.java
+++ b/APIJSONORM/src/main/java/apijson/JSONResponse.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index b7b909d64..ba1a03bd5 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/MethodAccess.java b/APIJSONORM/src/main/java/apijson/MethodAccess.java
index 31d45843e..1804f7a7b 100755
--- a/APIJSONORM/src/main/java/apijson/MethodAccess.java
+++ b/APIJSONORM/src/main/java/apijson/MethodAccess.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/NotNull.java b/APIJSONORM/src/main/java/apijson/NotNull.java
index d10a93691..1265ccac7 100755
--- a/APIJSONORM/src/main/java/apijson/NotNull.java
+++ b/APIJSONORM/src/main/java/apijson/NotNull.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/RequestMethod.java b/APIJSONORM/src/main/java/apijson/RequestMethod.java
index 875200b7a..27e4cab64 100755
--- a/APIJSONORM/src/main/java/apijson/RequestMethod.java
+++ b/APIJSONORM/src/main/java/apijson/RequestMethod.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java
index 110ae3d47..868f0d2aa 100755
--- a/APIJSONORM/src/main/java/apijson/SQL.java
+++ b/APIJSONORM/src/main/java/apijson/SQL.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/StringUtil.java b/APIJSONORM/src/main/java/apijson/StringUtil.java
index 13b0ff214..179161005 100755
--- a/APIJSONORM/src/main/java/apijson/StringUtil.java
+++ b/APIJSONORM/src/main/java/apijson/StringUtil.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
index ff2e484df..42831775b 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 5a39fd1fe..4d56499a0 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -1171,7 +1171,9 @@ else if (isArrayMainTable && position > 0) { // 数组主表使用专门的缓
// APP JOIN 副表时副表返回了这个字段 rawList = (List) result.remove(AbstractSQLExecutor.KEY_RAW_LIST);
String arrayPath = parentPath.substring(0, parentPath.lastIndexOf("[]") + 2);
- if (isSimpleArray == false) {
+ if (isSimpleArray) {
+ parser.putQueryResult(arrayPath, rawList); // 从数组外部引用该数组内值需要
+ } else {
long startTime = System.currentTimeMillis();
for (int i = 1; i < rawList.size(); i++) { // 从 1 开始,0 已经处理过
@@ -1196,7 +1198,7 @@ else if (isArrayMainTable && position > 0) { // 数组主表使用专门的缓
if (isSubquery == false && result != null) {
parser.putQueryResult(path, result); // 解决获取关联数据时requestObject里不存在需要的关联数据
- if (isSimpleArray && rawList != null) {
+ if (isSimpleArray) { // FIXME 改为从缓存获取,而不是 result 查
result.put(AbstractSQLExecutor.KEY_RAW_LIST, rawList);
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index eab5a368b..cd0b2c7b8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -39,7 +39,7 @@
public abstract class AbstractParser, L extends List>
implements Parser {
protected static final String TAG = "AbstractParser";
-
+
/**
* JSON 对象、数组对应的数据源、版本、角色、method等
*/
@@ -706,7 +706,7 @@ public M parseCorrectRequest(RequestMethod method, String tag, int version, Stri
return batchVerify(method, tag, version, name, request, maxUpdateCount, creator);
}
-
+
/**自动根据 tag 是否为 TableKey 及是否被包含在 object 内来决定是否包装一层,改为 { tag: object, "tag": tag }
* @param object
* @param tag
@@ -1563,7 +1563,39 @@ else if (join != null){
throw new UnsupportedDataTypeException(TAG + ".onJoinParse join 只能是 String 或 Map 类型!");
}
- Set> set = joinMap == null ? null : joinMap.entrySet();
+ List> slashKeys = new ArrayList<>();
+ List> nonSlashKeys = new ArrayList<>();
+ Set> entries = joinMap == null ? null : joinMap.entrySet();
+
+ if (entries == null || entries.isEmpty()) {
+ Log.e(TAG, "onJoinParse set == null || set.isEmpty() >> return null;");
+ return null;
+ }
+ for (Entry e : entries) {
+ String path = e.getKey();
+ if (path != null && path.indexOf("/") > 0) {
+ slashKeys.add(e); // 以 / 开头的 key,例如 whereJoinMap = new LinkedHashMap<>();
+
+ for (Entry e : nonSlashKeys) {
+ String tableKey = e.getKey(); // 如 "Location_info"
+ Object tableObj = e.getValue(); // value 是 Map
+
+ if (request.containsKey(tableKey)) {
+ whereJoinMap.put(tableKey, tableObj);
+ } else {
+ Log.w(TAG, "跳过 join 中 key = " + tableKey + ",因为它不在 request 中");
+ }
+ }
+
+
+ Set> set = joinMap == null ? null : new LinkedHashSet<>(slashKeys);
+
if (set == null || set.isEmpty()) {
Log.e(TAG, "onJoinParse set == null || set.isEmpty() >> return null;");
return null;
@@ -1759,9 +1791,15 @@ else if (join != null){
j.setAlias(alias);
M outerObj = (M) JSON.createJSONObject((Map) outer);
- j.setOuter(outerObj);
+ j.setOn(outerObj);
j.setRequest(requestObj);
+ if (whereJoinMap.containsKey(table)) {
+ Object rawOuter = whereJoinMap.get(table);
+ M outerObj1 = (M) JSON.createJSONObject((Map) rawOuter);
+ j.setOuter(outerObj1);
+ }
+
if (arrKey != null) {
Integer count = getInteger(parentPathObj, apijson.JSONRequest.KEY_COUNT);
j.setCount(count == null ? getDefaultQueryCount() : count);
@@ -1864,24 +1902,32 @@ else if (join != null){
* @param pathKeys
* @return
*/
- public static V getValue(Map parent, String[] pathKeys) {
- if (parent == null || pathKeys == null || pathKeys.length <= 0) {
+ public static V getValue(Object parent, String[] pathKeys) {
+ int len = parent == null || pathKeys == null ? 0 : pathKeys.length;
+ if (len <= 0) {
Log.w(TAG, "getChild parent == null || pathKeys == null || pathKeys.length <= 0 >> return parent;");
return (V) parent;
}
- //逐层到达child的直接容器JSONObject parent
- int last = pathKeys.length - 1;
- for (int i = 0; i < last; i++) {//一步一步到达指定位置
- if (parent == null) {//不存在或路径错误(中间的key对应value不是JSONObject)
+ // 逐层到达child的直接容器JSONObject parent
+ Object v = parent;
+ for (int i = 0; i < len; i++) { // 一步一步到达指定位置
+ if (v == null) { // 不存在或路径错误(中间的key对应value不是JSONObject)
break;
}
String k = getDecodedKey(pathKeys[i]);
- parent = JSON.get(parent, k);
+ try {
+ v = getFromObjOrArr(v, k);
+ } catch (Throwable e) {
+ if (IS_PRINT_BIG_LOG) {
+ e.printStackTrace();
+ }
+ v = null;
+ }
}
- return parent == null ? null : (V) parent.get(getDecodedKey(pathKeys[last]));
+ return (V) v;
}
@@ -1987,13 +2033,13 @@ public Object getValueByPath(String valuePath) {
}
//取出key被valuePath包含的result,再从里面获取key对应的value
- Map parent = null;
+ Object parent = null;
String[] keys = null;
for (Entry entry : queryResultMap.entrySet()){
String path = entry.getKey();
if (valuePath.startsWith(path + "/")) {
try {
- parent = (M) entry.getValue();
+ parent = entry.getValue();
} catch (Exception e) {
Log.e(TAG, "getValueByPath try { parent = (Map) queryResultMap.get(path); } catch { "
+ "\n parent not instanceof Map!");
@@ -2006,39 +2052,13 @@ public Object getValueByPath(String valuePath) {
}
}
- //逐层到达targetKey的直接容器JSONObject parent
- int last = keys == null ? -1 : keys.length - 1;
- if (last >= 1) {
- for (int i = 0; i < last; i++) {//一步一步到达指定位置parentPath
- if (parent == null) {//不存在或路径错误(中间的key对应value不是JSONObject)
- break;
- }
-
- String k = getDecodedKey(keys[i]);
- Object p = parent.get(k);
- parent = p instanceof Map, ?> ? (Map) p : null;
- }
- }
-
- if (parent != null) {
- Log.i(TAG, "getValueByPath >> get from queryResultMap >> return parent.get(keys[keys.length - 1]);");
- target = last < 0 ? parent : parent.get(getDecodedKey(keys[last])); //值为null应该报错NotExistExeption,一般都是id关联,不可为null,否则可能绕过安全机制
- if (target != null) {
- Log.i(TAG, "getValueByPath >> getValue >> return target = " + target);
- return target;
- }
- }
-
-
- //从requestObject中取值
- target = getValue(requestObject, StringUtil.splitPath(valuePath));
- if (target != null) {
- Log.i(TAG, "getValueByPath >> getValue >> return target = " + target);
- return target;
+ target = getValue(parent, keys); // 逐层到达targetKey的直接容器JSONObject parent
+ if (target == null) { //从requestObject中取值
+ target = getValue(requestObject, StringUtil.splitPath(valuePath));
}
- Log.i(TAG, "getValueByPath return null;");
- return null;
+ Log.i(TAG, "getValueByPath >> getValue >> return target = " + target);
+ return target;
}
/**解码 引用赋值 路径中的 key,支持把 URL encode 后的值,转为 decode 后的原始值,例如 %2Fuser%2Flist -> /user/list ; %7B%7D -> []
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index a919b2733..d9e9eb016 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -1,46 +1,25 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
package apijson.orm;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.regex.Pattern;
-
import apijson.*;
import apijson.orm.Join.On;
import apijson.orm.exception.NotExistException;
import apijson.orm.exception.UnsupportedDataTypeException;
-import apijson.orm.model.Access;
-import apijson.orm.model.AllColumn;
-import apijson.orm.model.AllColumnComment;
-import apijson.orm.model.AllTable;
-import apijson.orm.model.AllTableComment;
-import apijson.orm.model.Column;
-import apijson.orm.model.Document;
-import apijson.orm.model.ExtendedProperty;
-import apijson.orm.model.Function;
-import apijson.orm.model.PgAttribute;
-import apijson.orm.model.PgClass;
-import apijson.orm.model.Request;
-import apijson.orm.model.SysColumn;
-import apijson.orm.model.SysTable;
-import apijson.orm.model.Table;
-import apijson.orm.model.TestRecord;
+import apijson.orm.model.*;
+
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
import static apijson.JSON.getBoolean;
import static apijson.JSON.getString;
import static apijson.JSONMap.*;
-import static apijson.RequestMethod.DELETE;
-import static apijson.RequestMethod.GET;
-import static apijson.RequestMethod.POST;
-import static apijson.RequestMethod.PUT;
-import static apijson.SQL.AND;
-import static apijson.SQL.NOT;
-import static apijson.SQL.ON;
-import static apijson.SQL.OR;
+import static apijson.RequestMethod.*;
+import static apijson.SQL.*;
/**config sql for JSON Request
* @author Lemon
@@ -190,6 +169,7 @@ public abstract class AbstractSQLConfig, L exte
DATABASE_LIST.add(DATABASE_QUESTDB);
DATABASE_LIST.add(DATABASE_IOTDB);
DATABASE_LIST.add(DATABASE_SNOWFLAKE);
+ DATABASE_LIST.add(DATABASE_DATABEND);
DATABASE_LIST.add(DATABASE_DATABRICKS);
DATABASE_LIST.add(DATABASE_REDIS);
DATABASE_LIST.add(DATABASE_MONGODB);
@@ -1252,6 +1232,14 @@ public static boolean isSnowflake(String db) {
return DATABASE_SNOWFLAKE.equals(db);
}
+ @Override
+ public boolean isDatabend() {
+ return isDatabend(gainSQLDatabase());
+ }
+ public static boolean isDatabend(String db) {
+ return DATABASE_DATABEND.equals(db);
+ }
+
@Override
public boolean isDatabricks() {
return isDatabricks(gainSQLDatabase());
@@ -1574,7 +1562,7 @@ public String gainGroupString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getGroup() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -1589,6 +1577,23 @@ public String gainGroupString(boolean hasPrefix) {
first = false;
}
}
+
+ ////先处理左/右关联,内关联忽略
+ //SQLConfig outerConfig = join.getOuterConfig();
+ //SQLConfig outerConfig2 = (outerConfig != null && outerConfig.getGroup() != null) || join.isLeftOrRightJoin() ? outerConfig : null;
+ //
+ //if (outerConfig2 != null) {
+ // outerConfig2.setMain(false).setKeyPrefix(true);
+ // //if (StringUtil.isEmpty(cfg.getAlias(), true)) {
+ // // cfg.setAlias(cfg.getTable());
+ // //}
+ // String c = ((AbstractSQLConfig, ?, ?>) outerConfig2).gainGroupString(false);
+ //
+ // if (StringUtil.isNotEmpty(c, true)) {
+ // joinGroup += (first ? "" : ", ") + c;
+ // first = false;
+ // }
+ //}
}
}
@@ -1650,7 +1655,7 @@ public String gainHavingString(boolean hasPrefix) throws Exception {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getHaving() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -1763,7 +1768,7 @@ public String gainSampleString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getSample() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -1833,7 +1838,7 @@ public String gainLatestString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getLatest() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -1898,7 +1903,7 @@ public String gainPartitionString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getPartition() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -1963,7 +1968,7 @@ public String gainFillString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getFill() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -2029,6 +2034,7 @@ public AbstractSQLConfig setOrder(String order) {
public String gainOrderString(boolean hasPrefix) {
//加上子表的 order
String joinOrder = "";
+ String joinOuterOrder = "";
if (joinList != null) {
boolean first = true;
for (Join join : joinList) {
@@ -2036,7 +2042,7 @@ public String gainOrderString(boolean hasPrefix) {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
SQLConfig cfg = (ocfg != null && ocfg.getOrder() != null) || join.isLeftOrRightJoin() ? ocfg : join.getJoinConfig();
if (cfg != null) {
@@ -2345,7 +2351,7 @@ public String gainColumnString(boolean inSQLJoin) throws Exception {
continue;
}
- SQLConfig ocfg = join.getOuterConfig();
+ SQLConfig ocfg = join.getOnConfig();
boolean isEmpty = ocfg == null || ocfg.getColumn() == null;
boolean isLeftOrRightJoin = join.isLeftOrRightJoin();
@@ -3729,8 +3735,9 @@ protected String concatJoinWhereString(String whereString) throws Exception {
List pvl = new ArrayList<>(getPreparedValueList());
SQLConfig jc;
+ SQLConfig outerConfig;
String js;
-
+ boolean isWsEmpty = StringUtil.isEmpty(ws, true);
boolean changed = false;
// 各种 JOIN 没办法统一用 & | !连接,只能按优先级,和 @combine 一样?
for (Join j : joinList) {
@@ -3741,6 +3748,27 @@ protected String concatJoinWhereString(String whereString) throws Exception {
case "@": // APP JOIN
case "<": // LEFT JOIN
case ">": // RIGHT JOIN
+ outerConfig = j.getOuterConfig();
+ if (outerConfig == null){
+ break;
+ }
+ boolean isMain1 = outerConfig.isMain();
+ outerConfig.setMain(false).setPrepared(isPrepared()).setPreparedValueList(new ArrayList());
+ String outerWhere = outerConfig.gainWhereString(false);
+
+ int logic1 = Logic.getType(jt);
+ newWs += " ( "
+ + gainCondition(
+ Logic.isNot(logic1),
+ ws
+ + ( isWsEmpty ? "" : (Logic.isAnd(logic1) ? AND : OR) )
+ + " ( " + outerWhere + " ) "
+ )
+ + " ) ";
+ newPvl.addAll(pvl);
+ newPvl.addAll(outerConfig.getPreparedValueList());
+
+ changed = true;
break;
case "&": // INNER JOIN: A & B
@@ -3761,7 +3789,7 @@ protected String concatJoinWhereString(String whereString) throws Exception {
boolean isSideJoin = "^".equals(jt);
boolean isAntiJoin = "(".equals(jt);
boolean isForeignJoin = ")".equals(jt);
- boolean isWsEmpty = StringUtil.isEmpty(ws, true);
+ //boolean isWsEmpty = StringUtil.isEmpty(ws, true);
if (isWsEmpty) {
if (isOuterJoin) { // ! OUTER JOIN: ! (A | B)
@@ -5202,7 +5230,7 @@ public String gainJoinString() throws Exception {
);
}
- SQLConfig oc = j.getOuterConfig();
+ SQLConfig oc = j.getOnConfig();
String ow = null;
if (oc != null) {
oc.setPrepared(isPrepared());
@@ -6305,13 +6333,22 @@ else if (joinConfig.getDatabase().equals(config.getDatabase()) == false) {
joinConfig.setMain(false).setKeyPrefix(true);
+ if (join.getOn() != null) {
+ SQLConfig onConfig = newSQLConfig(method, table, alias, join.getOn(), null, false, callback);
+ onConfig.setMain(false)
+ .setKeyPrefix(true)
+ .setDatabase(joinConfig.getDatabase())
+ .setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
+
+ join.setOnConfig(onConfig);
+ }
+
if (join.getOuter() != null) {
SQLConfig outerConfig = newSQLConfig(method, table, alias, join.getOuter(), null, false, callback);
outerConfig.setMain(false)
.setKeyPrefix(true)
.setDatabase(joinConfig.getDatabase())
.setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
-
join.setOuterConfig(outerConfig);
}
}
@@ -6322,9 +6359,12 @@ LEFT JOIN ( SELECT count(*) AS count FROM sys.Comment ) AS Comment ON Comment.m
if (RequestMethod.isHeadMethod(method, true)) {
List onList = join.getOnList();
List column = onList == null ? null : new ArrayList<>(onList.size());
- if (column != null) {
+ //解决 pg 如果只查询关联键,会报找不到column的错误
+ ///* SELECT count(*) AS count FROM sys.Moment AS Moment
+ // LEFT JOIN ( SELECT * FROM sys.Comment ) AS Comment ON Comment.momentId = Moment.id LIMIT 1 OFFSET 0 */
+ if (column != null && joinConfig.isMSQL()) { // 暂时这样兼容 PostgreSQL 等不支持 SELECT 中不包含对应 key 的隐式 ON 关联字段的数据库
for (On on : onList) {
- column.add(on.getKey());
+ column.add(on.getKey()); // TODO PostgreSQL 等需要找到具体的 targetTable 对应 targetKey 来加到 SELECT,比直接 SELECT * 性能更好
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 5ec59f36c..797ac3dee 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -1315,7 +1315,7 @@ public Connection getConnection(@NotNull SQLConfig config) throws Excep
}
public String getConnectionKey(@NotNull SQLConfig config) {
- return getConnectionKey(config.getNamespace(), config.getCatalog(), config.getDatasource(), config.getDatabase());
+ return getConnectionKey(config.getDatabase(), config.getDatasource(), config.getNamespace(), config.getCatalog());
}
public String getConnectionKey(String database, String datasource, String namespace, String catalog) {
return database + "-" + datasource + "-" + namespace + "-" + catalog;
@@ -1541,3 +1541,4 @@ public int executeUpdate(@NotNull SQLConfig config, String sql) throws
}
+
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 0c52dca43..44b2d7f1d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -6,6 +6,8 @@
package apijson.orm;
import static apijson.JSON.*;
+import static apijson.JSONMap.KEY_COMBINE;
+import static apijson.JSONMap.KEY_KEY;
import static apijson.RequestMethod.DELETE;
import static apijson.RequestMethod.GET;
import static apijson.RequestMethod.GETS;
@@ -219,7 +221,7 @@ public static HashMap getAccessMap(MethodAccess access)
@Override
public String getVisitorIdKey(SQLConfig config) {
- return config.getUserIdKey();
+ return config == null ? getUserIdKey(null, null, null, null) : config.getUserIdKey();
}
@Override
@@ -346,21 +348,23 @@ public void verifyAllowRole(SQLConfig config, String table, RequestMeth
* @throws Exception
* @see {@link JSONMap#KEY_ROLE}
*/
- public void verifyUseRole(SQLConfig config, String table, RequestMethod method, String role) throws Exception {
+ public void verifyUseRole(@NotNull SQLConfig config, String table, RequestMethod method, String role) throws Exception {
Log.d(TAG, "verifyUseRole table = " + table + "; method = " + method + "; role = " + role);
+ Objects.requireNonNull(config);
//验证角色,假定真实强制匹配<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- String visitorIdKey = getVisitorIdKey(config);
if (table == null) {
- table = config == null ? null : config.getTable();
+ table = config.getTable();
}
if (method == null) {
- method = config == null ? GET : config.getMethod();
+ method = config.getMethod();
}
if (role == null) {
- role = config == null ? UNKNOWN : config.getRole();
+ role = config.getRole();
}
+ String visitorIdKey = getVisitorIdKey(config);
+
Object requestId;
switch (role) {
case LOGIN://verifyRole通过就行
@@ -1087,14 +1091,23 @@ public static , L extends List> M parse
// 判断不允许传的key<<<<<<<<<<<<<<<<<<<<<<<<<
for (String rk : rkset) {
+ if (rk == null) { // 无效的key
+ real.remove(rk);
+ continue;
+ }
+
if (refuseSet.contains(rk)) { // 不允许的字段
throw new IllegalArgumentException(method + "请求," + name
+ " 里面不允许传 " + rk + " 等" + StringUtil.get(refuseSet) + "内的任何字段!");
}
- if (rk == null) { // 无效的key
- real.remove(rk);
- continue;
+ if (KEY_COMBINE.equals(rk)) {
+ throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!" +
+ "非开放请求不允许传 " + KEY_COMBINE + ":value !");
+ }
+ if (KEY_KEY.equals(rk)) {
+ throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!" +
+ "非开放请求不允许传 " + KEY_KEY + ":value !");
}
Object rv = real.get(rk);
diff --git a/APIJSONORM/src/main/java/apijson/orm/Entry.java b/APIJSONORM/src/main/java/apijson/orm/Entry.java
index a8aaf7bfd..3e010435b 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Entry.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Entry.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/FunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/FunctionParser.java
index 155d80fae..8af029234 100644
--- a/APIJSONORM/src/main/java/apijson/orm/FunctionParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/FunctionParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
index 0b772c238..40bfa147a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/Join.java b/APIJSONORM/src/main/java/apijson/orm/Join.java
index 39ca09a61..96d5e2ec7 100644
--- a/APIJSONORM/src/main/java/apijson/orm/Join.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Join.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -25,12 +25,14 @@ public class Join, L extends List> {
private List onList; // ON User.id = Moment.userId AND ...
private M request; // { "id@":"/Moment/userId" }
- private M outer; // "join": { " joinConfig;
private SQLConfig cacheConfig;
- private SQLConfig outerConfig;
+ private SQLConfig onConfig;
+ private SQLConfig outerConfig;
public String getPath() {
return path;
@@ -78,13 +80,29 @@ public M getRequest() {
public void setRequest(M request) {
this.request = request;
}
- public M getOuter() {
- return outer;
+ public M getOn() {
+ return on;
+ }
+ public void setOn(M on) {
+ this.on = on;
}
+
public void setOuter(M outer) {
this.outer = outer;
}
+ public M getOuter() {
+ return outer;
+ }
+
+ public SQLConfig getOuterConfig() {
+ return outerConfig;
+ }
+
+ public void setOuterConfig(SQLConfig outerConfig) {
+ this.outerConfig = outerConfig;
+ }
+
public SQLConfig getJoinConfig() {
return joinConfig;
}
@@ -97,11 +115,11 @@ public SQLConfig getCacheConfig() {
public void setCacheConfig(SQLConfig cacheConfig) {
this.cacheConfig = cacheConfig;
}
- public SQLConfig getOuterConfig() {
- return outerConfig;
+ public SQLConfig getOnConfig() {
+ return onConfig;
}
- public void setOuterConfig(SQLConfig outerConfig) {
- this.outerConfig = outerConfig;
+ public void setOnConfig(SQLConfig onConfig) {
+ this.onConfig = onConfig;
}
public boolean isOne2One() {
diff --git a/APIJSONORM/src/main/java/apijson/orm/Logic.java b/APIJSONORM/src/main/java/apijson/orm/Logic.java
index bb8e806e6..f860aa9a4 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Logic.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Logic.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
index 8817886d1..5d207f41a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/ObjectParser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/OnParseCallback.java b/APIJSONORM/src/main/java/apijson/orm/OnParseCallback.java
index 243acf048..e02fd90f8 100755
--- a/APIJSONORM/src/main/java/apijson/orm/OnParseCallback.java
+++ b/APIJSONORM/src/main/java/apijson/orm/OnParseCallback.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java
index 2976d09b0..1c4f2dc5f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Operation.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/Pair.java b/APIJSONORM/src/main/java/apijson/orm/Pair.java
index 94f2abcc4..7661d07b5 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Pair.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Pair.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/Parser.java b/APIJSONORM/src/main/java/apijson/orm/Parser.java
index ce726eb73..1492dfc11 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Parser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Parser.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/ParserCreator.java b/APIJSONORM/src/main/java/apijson/orm/ParserCreator.java
index d6eb1c7db..f3f8d375b 100755
--- a/APIJSONORM/src/main/java/apijson/orm/ParserCreator.java
+++ b/APIJSONORM/src/main/java/apijson/orm/ParserCreator.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
index b8c9fad86..5386d160e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java
@@ -1,4 +1,4 @@
-/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+/*Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
@@ -35,6 +35,7 @@ public interface SQLConfig, L extends List, L extends List
cloudAndMonkey 提交的 11 个 Commits, 对 APIJSON 做出了 1,496 增加和 845 处删减(截止 2022/12/15 日);
diff --git a/Document-English.md b/Document-English.md
index c9a4f2948..62f40b0a0 100644
--- a/Document-English.md
+++ b/Document-English.md
@@ -324,7 +324,7 @@ Response:
Fuzzy matching | `"key$":"SQL search expressions"` => `"key$":["SQL search expressions"]` Any SQL search expressions.Eg.%key%(include key), key%(start with key),%k%e%y%(include k, e, y). % means any characters. | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}) In SQL, it's `name LIKE '%m%'`, meaning that get User with ‘m’ in name.
Regular Expression| `"key~":"regular expression"` => `"key~":["regular expression"]` It can be any regular expressions.Eg. ^[0-9]+$ ,*~ not case sensitive, advanced search is applicable.| ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}) In SQL, it's `name REGEXP '^[0-9]+$'`.
Get data in a range| `"key%":"start,end"` => `"key%":["start,end"]` The data type of start and end can only be either Boolean, Number or String. Eg. "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"]. It's used for getting data from a specific time range. | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}) In SQL, it's `date BETWEEN '2017-10-01' AND '2018-10-01'`, meaning to get User data that registered between 2017-10-01 and 2018-10-01.
- Make an alias | `"name:alias"` this changes name to alias in returning results. It’s applicable to column, tableName, SQL Functions, etc. but only in GET, HEAD requests. | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}) In SQL, it's `toId AS parentId`. It'll return `parentId` instead of `toId`.
+ Make an alias | `"name:alias"` this changes name to alias in returning results. It’s applicable to column, tableName, SQL Functions, etc. but only in GET, HEAD requests. | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}) In SQL, it's `toId AS parentId`. It'll return `parentId` instead of `toId`. For @key format like "lc_wai6b3vk2:(lc_wai6b3vk)", it means renaming field lc_wai6b3vk2 to lc_wai6b3vk, commonly used for field renaming scenarios. Example: { "lc_sinan_ba074fbb": { "lc_wai6b3vk": "11", "lc_wai6b3vk2": "22", "@combine": "lc_wai6b3vk \\| lc_wai6b3vk2", "@key": "lc_wai6b3vk2:(lc_wai6b3vk)" } } corresponds to SQL `(lc_wai6b3vk = '11' OR lc_wai6b3vk2 = '22')`, but the lc_wai6b3vk2 field will be renamed and displayed as lc_wai6b3vk in the returned result
Add / expand an item | `"key+":Object` The type of Object is decided by *key*. Types can be Number, String, JSONArray. Froms are 82001,"apijson",["url0","url1"] respectively. It’s only applicable to PUT request.| "praiseUserIdList+":[82001]. In SQL, it's `json_insert(praiseUserIdList,82001)`. Add an *id* that praised the Moment.
Delete / decrease an item | `"Key-":Object` It’s the contrary of "key+" | "balance-":100.00. In SQL, it's `balance = balance - 100.00`, meaning there's 100 less in balance.
Operations | &, \|, ! They're used in logic operations. It’s the same as AND, OR, NOT in SQL respectively. By default, for the same key, it’s ‘\|’ (OR)operation among conditions; for different keys, the default operation among conditions is ‘&’(AND). | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}) In SQL, it's `id>80000 AND id<=90000`, meaning *id* needs to be id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}) It's the same as "id{}":">90000,<=80000". In SQL, it's `id>80000 OR id<=90000`, meaning that *id* needs to be id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}) In SQL, it's `id NOT IN(82001,38710)`, meaning id needs to be ! (id=82001 \| id=38710).
diff --git a/Document.md b/Document.md
index a97f7b823..11db6fe86 100644
--- a/Document.md
+++ b/Document.md
@@ -408,19 +408,19 @@ DELETE: 删除数据 | base_url/delete/ | { TableName:{<
匹配条件范围 | "key{}":"条件0,条件1...",条件为 SQL 表达式字符串,可进行数字比较运算等 | ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}),对应 SQL 是`id<=80000 OR id>90000`,查询 id 符合 id\<=80000 \| id>90000 的一个 User 数组
包含选项范围 | "key<\>":value => "key<\>":[value],key 对应值的类型必须为 JSONArray,value 值类型只能为 Boolean, Number, String 中的一种 | ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}),对应SQL是`json_contains(contactIdList,38710)`,查询 contactIdList 包含 38710 的一个 User 数组
判断是否存在 | "key}{@":{ "from":"Table", "Table":{ ... } } 其中: }{ 表示 EXISTS; key 用来标识是哪个判断; @ 后面是 子查询 对象,具体见下方 子查询 的说明。 | ["id}{@":{ "from":"Comment", "Comment":{ "momentId":15 } }](http://apijson.cn:8080/get/{"User":{"id}{@":{"from":"Comment","Comment":{"momentId":15}}}}) WHERE EXISTS(SELECT * FROM Comment WHERE momentId=15)
- 远程调用函数 | "key()":"函数表达式",函数表达式为 function(key0,key1...),会调用后端对应的函数 function(JSONObject request, String key0, String key1...),实现 参数校验、数值计算、数据同步、消息推送、字段拼接、结构变换 等特定的业务逻辑处理, 可使用 - 和 + 表示优先级,解析 key-() > 解析当前对象 > 解析 key() > 解析子对象 > 解析 key+() | ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}),会调用远程函数 [boolean isContain(JSONObject request, String array, String value)](https://github.com/APIJSON/apijson-framework/blob/master/src/main/java/apijson/framework/APIJSONFunctionParser.java#L361-L374) ,然后变为 "isPraised":true 这种(假设点赞用户 id 列表包含了 userId,即这个 User 点了赞)
- 存储过程 | "@key()":"SQL函数表达式",函数表达式为 function(key0,key1...) 会调用后端数据库对应的存储过程 SQL 函数 function(String key0, String key1...) 除了参数会提前赋值,其它和 远程函数 一致 | ["@limit":10, "@offset":0, "@procedure()":"getCommentByUserId(id,@limit,@offset)"](http://apijson.cn:8080/get/{"User":{"@limit":10,"@offset":0,"@procedure()":"getCommentByUserId(id,@limit,@offset)"}}) 会转为 `getCommentByUserId(38710,10,0)` 来调用存储过程 SQL 函数 `getCommentByUserId(IN id bigint, IN limit int, IN offset int)` 然后变为 "procedure":{ "count":-1, "update":false, "list":[] } 其中 count 是指写操作影响记录行数,-1 表示不是写操作;update 是指是否为写操作(增删改);list 为返回结果集
- 引用赋值 | "key@":"key0/key1/.../refKey",引用路径为用 / 分隔的字符串。以 / 开头的是缺省引用路径,从声明 key 所处容器的父容器路径开始;其它是完整引用路径,从最外层开始。 被引用的 refKey 必须在声明 key 的上面。如果对 refKey 的容器指定了返回字段,则被引用的 refKey 必须写在 @column 对应的值内,例如 "@column":"refKey,key1,..." | ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) User 内的 id 引用了与 User 同级的 Moment 内的 userId, 即 User.id = Moment.userId,请求完成后 "id@":"/Moment/userId" 会变成 "id":38710
- 子查询 | "key@":{ "range":"ALL", "from":"Table", // 可省略,默认为首个表对象 key 名 "Table":{ ... } } 其中: range 可为 ALL,ANY; from 为目标表 Table 的名称; @ 后面的对象类似数组对象,可使用 count 和 join 等功能。 | ["id@":{ "from":"Comment", // 可省略 "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id=(SELECT min(userId) FROM Comment)
- 模糊搜索 | `"key$":"SQL搜索表达式"` => `"key$":["SQL搜索表达式"]`,任意 SQL 搜索表达式字符串,如 %key%(包含 key), key%(以 key 开始), %k%e%y%(包含字母 k,e,y) 等,% 表示任意字符 | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}),对应 SQL 是`name LIKE '%m%'`,查询 name 包含 "m" 的一个 User 数组
- 正则匹配 | "key~":"正则表达式" => "key~":["正则表达式"],任意正则表达式字符串,如 ^[0-9]+$ ,*~ 忽略大小写,可用于高级搜索 | ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}),对应 SQL 是`name REGEXP '^[0-9]+$'`,查询 name 中字符全为数字的一个 User 数组
- 连续范围 | "key%":"start,end" => "key%":["start,end"],其中 start 和 end 都只能为 Number, String 中的一种,如 "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"] ,可用于连续范围内的筛选 | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}),对应SQL是`date BETWEEN '2017-10-01' AND '2018-10-01'`,查询在2017-10-01和2018-10-01期间注册的用户的一个User数组
- 新建别名 | "name:alias",name 映射为 alias,用 alias 替代 name。可用于 column,Table,SQL 函数 等。只用于 GET 类型、HEAD 类型的请求 | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}),对应 SQL 是`toId AS parentId`,将查询的字段 toId 变为 parentId 返回
- 增加 或 扩展 | "key+":Object,Object的类型由key指定,且类型为 Number,String,JSONArray 中的一种。如 82001,"apijson",["url0","url1"] 等。只用于 PUT 请求 | "praiseUserIdList+":[82001],对应 SQL 是`json_insert(praiseUserIdList,82001)`,添加一个点赞用户 id,即这个用户点了赞
+ 远程调用函数 | "key()":"函数表达式",函数表达式为 function(key0,key1...),会调用后端对应的函数 function(JSONObject request, String key0, String key1...),实现 参数校验、数值计算、数据同步、消息推送、字段拼接、结构变换 等特定的业务逻辑处理, 可使用 - 和 + 表示优先级,解析 key-() > 解析当前对象 > 解析 key() > 解析子对象 > 解析 key+() | ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}),会调用远程函数 [boolean isContain(JSONObject request, String array, String value)](https://github.com/APIJSON/apijson-framework/blob/master/src/main/java/apijson/framework/APIJSONFunctionParser.java#L361-L374) ,然后变为 "isPraised":true 这种(假设点赞用户id列表包含了userId,即这个User点了赞)
+ 存储过程 | "@key()":"SQL函数表达式",函数表达式为 function(key0,key1...) 会调用后端数据库对应的存储过程 SQL函数 function(String key0, String key1...) 除了参数会提前赋值,其它和 远程函数 一致 | ["@limit":10, "@offset":0, "@procedure()":"getCommentByUserId(id,@limit,@offset)"](http://apijson.cn:8080/get/{"User":{"@limit":10,"@offset":0,"@procedure()":"getCommentByUserId(id,@limit,@offset)"}}) 会转为 `getCommentByUserId(38710,10,0)` 来调用存储过程 SQL 函数 `getCommentByUserId(IN id bigint, IN limit int, IN offset int)` 然后变为 "procedure":{ "count":-1, "update":false, "list":[] } 其中 count 是指写操作影响记录行数,-1 表示不是写操作;update 是指是否为写操作(增删改);list 为返回结果集
+ 引用赋值 | "key@":"key0/key1/.../refKey",引用路径为用/分隔的字符串。以/开头的是缺省引用路径,从声明key所处容器的父容器路径开始;其它是完整引用路径,从最外层开始。 被引用的refKey必须在声明key的上面。如果对refKey的容器指定了返回字段,则被引用的refKey必须写在@column对应的值内,例如 "@column":"refKey,key1,..." | ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) User内的id引用了与User同级的Moment内的userId, 即User.id = Moment.userId,请求完成后 "id@":"/Moment/userId" 会变成 "id":38710
+ 子查询 | "key@":{ "range":"ALL", "from":"Table", "Table":{ ... } } 其中: range 可为 ALL,ANY; from 为目标表 Table 的名称; @ 后面的对象类似数组对象,可使用 count 和 join 等功能。 | ["id@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id=(SELECT min(userId) FROM Comment)
+ 模糊搜索 | `"key$":"SQL搜索表达式"` => `"key$":["SQL搜索表达式"]`,任意SQL搜索表达式字符串,如 %key%(包含key), key%(以key开始), %k%e%y%(包含字母k,e,y) 等,%表示任意字符 | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}),对应SQL是`name LIKE '%m%'`,查询name包含"m"的一个User数组
+ 正则匹配 | "key~":"正则表达式" => "key~":["正则表达式"],任意正则表达式字符串,如 ^[0-9]+$ ,*~ 忽略大小写,可用于高级搜索 | ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}),对应SQL是`name REGEXP '^[0-9]+$'`,查询name中字符全为数字的一个User数组
+ 连续范围 | "key%":"start,end" => "key%":["start,end"],其中 start 和 end 都只能为 Boolean, Number, String 中的一种,如 "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"] ,可用于连续范围内的筛选 | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}),对应SQL是`date BETWEEN '2017-10-01' AND '2018-10-01'`,查询在2017-10-01和2018-10-01期间注册的用户的一个User数组
+ 新建别名 | ① "name:alias",name 映射为 alias,用 alias 替代 name,可用于 column,Table,SQL 函数 等,只用于 GET 类型、HEAD 类型的请求 ② 函数调用映射 "@key": "fun:avg(id);keyA:(keyB)", "fun>": 1, "keyA": 1 其中 fun:fun(arg) 把 SQL 函数调用 fun(arg) 作为左侧表达式替代 fun,即 fun(arg) > 1; keyA:(keyB) 表示将字段 keyA 重命名为 keyB,即实际 SQL 中为 keyB = 1,常用于重命名冲突的多条件同名字段。 | ① ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}),对应 SQL 是 `toId AS parentId`,将查询的字段 toId 变为 parentId 返回 ② ["@key": "len:length(content);mid:(momentId)", "len<=": 10, "mid": 12, "momentId": 15, "@combine": "(len<= \\| mid) & momentId"](http://apijson.cn/api?type=JSON&json={%22Comment%22:{%22@key%22:%22len%3Alength(content)%3Bmid%3A(momentId)%22,%22len%3C=%22:10,%22mid%22:12,%22momentId%22:15,%22@combine%22:%22(len%3C%3D%20%7C%20mid)%20%26%20momentId%22}}) 对应 SQL 是 `(length(content) <= 10 OR momentId = 12) AND momentId = 15`
+ 增加 或 扩展 | "key+":Object,Object的类型由key指定,且类型为Number,String,JSONArray中的一种。如 82001,"apijson",["url0","url1"] 等。只用于PUT请求 | "praiseUserIdList+":[82001],对应SQL是`json_insert(praiseUserIdList,82001)`,添加一个点赞用户id,即这个用户点了赞
减少 或 去除 | "key-":Object,与"key+"相反 | "balance-":100.00,对应SQL是`balance = balance - 100.00`,余额减少100.00,即花费了100元
比较运算 | >, <, >=, <= 比较运算符,用于 ① 提供 "id{}":"<=90000" 这种条件范围的简化写法 ② 实现子查询相关比较运算 不支持 "key=":Object 和 "key!=":Object 这两种写法,直接用更简单的 "key":Object 和 "key!":Object 替代。 | ① ["id<=":90000](http://apijson.cn:8080/get/{"[]":{"User":{"id<=":90000}}}),对应 SQL 是`id<=90000`,查询符合id<=90000的一个User数组 ② ["id>@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id>@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) WHERE id>(SELECT min(userId) FROM Comment)
逻辑运算 | &, \|, ! 逻辑运算符,对应数据库 SQL 中的 AND, OR, NOT。 横或纵与:同一键值对的值内条件默认 \| 或连接,可以在 key 后加逻辑运算符来具体指定;不同键值对的条件默认 & 与连接,可以用下面说明的对象关键词 @combine 来具体指定。 ① & 可用于 "key&{}":"条件"等 ② \| 可用于 "key\|{}":"条件", "key\|{}":[]等,一般可省略 ③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000 ② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同 "id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000 ③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
- 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑤ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑤ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
+ 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定 ① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100 ② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用 ③ "query":2,查询内容 0-对象,1-总数和分页详情,2-数据、总数和分页详情 总数关键词为 total,分页详情关键词为 info, 它们都和 query 同级,通过引用赋值得到自定义 key:value 键值对,不传则返回默认键值对,例如 "total@":"/[]/total", "info@":"/[]/info" 这里query及total仅为GET类型的请求提供方便, 一般可直接用HEAD类型的请求获取总数 ④ "compat": true 处理最外层查询字段有特殊处理的情况下 "query":2 返回查询总数不准确的问题:如DISTINCT去重、Aggregate 函数等 ⑤ "join":"&/Table0,\"join":{ "&/Table0":{}, // 支持 ON 多个字段关联, "\ "key0":value0, // 其它ON条件 "key2":value2, ... "@combine":"...", // 其它ON条件的组合方式 "@column":"...", // 外层 SELECT "@group":"...", // 外层 GROUP BY "@having":"..." // 外层 HAVING } } 多表连接方式: "@" - APP JOIN "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "*" - CROSS JOIN "^" - SIDE JOIN "(" - ANTI JOIN ")" - FOREIGN JOIN 其中 @ APP JOIN 为应用层连表,会从已查出的主表里取得所有副表 key@ 关联的主表内的 refKey 作为一个数组 refKeys: [value0, value1...],然后把原来副表 count 次查询 key=$refKey 的 SQL 用 key IN($refKeys) 的方式合并为一条 SQL 来优化性能; 其它 JOIN 都是 SQL JOIN,具体功能和 MySQL,PostgreSQL 等数据库的 JOIN 一一对应 `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` 会对应生成 `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey` AND 其它ON条件 除了 = 等价关联,也支持 ! 不等关联、\> \< \>= \<= 等比较关联和 $ ~ {} <> 等其它复杂关联方式 ⑥ "otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 查询User数组,最多5个: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}}) 对应 SQL 是`LIMIT 5` ② 查询第3页的User数组,每页5个: ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}}) 对应 SQL 是`LIMIT 5 OFFSET 15` ③ 查询User数组和对应的User总数: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total", // 可省略 "info@":"/[]/info" // 可省略](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal","info@":"%252F[]%252Finfo"}) 返回的数据中,总数及分页详情结构为: "total":139, // 总数 "info":{ // 分页详情 "total":139, // 总数 "count":5, // 每页数量 "page":0, // 当前页码 "max":27, // 最大页码 "more":true, // 是否还有更多 "first":true, // 是否为首页 "last":false // 是否为尾页 } ④查询User数组ID唯一情况下的User总数: ["[]":{ "query":2, "compat":"true", "User":{ "@column":"DISTINCT id" } }](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}}}) 返回的数据和结构同上 ⑤ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join":"&/User/id@,\ "Moment":{ "@group":"id" // 主副表不是一对一,要去除重复数据 }, "User":{ "name~":"t", "id@":"/Moment/userId" }, "Comment":{ "momentId@":"/Moment/id" } }](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/get&json=%7B%22%5B%5D%22:%7B%22count%22:5,%22join%22:%22%26%2FUser%2Fid@,%3C%2FComment%22,%22Moment%22:%7B%22@column%22:%22id,userId,content%22,%22@group%22:%22id%22%7D,%22User%22:%7B%22name~%22:%22t%22,%22id@%22:%22%2FMoment%2FuserId%22,%22@column%22:%22id,name,head%22%7D,%22Comment%22:%7B%22momentId@%22:%22%2FMoment%2Fid%22,%22@column%22:%22id,momentId,content%22%7D%7D%7D) ⑥ 每一层都加当前用户名: ["User":{}, "[]":{ "name@":"User/name", // 自定义关键词 "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
对象关键词,可自定义 | "@key":Object,@key 为 Table:{} 中 {} 内的关键词,Object 的类型由 @key 指定 ① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。 ② "@column":"column;function(arg)...",返回字段 ③ "@order":"column0+,column1-...",排序方式 ④ "@group":"column0,column1...",分组方式。如果 @column 里声明了 Table 的 id,则 id 也必须在 @group 中声明;其它情况下必须满足至少一个条件: 1.分组的 key 在 @column 里声明 2.Table 主键在 @group 中声明 ⑤ "@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // OR 连接,或 "@having&":"function0(...)?value0;function1(...)?value1;function2(...)?value2..." // AND 连接,或 "@having":{ "h0":"function0(...)?value0", "h1":function1(...)?value1", "h2":function2(...)?value2...", "@combine":"h0 & (h1 \| !h2)" // 任意组合,非必传 } SQL 函数条件,一般和 @group 一起用,函数一般在 @column 里声明 ⑥ "@schema":"sys",集合空间(数据库名/模式),非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑦ "@database":"POSTGRESQL",数据库类型,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑧ "@datasource":"DRUID",跨数据源,非默认的值可通过它来指定,可以在最外层作为全局默认配置 ⑨ "@json":"key0,key1...",转为 JSON 格式返回,符合 JSONObject 则转为 {...},符合 JSONArray 则转为 \[...] ⑩ "@role":"OWNER",来访角色,包括 UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, 可以在最外层作为全局默认配置, 可自定义其它角色并重写 Verifier.verify 等相关方法来自定义校验 ⑪ "@explain":true,性能分析,可以在最外层作为全局默认配置 ⑫ "@raw":"key0,key1...",其中 key0, key1 都对应有键值对 "key0":"SQL片段或SQL片段的别名", "key1":"SQL片段或SQL片段的别名" 自定义原始SQL片段,可扩展嵌套SQL函数等复杂语句,必须是后端已配置的,只有其它功能符都做不到才考虑,谨慎使用,注意防 SQL 注入 ⑬ "@null":"key1,key2...",空值键值对,自动插入 key1:null, key2:null ... 并作为有效键值对执行,作为条件时对应 SQL 是 `WHERE tag IS NULL`,作为值时对应 SQL 是 `SET tag = NULL` ⑭ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'` ② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name` ③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id` ④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id` ⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100` ⑥ 查询 sys 内的 User 表: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}}) 对应 SQL 是`FROM sys.User` ⑦ 查询 PostgreSQL 数据库的 User 表: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL","@explain":true}}) ⑧ 使用 Druid 连接池查询 User 表: ["@datasource":"DRUID"](http://apijson.cn:8080/get/{"User":{"@datasource":"DRUID"}}) ⑨ 将 VARCHAR 字符串字段 get 转为 JSONArray 返回: ["@json":"get"](http://apijson.cn:8080/get/{"Access":{"@json":"get"}}) ⑩ 查询当前用户的动态: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}}) ⑪ 开启性能分析: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}}) 对应 SQL 是`EXPLAIN` ⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7`` ⑬ 把用户的标签设置为空 ["@null":"tag"](http://apijson.cn/api/?type=JSON&url=http://apijson.cn:8080/put/User&json={%22id%22:82001,%22@null%22:%22tag%22,%22@explain%22:true}) ⑭ 从pictureList 获取第 0 张图片: ["@position":0, // 自定义关键词 "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
全局关键词 | 为最外层对象 {} 内的关键词。其中 @database,@schema, @datasource, @role, @explain 基本同对象关键词,见上方说明,区别是全局关键词会每个表对象中没有时自动放入,作为默认值。 ① "tag":"Table",后面的 tag 是非 GET、HEAD 请求中匹配请求的 JSON 结构的标识,一般是要查询的 Table 的名称或该名称对应的数组 Table[] 或 Table:[],由后端 Request 表中指定。 ② "version":1,接口版本,version 不传、为 null 或 <=0 都会使用最高版本,传了其它有效值则会使用最接近它的最低版本,由后端 Request 表中指定。 ③ "format":true,格式化返回 Response JSON 的 key,一般是将 TableName 转为 tableName, TableName[] 转为 tableNameList, Table:alias 转为 alias, TableName-key[] 转为 tableNameKeyList 等小驼峰格式。 | ① 查隐私信息: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}}) ③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
diff --git a/LICENSE b/LICENSE
index 1a71560c8..9743dd5aa 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,18 +1,18 @@
Tencent is pleased to support the open source community by making APIJSON available.
-Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+Copyright (C) 2020 Tencent. All rights reserved.
APIJSON is licensed under the Apache License Version 2.0.
A copy of the Apache License Version 2.0 is included in this file.
+The copyright notice pertaining to the Tencent code in this repo was previously in the name of “THL A29 Limited.”
+That entity has now been de-registered.
+You should treat all previously distributed copies of the code as if the copyright notice was in the name of “Tencent.”
Other dependencies and licenses:
Open Source Software Licensed under the Apache License Version 2.0:
--------------------------------------------------------------------
-1. fastjson
-Copyright 1999-2019 Alibaba Group Holding Ltd.
-
Terms of Apache License Version 2.0
diff --git a/README-English.md b/README-English.md
index e7c9e6d04..ccafb9850 100644
--- a/README-English.md
+++ b/README-English.md
@@ -1,5 +1,5 @@
Tencent is pleased to support the open source community by making APIJSON available.
-Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0
diff --git a/README.md b/README.md
index 098c17fc1..281462b98 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
Tencent is pleased to support the open source community by making APIJSON available.
-Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
+Copyright (C) 2020 Tencent. All rights reserved.
This source code is licensed under the Apache License Version 2.0
@@ -66,6 +66,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -191,7 +192,7 @@ https://github.com/Tencent/APIJSON/wiki
* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽)
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **社区影响力大** (GitHub 18K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前五、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
@@ -202,7 +203,7 @@ https://github.com/Tencent/APIJSON/wiki
* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,蚂蚁集团源伞 Pinpoint 代码扫描分析报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
-* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
+* **工程轻量小巧** (无第三方依赖,Jar 仅 263KB,Java 文件仅 68 个共 14864 行代码,例如 APIJSONORM 8.1.0)
* **多年持续迭代** (自 2016 年起已连续维护 8 年多,70+ 贡献者、100+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
@@ -559,7 +560,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[3步创建APIJSON后端新表及配置](https://my.oschina.net/tommylemon/blog/889074)
-[APIJSON对接分布式HTAP数据库TiDB](https://asktug.com/t/htap-tidb/395)
+[APIJSON对接分布式HTAP数据库TiDB](https://my.oschina.net/tommylemon/blog/3081913)
[APIJSON教程(一):上手apijson项目,学习apijson语法,并实现持久层配置](https://zhuanlan.zhihu.com/p/375681893)
@@ -572,13 +573,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[学习自动化接口APIJSON](https://www.jianshu.com/p/981a2a630c7b)
[APIJSON 接口调试实践](https://github.com/Tencent/APIJSON/issues/189)
-
-[关于APIJSON远程函数](https://mp.weixin.qq.com/s?__biz=Mzg3NTc1NDUyNA==&mid=2247483950&idx=1&sn=b11e70bdf083c55d72238e107449ae2e&chksm=cf3de75df84a6e4b3a4acd0846531b0bd12bc90379523fbaf6b4f900fc3cdc1b1ce3eff97fd9&scene=178&cur_album_id=2548737392338354178#rd)
-
-[APIJSON新增方法实例](https://cloud.tencent.com/developer/article/2098890)
-
-[APIJSON-APIJSON的那些事儿](https://cloud.tencent.com/developer/article/2098888)
-
+
[APIJSON-零代码接口和文档 JSON 协议 与 ORM 库](https://cloud.tencent.com/developer/article/2077042)
[APIJSON使用例子总结](https://blog.csdn.net/weixin_41077841/article/details/110518007)
@@ -589,8 +584,6 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[APIJSON复杂业务深入实践(类似12306订票系统)](https://blog.csdn.net/aa330233789/article/details/105309571)
-[全国行政区划数据抓取与处理](https://www.xlongwei.com/detail/21032616)
-
[新手搭建 APIJSON 项目指北](https://github.com/jerrylususu/apijson_todo_demo/blob/master/FULLTEXT.md)
[使用APIJSON写低代码Crud接口](https://blog.csdn.net/weixin_42375862/article/details/121654264)
@@ -635,6 +628,10 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[腾讯开源!零代码,全自动万能API接口](https://mp.weixin.qq.com/s/WWndAa68BqBfflWgL5592A)
+[APIJSON项目实战教程:零代码实现高效JSON接口开发](https://blog.csdn.net/gitblog_00682/article/details/148375065)
+
+[springboot整合APIJSON——零代码万能通用 API(附源码)](https://blog.csdn.net/longzhutengyue/article/details/150579233)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
@@ -646,10 +643,12 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-column](https://github.com/APIJSON/apijson-column) APIJSON 的字段插件,支持 字段名映射 和 !key 反选字段
-[apijson-gson](https://github.com/APIJSON/apijson-gson) APIJSON 的 gson 插件,简化使用
+[apijson-jackson](https://github.com/APIJSON/apijson-jackson) APIJSON 的 jackson 插件,简化使用
[apijson-fastjson2](https://github.com/APIJSON/apijson-fastjson2) APIJSON 的 fastjson2 插件,简化使用
+[apijson-gson](https://github.com/APIJSON/apijson-gson) APIJSON 的 gson 插件,简化使用
+
[apijson-milvus](https://github.com/APIJSON/apijson-milvus) APIJSON 的 Milvus AI 向量数据库插件
[apijson-influxdb](https://github.com/APIJSON/apijson-influxdb) APIJSON 的 InfluxDB 物联网时序数据库插件
@@ -658,13 +657,15 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-cassandra](https://github.com/APIJSON/apijson-cassandra) APIJSON 的 Cassandra NoSQL 数据库插件
-[APIAuto](https://github.com/TommyLemon/APIAuto) 敏捷开发最强大易用的接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释
+[APIAuto](https://github.com/TommyLemon/APIAuto) ☔ 敏捷开发最强大易用的接口工具,零代码测试与 AI 问答、生成代码与静态检查、生成文档与光标悬浮注释,腾讯、SHEIN、传音 等使用
+
+[CVAuto](https://github.com/TommyLemon/CVAuto) 👁 零代码零标注 CV AI 自动化测试平台 🚀 免除大量人工画框和打标签等,直接快速测试 CV 计算机视觉 AI 图像识别算法
[UnitAuto](https://github.com/TommyLemon/UnitAuto) 最先进、最省事、ROI 最高的单元测试,机器学习 零代码、全方位、自动化 测试 方法/函数,用户包含腾讯、快手、某 500 强巨头等
-[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 语句执行结果的数据库工具,一键批量生成参数组合、快速构造大量测试数据
+[SQLAuto](https://github.com/TommyLemon/SQLAuto) 智能零代码自动化测试 SQL 数据库工具,任意增删改查、任意 SQL 模板变量、一键批量生成参数组合、快速构造大量测试数据
-[UIGO](https://github.com/TommyLemon/UIGO) 📱 零代码快准稳 UI 智能录制回放平台 🚀 自动兼容任意宽高比分辨率屏幕、自动精准等待网络请求,录制回放快、准、稳!
+[UIGO](https://github.com/TommyLemon/UIGO) 📱 零代码快准稳 UI 智能录制回放平台 🚀 3 像素内自动精准定位,2 毫秒内自动精准等待,用户包含腾讯,微信团队邀请分享
[apijson-doc](https://github.com/vincentCheng/apijson-doc) APIJSON 官方文档,提供排版清晰、搜索方便的文档内容展示,包括设计规范、图文教程等
@@ -688,6 +689,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) Python 版 APIJSON,支持 MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等
+[panda-base](https://gitee.com/digithub/panda-base) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL
+
[APIJSONParser](https://github.com/Zerounary/APIJSONParser) 第三方 APIJSON 解析器,将 JSON 动态解析成 SQL
[FfApiJson](https://gitee.com/own_3_0/ff-api-json) 用 JSON 格式直接生成 SQL,借鉴 APIJSON 支持多数据源
@@ -710,7 +713,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-examples](https://gitee.com/drone/apijson-examples) APIJSON 的前端、业务后端、管理后端 Demo
-[apijson-ruoyi](https://gitee.com/yxiedd/apijson-ruoyi) APIJSON 和 RuoYi 框架整合,实现零代码生成页面模板接口,在线维护 APIJSON 数据库配置等
+[apijson-ruoyi](https://github.com/daodol/apijson-ruoyi) APIJSON 和 RuoYi 框架整合,实现零代码生成页面模板接口,在线维护 APIJSON 数据库配置等
[light4j](https://github.com/xlongwei/light4j) 整合 APIJSON 和微服务框架 light-4j 的 Demo,同时接入了 Redis
@@ -749,7 +752,9 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[review_plan](https://gitee.com/PPXcodeTry/review_plan) 复习提醒Web版(Java技术练习项目)
[apijson-nutz](https://github.com/vincent109/apijson-nutz) APIJSON + Nutz 框架 + NutzBoot 的 Demo
-
+
+[apijson-spring-boot](https://gitee.com/yunjiao-source/apijson-spring-boot) Springboot3 for APIJSON,用 YAML 简化代码配置
+
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~