Merge branch 'Tencent:master' into master
This commit is contained in:
commit
98ec0931f3
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.github.Tencent</groupId>
|
||||
<artifactId>APIJSON</artifactId>
|
||||
<version>6.1.0</version>
|
||||
<version>6.2.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>APIJSONORM</name>
|
||||
@ -36,6 +36,20 @@
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -6,6 +6,7 @@ This source code is licensed under the Apache License Version 2.0.*/
|
||||
package apijson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -151,7 +152,16 @@ public class JSONObject extends com.alibaba.fastjson.JSONObject {
|
||||
public static final String KEY_ORDER = "@order"; //排序方式
|
||||
public static final String KEY_RAW = "@raw"; // 自定义原始 SQL 片段
|
||||
public static final String KEY_JSON = "@json"; //SQL Server 把字段转为 JSON 输出
|
||||
public static final String KEY_METHOD = "@method"; //json对象配置操作方法
|
||||
public static final String KEY_METHOD = "@method"; // json 对象配置操作方法
|
||||
public static final String KEY_GET = "@get"; // json 对象配置操作方法
|
||||
public static final String KEY_GETS = "@gets"; // json 对象配置操作方法
|
||||
public static final String KEY_HEAD = "@head"; // json 对象配置操作方法
|
||||
public static final String KEY_HEADS = "@heads"; // json 对象配置操作方法
|
||||
public static final String KEY_POST = "@post"; // json 对象配置操作方法
|
||||
public static final String KEY_PUT = "@put"; // json 对象配置操作方法
|
||||
public static final String KEY_DELETE = "@delete"; // json 对象配置操作方法
|
||||
|
||||
public static final Map<String, RequestMethod> KEY_METHOD_ENUM_MAP;
|
||||
|
||||
public static final List<String> TABLE_KEY_LIST;
|
||||
static {
|
||||
@ -174,6 +184,22 @@ public class JSONObject extends com.alibaba.fastjson.JSONObject {
|
||||
TABLE_KEY_LIST.add(KEY_RAW);
|
||||
TABLE_KEY_LIST.add(KEY_JSON);
|
||||
TABLE_KEY_LIST.add(KEY_METHOD);
|
||||
TABLE_KEY_LIST.add(KEY_GET);
|
||||
TABLE_KEY_LIST.add(KEY_GETS);
|
||||
TABLE_KEY_LIST.add(KEY_HEAD);
|
||||
TABLE_KEY_LIST.add(KEY_HEADS);
|
||||
TABLE_KEY_LIST.add(KEY_POST);
|
||||
TABLE_KEY_LIST.add(KEY_PUT);
|
||||
TABLE_KEY_LIST.add(KEY_DELETE);
|
||||
|
||||
KEY_METHOD_ENUM_MAP = new LinkedHashMap<>();
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_GET, RequestMethod.GET);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_GETS, RequestMethod.GETS);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_HEAD, RequestMethod.HEAD);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_HEADS, RequestMethod.HEADS);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_POST, RequestMethod.POST);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_PUT, RequestMethod.PUT);
|
||||
KEY_METHOD_ENUM_MAP.put(KEY_DELETE, RequestMethod.DELETE);
|
||||
}
|
||||
|
||||
//@key关键字都放这个类 >>>>>>>>>>>>>>>>>>>>>>
|
||||
@ -395,18 +421,6 @@ public class JSONObject extends com.alibaba.fastjson.JSONObject {
|
||||
return puts(KEY_JSON, keys);
|
||||
}
|
||||
|
||||
/**用 setJson 替代。
|
||||
* set keys to cast to json
|
||||
* @param keys "key0,key1,key2..."
|
||||
* @return
|
||||
* @see #{@link #setJson(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public JSONObject setJSON(String keys) {
|
||||
return puts(KEY_JSON, keys);
|
||||
}
|
||||
|
||||
|
||||
//JSONObject内关键词 key >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
@ -9,6 +9,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static apijson.StringUtil.PATTERN_ALPHA_BIG;
|
||||
|
||||
/**wrapper for request
|
||||
* @author Lemon
|
||||
* @see #puts
|
||||
@ -196,4 +198,69 @@ public class JSONRequest extends JSONObject {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**ABCdEfg => upper ? A-B-CD-EFG : a-b-cd-efg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String recoverHyphen(@NotNull String key, Boolean upper) {
|
||||
return recoverDivider(key, "-", upper);
|
||||
}
|
||||
|
||||
/**ABCdEfg => upper ? A_B_CD_EFG : a_b_cd_efg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String recoverUnderline(@NotNull String key, Boolean upper) {
|
||||
return recoverDivider(key, "_", upper);
|
||||
}
|
||||
|
||||
/**ABCdEfg => upper ? A$B$CD$EFG : a$b$cd$efg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String recoverDollar(@NotNull String key, Boolean upper) {
|
||||
return recoverDivider(key, "$", upper);
|
||||
}
|
||||
|
||||
/**ABCdEfg => upper ? A.B.CD.EFG : a.b.cd.efg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String recoverDot(@NotNull String key, Boolean upper) {
|
||||
return recoverDivider(key, ".", upper);
|
||||
}
|
||||
|
||||
/**ABCdEfg => upper ? A_B_CD_EFG : a/b/cd/efg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String recoverDivider(@NotNull String key, Boolean upper) {
|
||||
return recoverDivider(key, "/", upper);
|
||||
}
|
||||
|
||||
/**驼峰格式转为带分隔符的全大写或全小写格式
|
||||
* @param key
|
||||
* @param divider
|
||||
* @param upper
|
||||
* @return
|
||||
*/
|
||||
public static String recoverDivider(@NotNull String key, @NotNull String divider, Boolean upper) {
|
||||
StringBuilder name = new StringBuilder();
|
||||
char[] cs = key.toCharArray();
|
||||
int len = key.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
String s = key.substring(i, i + 1);
|
||||
if (i > 0 && PATTERN_ALPHA_BIG.matcher(s).matches()) {
|
||||
name.append(divider);
|
||||
}
|
||||
if (upper != null) {
|
||||
s = upper ? s.toUpperCase() : s.toLowerCase();
|
||||
}
|
||||
name.append(s);
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,13 +5,12 @@ This source code is licensed under the Apache License Version 2.0.*/
|
||||
|
||||
package apijson;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**parser for response
|
||||
* @author Lemon
|
||||
* @see #getObject
|
||||
@ -23,6 +22,17 @@ import com.alibaba.fastjson.JSONObject;
|
||||
public class JSONResponse extends apijson.JSONObject {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// 节约性能和减少 bug,除了关键词 @key ,一般都符合变量命名规范,不符合也原样返回便于调试
|
||||
/**格式化带 - 中横线的单词
|
||||
*/
|
||||
public static boolean IS_FORMAT_HYPHEN = false;
|
||||
/**格式化带 _ 下划线的单词
|
||||
*/
|
||||
public static boolean IS_FORMAT_UNDERLINE = false;
|
||||
/**格式化带 $ 美元符的单词
|
||||
*/
|
||||
public static boolean IS_FORMAT_DOLLAR = false;
|
||||
|
||||
private static final String TAG = "JSONResponse";
|
||||
|
||||
public JSONResponse() {
|
||||
@ -206,10 +216,6 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
//状态信息,非GET请求获得的信息>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* key = clazz.getSimpleName()
|
||||
* @param clazz
|
||||
@ -408,18 +414,18 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
|
||||
/**获取变量名
|
||||
* @param fullName
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean)} formatColon = true, formatAt = true, formatHyphen = true, firstCase = true
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, boolean)} formatColon = true, formatAt = true, formatHyphen = true, firstCase = true
|
||||
*/
|
||||
public static String getVariableName(String fullName) {
|
||||
if (isArrayKey(fullName)) {
|
||||
fullName = StringUtil.addSuffix(fullName.substring(0, fullName.length() - 2), "list");
|
||||
}
|
||||
return formatKey(fullName, true, true, true, true);
|
||||
return formatKey(fullName, true, true, true, true, false, true);
|
||||
}
|
||||
|
||||
/**格式化数组的名称 key[] => keyList; key:alias[] => aliasList; Table-column[] => tableColumnList
|
||||
* @param key empty ? "list" : key + "List" 且首字母小写
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = true, firstCase = true
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = true, firstCase = true
|
||||
*/
|
||||
public static String formatArrayKey(String key) {
|
||||
if (isArrayKey(key)) {
|
||||
@ -430,28 +436,29 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
return key.substring(index + 1); //不处理自定义的
|
||||
}
|
||||
|
||||
return formatKey(key, false, true, true, true); //节约性能,除了数组对象 Table-column:alias[] ,一般都符合变量命名规范
|
||||
return formatKey(key, false, true, true, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR, false); //节约性能,除了数组对象 Table-column:alias[] ,一般都符合变量命名规范
|
||||
}
|
||||
|
||||
/**格式化对象的名称 name => name; name:alias => alias
|
||||
* @param key name 或 name:alias
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = true
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = true
|
||||
*/
|
||||
public static String formatObjectKey(String key) {
|
||||
int index = key == null ? -1 : key.indexOf(":");
|
||||
if (index >= 0) {
|
||||
return key.substring(index + 1); //不处理自定义的
|
||||
return key.substring(index + 1); // 不处理自定义的
|
||||
}
|
||||
|
||||
return formatKey(key, false, true, false, true); //节约性能,除了表对象 Table:alias ,一般都符合变量命名规范
|
||||
return formatKey(key, false, true, IS_FORMAT_HYPHEN, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR, false); //节约性能,除了表对象 Table:alias ,一般都符合变量命名规范
|
||||
}
|
||||
|
||||
/**格式化普通值的名称 name => name; name:alias => alias
|
||||
* @param fullName name 或 name:alias
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = false
|
||||
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = false
|
||||
*/
|
||||
public static String formatOtherKey(String fullName) {
|
||||
return formatKey(fullName, false, true, false, false); //节约性能,除了关键词 @key ,一般都符合变量命名规范,不符合也原样返回便于调试
|
||||
return formatKey(fullName, false, true, IS_FORMAT_HYPHEN, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR
|
||||
, IS_FORMAT_HYPHEN || IS_FORMAT_UNDERLINE || IS_FORMAT_DOLLAR ? false : null);
|
||||
}
|
||||
|
||||
|
||||
@ -460,10 +467,13 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
* @param formatAt 去除前缀 @ , @a => a
|
||||
* @param formatColon 去除分隔符 : , A:b => b
|
||||
* @param formatHyphen 去除分隔符 - , A-b-cd-Efg => aBCdEfg
|
||||
* @param formatUnderline 去除分隔符 _ , A_b_cd_Efg => aBCdEfg
|
||||
* @param formatDollar 去除分隔符 $ , A$b$cd$Efg => aBCdEfg
|
||||
* @param firstCase 第一个单词首字母小写,后面的首字母大写, Ab => ab ; A-b-Cd => aBCd
|
||||
* @return name => name; name:alias => alias
|
||||
*/
|
||||
public static String formatKey(String fullName, boolean formatColon, boolean formatAt, boolean formatHyphen, boolean firstCase) {
|
||||
public static String formatKey(String fullName, boolean formatColon, boolean formatAt, boolean formatHyphen
|
||||
, boolean formatUnderline, boolean formatDollar, Boolean firstCase) {
|
||||
if (fullName == null) {
|
||||
Log.w(TAG, "formatKey fullName == null >> return null;");
|
||||
return null;
|
||||
@ -476,10 +486,17 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
fullName = formatAt(fullName);
|
||||
}
|
||||
if (formatHyphen) {
|
||||
fullName = formatHyphen(fullName, firstCase);
|
||||
fullName = formatHyphen(fullName, firstCase != null);
|
||||
}
|
||||
if (formatUnderline) {
|
||||
fullName = formatUnderline(fullName, firstCase != null);
|
||||
}
|
||||
if (formatDollar) {
|
||||
fullName = formatDollar(fullName, firstCase != null);
|
||||
}
|
||||
|
||||
return firstCase ? StringUtil.firstCase(fullName) : fullName; //不格式化普通 key:value (value 不为 [], {}) 的 key
|
||||
// 默认不格式化普通 key:value (value 不为 [], {}) 的 key
|
||||
return firstCase == null ? fullName : StringUtil.firstCase(fullName, firstCase);
|
||||
}
|
||||
|
||||
/**"@key" => "key"
|
||||
@ -489,6 +506,7 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
public static String formatAt(@NotNull String key) {
|
||||
return key.startsWith("@") ? key.substring(1) : key;
|
||||
}
|
||||
|
||||
/**key:alias => alias
|
||||
* @param key
|
||||
* @return
|
||||
@ -502,15 +520,60 @@ public class JSONResponse extends apijson.JSONObject {
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String formatHyphen(@NotNull String key, boolean firstCase) {
|
||||
String name = "";
|
||||
|
||||
StringTokenizer parts = new StringTokenizer(key, "-");
|
||||
name += parts.nextToken();
|
||||
while(parts.hasMoreTokens()) {
|
||||
String part = parts.nextToken();
|
||||
name += firstCase ? StringUtil.firstCase(part, true) : part;
|
||||
}
|
||||
return name;
|
||||
public static String formatHyphen(@NotNull String key, Boolean firstCase) {
|
||||
return formatDivider(key, "-", firstCase);
|
||||
}
|
||||
|
||||
/**A_b_cd_Efg => ABCdEfg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String formatUnderline(@NotNull String key, Boolean firstCase) {
|
||||
return formatDivider(key, "_", firstCase);
|
||||
}
|
||||
|
||||
/**A$b$cd$Efg => ABCdEfg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String formatDollar(@NotNull String key, Boolean firstCase) {
|
||||
return formatDivider(key, "$", firstCase);
|
||||
}
|
||||
|
||||
/**A.b.cd.Efg => ABCdEfg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String formatDot(@NotNull String key, Boolean firstCase) {
|
||||
return formatDivider(key, ".", firstCase);
|
||||
}
|
||||
|
||||
/**A/b/cd/Efg => ABCdEfg
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String formatDivider(@NotNull String key, Boolean firstCase) {
|
||||
return formatDivider(key, "/", firstCase);
|
||||
}
|
||||
|
||||
/**去除分割符,返回驼峰格式
|
||||
* @param key
|
||||
* @param divider
|
||||
* @param firstCase
|
||||
* @return
|
||||
*/
|
||||
public static String formatDivider(@NotNull String key, @NotNull String divider, Boolean firstCase) {
|
||||
String[] parts = StringUtil.split(key, divider);
|
||||
StringBuilder name = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
part = part.toLowerCase(); // 始终小写,也方便反过来 ABCdEfg -> A_b_cd_Efg
|
||||
if (firstCase != null) {
|
||||
// 始终小写, A_b_cd_Efg -> ABCdEfg, firstCase ? part.toLowerCase() : part.toUpperCase();
|
||||
part = StringUtil.firstCase(part, firstCase);
|
||||
}
|
||||
name.append(part);
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ This source code is licensed under the Apache License Version 2.0.*/
|
||||
|
||||
package apijson;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**请求方法,对应org.springframework.web.bind.annotation.RequestMethod,多出GETS,HEADS方法
|
||||
* @author Lemon
|
||||
*/
|
||||
@ -40,17 +43,20 @@ public enum RequestMethod {
|
||||
*/
|
||||
PUT,
|
||||
|
||||
/**
|
||||
* json包含多条语句,支持增删改查,函数调用
|
||||
*/
|
||||
CRUD,
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
*/
|
||||
DELETE;
|
||||
DELETE,
|
||||
|
||||
public static final RequestMethod[] ALL = new RequestMethod[]{ GET, HEAD, GETS, HEADS, POST, PUT, CRUD, DELETE};
|
||||
/**
|
||||
* json 包含多条语句,支持增删改查、函数调用
|
||||
*/
|
||||
CRUD;
|
||||
|
||||
public static final RequestMethod[] ALL = new RequestMethod[]{ GET, HEAD, GETS, HEADS, POST, PUT, DELETE, CRUD };
|
||||
public static final List<String> ALL_NAME_LIST = Arrays.asList(
|
||||
GET.name(), HEAD.name(), GETS.name(), HEADS.name(), POST.name(), PUT.name(), DELETE.name(), CRUD.name()
|
||||
);
|
||||
|
||||
/**是否为GET请求方法
|
||||
* @param method
|
||||
|
@ -5,12 +5,10 @@ This source code is licensed under the Apache License Version 2.0.*/
|
||||
|
||||
package apijson.orm;
|
||||
|
||||
import apijson.Log;
|
||||
import apijson.NotNull;
|
||||
import apijson.RequestMethod;
|
||||
import apijson.StringUtil;
|
||||
import apijson.*;
|
||||
import apijson.orm.exception.UnsupportedDataTypeException;
|
||||
import apijson.orm.script.ScriptExecutor;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.parser.ParserConfig;
|
||||
import com.alibaba.fastjson.util.TypeUtils;
|
||||
@ -18,6 +16,7 @@ import com.alibaba.fastjson.util.TypeUtils;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import static apijson.orm.AbstractSQLConfig.PATTERN_SCHEMA;
|
||||
@ -26,7 +25,7 @@ import static apijson.orm.SQLConfig.TYPE_ITEM;
|
||||
/**可远程调用的函数类
|
||||
* @author Lemon
|
||||
*/
|
||||
public class AbstractFunctionParser implements FunctionParser {
|
||||
public class AbstractFunctionParser<T extends Object> implements FunctionParser<T> {
|
||||
private static final String TAG = "AbstractFunctionParser";
|
||||
|
||||
/**是否解析参数 key 的对应的值,不用手动编码 curObj.getString(key)
|
||||
@ -44,6 +43,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
// <isContain, <arguments:"array,key", tag:null, methods:null>>
|
||||
public static Map<String, ScriptExecutor> SCRIPT_EXECUTOR_MAP;
|
||||
public static Map<String, JSONObject> FUNCTION_MAP;
|
||||
|
||||
static {
|
||||
FUNCTION_MAP = new HashMap<>();
|
||||
SCRIPT_EXECUTOR_MAP = new HashMap<>();
|
||||
@ -53,9 +53,11 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
private String tag;
|
||||
private int version;
|
||||
private JSONObject request;
|
||||
|
||||
public AbstractFunctionParser() {
|
||||
this(null, null, 0, null);
|
||||
}
|
||||
|
||||
public AbstractFunctionParser(RequestMethod method, String tag, int version, @NotNull JSONObject request) {
|
||||
setMethod(method == null ? RequestMethod.GET : method);
|
||||
setTag(tag);
|
||||
@ -63,13 +65,15 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
setRequest(request);
|
||||
}
|
||||
|
||||
private Parser<?> parser;
|
||||
private Parser<T> parser;
|
||||
|
||||
@Override
|
||||
public Parser<?> getParser() {
|
||||
public Parser<T> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setParser(Parser<?> parser) {
|
||||
public AbstractFunctionParser<T> setParser(Parser<T> parser) {
|
||||
this.parser = parser;
|
||||
return this;
|
||||
}
|
||||
@ -78,58 +82,70 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
public RequestMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setMethod(RequestMethod method) {
|
||||
public AbstractFunctionParser<T> setMethod(RequestMethod method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setTag(String tag) {
|
||||
public AbstractFunctionParser<T> setTag(String tag) {
|
||||
this.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setVersion(int version) {
|
||||
public AbstractFunctionParser<T> setVersion(int version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String key;
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setKey(String key) {
|
||||
public AbstractFunctionParser<T> setKey(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String parentPath;
|
||||
|
||||
@Override
|
||||
public String getParentPath() {
|
||||
return parentPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setParentPath(String parentPath) {
|
||||
public AbstractFunctionParser<T> setParentPath(String parentPath) {
|
||||
this.parentPath = parentPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String currentName;
|
||||
|
||||
@Override
|
||||
public String getCurrentName() {
|
||||
return currentName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setCurrentName(String currentName) {
|
||||
public AbstractFunctionParser<T> setCurrentName(String currentName) {
|
||||
this.currentName = currentName;
|
||||
return this;
|
||||
}
|
||||
@ -139,24 +155,125 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
public JSONObject getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setRequest(@NotNull JSONObject request) {
|
||||
public AbstractFunctionParser<T> setRequest(@NotNull JSONObject request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
}
|
||||
|
||||
private JSONObject currentObject;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JSONObject getCurrentObject() {
|
||||
return currentObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractFunctionParser setCurrentObject(@NotNull JSONObject currentObject) {
|
||||
public AbstractFunctionParser<T> setCurrentObject(@NotNull JSONObject currentObject) {
|
||||
this.currentObject = currentObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**根据路径取 Boolean 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Boolean getArgBool(String path) {
|
||||
return getArgVal(path, Boolean.class);
|
||||
}
|
||||
|
||||
/**根据路径取 Integer 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Integer getArgInt(String path) {
|
||||
return getArgVal(path, Integer.class);
|
||||
}
|
||||
|
||||
/**根据路径取 Long 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Long getArgLong(String path) {
|
||||
return getArgVal(path, Long.class);
|
||||
}
|
||||
|
||||
/**根据路径取 Float 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Float getArgFloat(String path) {
|
||||
return getArgVal(path, Float.class);
|
||||
}
|
||||
|
||||
/**根据路径取 Double 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Double getArgDouble(String path) {
|
||||
return getArgVal(path, Double.class);
|
||||
}
|
||||
|
||||
/**根据路径取 Number 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public Number getArgNum(String path) {
|
||||
return getArgVal(path, Number.class);
|
||||
}
|
||||
|
||||
/**根据路径取 BigDecimal 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal getArgDecimal(String path) {
|
||||
return getArgVal(path, BigDecimal.class);
|
||||
}
|
||||
|
||||
/**根据路径取 String 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public String getArgStr(String path) {
|
||||
Object obj = getArgVal(path);
|
||||
return JSON.toJSONString(obj);
|
||||
}
|
||||
|
||||
/**根据路径取 JSONObject 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public JSONObject getArgObj(String path) {
|
||||
return getArgVal(path, JSONObject.class);
|
||||
}
|
||||
|
||||
/**根据路径取 JSONArray 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public JSONArray getArgArr(String path) {
|
||||
return getArgVal(path, JSONArray.class);
|
||||
}
|
||||
|
||||
/**根据路径取 List<T> 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public <T extends Object> List<T> getArgList(String path) {
|
||||
return getArgList(path, null);
|
||||
}
|
||||
|
||||
/**根据路径取 List<T> 值
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public <T extends Object> List<T> getArgList(String path, Class<T> clazz) {
|
||||
String s = getArgStr(path);
|
||||
return JSON.parseArray(s, clazz);
|
||||
}
|
||||
|
||||
/**根据路径取值
|
||||
* @param path
|
||||
* @return
|
||||
@ -177,7 +294,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
/**根据路径取值
|
||||
* @param path
|
||||
* @param clazz
|
||||
* @param tryAll false-仅当前对象,true-本次请求的全局对象以及 Parser 缓存值
|
||||
* @param tryAll false-仅当前对象,true-本次请求的全局对象以及 Parser<T> 缓存值
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
@ -232,7 +349,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
* @param currentObject
|
||||
* @return {@link #invoke(AbstractFunctionParser, String, Class[], Object[])}
|
||||
*/
|
||||
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String function, @NotNull JSONObject currentObject, boolean containRaw) throws Exception {
|
||||
public static <T extends Object> Object invoke(@NotNull AbstractFunctionParser<T> parser, @NotNull String function, @NotNull JSONObject currentObject, boolean containRaw) throws Exception {
|
||||
if (ENABLE_REMOTE_FUNCTION == false) {
|
||||
throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_REMOTE_FUNCTION" +
|
||||
" == false 时不支持远程函数!如需支持则设置 AbstractFunctionParser.ENABLE_REMOTE_FUNCTION = true !");
|
||||
@ -254,7 +371,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
}
|
||||
|
||||
if (lang != null && SCRIPT_EXECUTOR_MAP.get(lang) == null) {
|
||||
throw new ClassNotFoundException("找不到脚本语言 " + lang + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!");
|
||||
throw new ClassNotFoundException("找不到脚本语言 " + lang + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser<T> 中注册!");
|
||||
}
|
||||
|
||||
int version = row.getIntValue("version");
|
||||
@ -305,7 +422,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
* @return {@link #invoke(AbstractFunctionParser, String, Class[], Object[], String, JSONObject, ScriptExecutor)}
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName
|
||||
public static <T extends Object> Object invoke(@NotNull AbstractFunctionParser<T> parser, @NotNull String methodName
|
||||
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args) throws Exception {
|
||||
return invoke(parser, methodName, parameterTypes, args, null, null, null);
|
||||
}
|
||||
@ -320,7 +437,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName
|
||||
public static <T extends Object> Object invoke(@NotNull AbstractFunctionParser<T> parser, @NotNull String methodName
|
||||
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, String returnType
|
||||
, JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception {
|
||||
if (scriptExecutor != null) {
|
||||
@ -357,7 +474,7 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object invokeScript(@NotNull AbstractFunctionParser parser, @NotNull String methodName
|
||||
public static <T extends Object> Object invokeScript(@NotNull AbstractFunctionParser<T> parser, @NotNull String methodName
|
||||
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, String returnType, JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception {
|
||||
Object result = scriptExecutor.execute(parser, currentObject, methodName, args);
|
||||
if (Log.DEBUG && result != null) {
|
||||
@ -511,6 +628,10 @@ public class AbstractFunctionParser implements FunctionParser {
|
||||
}
|
||||
|
||||
public static String extractSchema(String sch, String table) {
|
||||
if (StringUtil.isEmpty(sch)) {
|
||||
return sch;
|
||||
}
|
||||
|
||||
if (table == null) {
|
||||
table = "Table";
|
||||
}
|
||||
|
@ -35,20 +35,24 @@ import static apijson.RequestMethod.GET;
|
||||
/**简化Parser,getObject和getArray(getArrayConfig)都能用
|
||||
* @author Lemon
|
||||
*/
|
||||
public abstract class AbstractObjectParser implements ObjectParser {
|
||||
public abstract class AbstractObjectParser<T extends Object> implements ObjectParser<T> {
|
||||
private static final String TAG = "AbstractObjectParser";
|
||||
|
||||
@NotNull
|
||||
protected AbstractParser<?> parser;
|
||||
public AbstractObjectParser setParser(AbstractParser<?> parser) {
|
||||
this.parser = parser;
|
||||
protected AbstractParser<T> parser;
|
||||
@Override
|
||||
public AbstractParser<T> getParser() {
|
||||
return parser;
|
||||
}
|
||||
@Override
|
||||
public AbstractObjectParser<T> setParser(Parser<T> parser) {
|
||||
this.parser = (AbstractParser<T>) parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
protected JSONObject request;//不用final是为了recycle
|
||||
protected String parentPath;//不用final是为了recycle
|
||||
protected SQLConfig arrayConfig;//不用final是为了recycle
|
||||
protected SQLConfig<T> arrayConfig;//不用final是为了recycle
|
||||
protected boolean isSubquery;
|
||||
|
||||
protected final int type;
|
||||
@ -423,7 +427,21 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
|
||||
if (target == null) { // String#equals(null)会出错
|
||||
Log.d(TAG, "onParse target == null >> return true;");
|
||||
return true;
|
||||
|
||||
if (Log.DEBUG) {
|
||||
parser.putWarnIfNeed(AbstractParser.KEY_REF, path + "/" + key + ": " + targetPath + " 引用赋值获取路径对应的值为 null!请检查路径是否错误!");
|
||||
}
|
||||
|
||||
// 非查询关键词 @key 不影响查询,直接跳过
|
||||
if (isTable && (key.startsWith("@") == false || JSONRequest.TABLE_KEY_LIST.contains(key))) {
|
||||
Log.e(TAG, "onParse isTable && (key.startsWith(@) == false"
|
||||
+ " || JSONRequest.TABLE_KEY_LIST.contains(key)) >> return null;");
|
||||
return false; // 获取不到就不用再做无效的 query 了。不考虑 Table:{Table:{}} 嵌套
|
||||
}
|
||||
|
||||
|
||||
Log.d(TAG, "onParse isTable(table) == false >> return true;");
|
||||
return true; // 舍去,对Table无影响
|
||||
}
|
||||
|
||||
// if (target instanceof Map) { // target 可能是从 requestObject 里取出的 {}
|
||||
@ -815,22 +833,49 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
@Override
|
||||
public JSONObject parseResponse(RequestMethod method, String table, String alias
|
||||
, JSONObject request, List<Join> joinList, boolean isProcedure) throws Exception {
|
||||
SQLConfig config = newSQLConfig(method, table, alias, request, joinList, isProcedure)
|
||||
SQLConfig<T> config = newSQLConfig(method, table, alias, request, joinList, isProcedure)
|
||||
.setParser(parser)
|
||||
.setObjectParser(this);
|
||||
return parseResponse(config, isProcedure);
|
||||
}
|
||||
@Override
|
||||
public JSONObject parseResponse(SQLConfig config, boolean isProcedure) throws Exception {
|
||||
public JSONObject parseResponse(SQLConfig<T> config, boolean isProcedure) throws Exception {
|
||||
if (parser.getSQLExecutor() == null) {
|
||||
parser.createSQLExecutor();
|
||||
}
|
||||
if (parser != null && config.getParser() == null) {
|
||||
config.setParser(parser);
|
||||
}
|
||||
return parser.getSQLExecutor().execute(config, isProcedure);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SQLConfig newSQLConfig(boolean isProcedure) throws Exception {
|
||||
String raw = Log.DEBUG == false || sqlRequest == null ? null : sqlRequest.getString(apijson.JSONRequest.KEY_RAW);
|
||||
String[] keys = raw == null ? null : StringUtil.split(raw);
|
||||
if (keys != null && keys.length > 0) {
|
||||
boolean allow = AbstractSQLConfig.ALLOW_MISSING_KEY_4_COMBINE;
|
||||
|
||||
for (String key : keys) {
|
||||
if (sqlRequest.get(key) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String msg = "@raw:value 的 value 中 " + key + " 不合法!对应的 "
|
||||
+ key + ": value 在当前对象 " + name + " 不存在或 value = null,无法有效转为原始 SQL 片段!";
|
||||
|
||||
if (allow == false) {
|
||||
throw new UnsupportedOperationException(msg);
|
||||
}
|
||||
|
||||
if (parser instanceof AbstractParser) {
|
||||
((AbstractParser) parser).putWarnIfNeed(JSONRequest.KEY_RAW, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newSQLConfig(method, table, alias, sqlRequest, joinList, isProcedure)
|
||||
.setParser(parser)
|
||||
.setObjectParser(this);
|
||||
@ -1177,13 +1222,13 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
return alias;
|
||||
}
|
||||
@Override
|
||||
public SQLConfig getArrayConfig() {
|
||||
public SQLConfig<T> getArrayConfig() {
|
||||
return arrayConfig;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SQLConfig getSQLConfig() {
|
||||
public SQLConfig<T> getSQLConfig() {
|
||||
return sqlConfig;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ This source code is licensed under the Apache License Version 2.0.*/
|
||||
|
||||
package apijson.orm;
|
||||
|
||||
import apijson.orm.exception.ConflictException;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
@ -38,7 +39,7 @@ import static apijson.JSONObject.KEY_EXPLAIN;
|
||||
import static apijson.RequestMethod.CRUD;
|
||||
import static apijson.RequestMethod.GET;
|
||||
|
||||
/**parser for parsing request to JSONObject
|
||||
/**Parser<T> for parsing request to JSONObject
|
||||
* @author Lemon
|
||||
*/
|
||||
public abstract class AbstractParser<T extends Object> implements Parser<T>, ParserCreator<T>, VerifierCreator<T>, SQLCreator {
|
||||
@ -120,10 +121,10 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
* @param method null ? requestMethod = GET
|
||||
*/
|
||||
public AbstractParser(RequestMethod method) {
|
||||
super();
|
||||
setMethod(method);
|
||||
setNeedVerifyRole(AbstractVerifier.ENABLE_VERIFY_ROLE);
|
||||
setNeedVerifyContent(AbstractVerifier.ENABLE_VERIFY_CONTENT);
|
||||
super();
|
||||
setMethod(method);
|
||||
setNeedVerifyRole(AbstractVerifier.ENABLE_VERIFY_ROLE);
|
||||
setNeedVerifyContent(AbstractVerifier.ENABLE_VERIFY_CONTENT);
|
||||
}
|
||||
/**
|
||||
* @param method null ? requestMethod = GET
|
||||
@ -144,6 +145,57 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
return this;
|
||||
}
|
||||
|
||||
public static final String KEY_REF = "Reference";
|
||||
|
||||
/**警告信息
|
||||
* Map<"Reference", "引用赋值获取路径 /Comment/userId 对应的值为 null!">
|
||||
*/
|
||||
protected Map<String, String> warnMap = new LinkedHashMap<>();
|
||||
public String getWarn(String type) {
|
||||
return warnMap == null ? null : warnMap.get(type);
|
||||
}
|
||||
public AbstractParser<T> putWarnIfNeed(String type, String warn) {
|
||||
if (Log.DEBUG) {
|
||||
String w = getWarn(type);
|
||||
if (StringUtil.isEmpty(w, true)) {
|
||||
putWarn(type, warn);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public AbstractParser<T> putWarn(String type, String warn) {
|
||||
if (warnMap == null) {
|
||||
warnMap = new LinkedHashMap<>();
|
||||
}
|
||||
warnMap.put(type, warn);
|
||||
return this;
|
||||
}
|
||||
/**获取警告信息
|
||||
* @return
|
||||
*/
|
||||
public String getWarnString() {
|
||||
Set<Entry<String, String>> set = warnMap == null ? null : warnMap.entrySet();
|
||||
if (set == null || set.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Entry<String, String> e : set) {
|
||||
String k = e == null ? null : e.getKey();
|
||||
String v = k == null ? null : e.getValue();
|
||||
if (StringUtil.isEmpty(v, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StringUtil.isNotEmpty(k, true)) {
|
||||
sb.append("[" + k + "]: ");
|
||||
}
|
||||
sb.append(v + "; ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
protected Visitor<T> visitor;
|
||||
@ -333,9 +385,6 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected SQLExecutor sqlExecutor;
|
||||
protected Verifier<T> verifier;
|
||||
protected Map<String, Object> queryResultMap;//path-result
|
||||
@ -410,11 +459,12 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
requestObject = request;
|
||||
try {
|
||||
setVersion(requestObject.getIntValue(JSONRequest.KEY_VERSION));
|
||||
requestObject.remove(JSONRequest.KEY_VERSION);
|
||||
|
||||
if (getMethod() != RequestMethod.CRUD) {
|
||||
setTag(requestObject.getString(JSONRequest.KEY_TAG));
|
||||
requestObject.remove(JSONRequest.KEY_TAG);
|
||||
}
|
||||
requestObject.remove(JSONRequest.KEY_VERSION);
|
||||
} catch (Exception e) {
|
||||
return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
|
||||
}
|
||||
@ -485,7 +535,9 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
onRollback();
|
||||
}
|
||||
|
||||
requestObject = error == null ? extendSuccessResult(requestObject, isRoot) : extendErrorResult(requestObject, error, requestMethod, getRequestURL(), isRoot);
|
||||
String warn = Log.DEBUG == false || error != null ? null : getWarnString();
|
||||
|
||||
requestObject = error == null ? extendSuccessResult(requestObject, warn, isRoot) : extendErrorResult(requestObject, error, requestMethod, getRequestURL(), isRoot);
|
||||
|
||||
JSONObject res = (globalFormat != null && globalFormat) && JSONResponse.isSuccess(requestObject) ? new JSONResponse(requestObject) : requestObject;
|
||||
|
||||
@ -543,7 +595,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void onVerifyRole(@NotNull SQLConfig config) throws Exception {
|
||||
public void onVerifyRole(@NotNull SQLConfig<T> config) throws Exception {
|
||||
if (Log.DEBUG) {
|
||||
Log.i(TAG, "onVerifyRole config = " + JSON.toJSONString(config));
|
||||
}
|
||||
@ -661,31 +713,49 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newResult(int code, String msg) {
|
||||
return newResult(code, msg, false);
|
||||
return newResult(code, msg, null);
|
||||
}
|
||||
/**新建带状态内容的JSONObject
|
||||
|
||||
/**
|
||||
* 添加JSONObject的状态内容,一般用于错误提示结果
|
||||
*
|
||||
* @param code
|
||||
* @param msg
|
||||
* @param warn
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newResult(int code, String msg, String warn) {
|
||||
return newResult(code, msg, warn, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建带状态内容的JSONObject
|
||||
*
|
||||
* @param code
|
||||
* @param msg
|
||||
* @param warn
|
||||
* @param isRoot
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newResult(int code, String msg, boolean isRoot) {
|
||||
return extendResult(null, code, msg, isRoot);
|
||||
public static JSONObject newResult(int code, String msg, String warn, boolean isRoot) {
|
||||
return extendResult(null, code, msg, warn, isRoot);
|
||||
}
|
||||
|
||||
/**添加JSONObject的状态内容,一般用于错误提示结果
|
||||
/**
|
||||
* 添加JSONObject的状态内容,一般用于错误提示结果
|
||||
*
|
||||
* @param object
|
||||
* @param code
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject extendResult(JSONObject object, int code, String msg, boolean isRoot) {
|
||||
public static JSONObject extendResult(JSONObject object, int code, String msg, String warn, 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提 bug 请发请求和响应的【完整截屏】,没图的自行解决!"
|
||||
+ " \n开发者有限的时间和精力主要放在【维护项目源码和文档】上!"
|
||||
+ " \n【描述不详细】 或 【文档/常见问题 已有答案】 的问题可能会被忽略!!"
|
||||
+ " \n【态度 不文明/不友善】的可能会被踢出群,问题也可能不予解答!!!"
|
||||
+ " \n开发者有限的时间和精力主要放在【维护项目源码和文档】上!"
|
||||
+ " \n【描述不详细】 或 【文档/常见问题 已有答案】 的问题可能会被忽略!!"
|
||||
+ " \n【态度 不文明/不友善】的可能会被踢出群,问题也可能不予解答!!!"
|
||||
+ " \n\n **环境信息** "
|
||||
+ " \n系统: " + Log.OS_NAME + " " + Log.OS_VERSION
|
||||
+ " \n数据库: DEFAULT_DATABASE = " + AbstractSQLConfig.DEFAULT_DATABASE
|
||||
@ -715,6 +785,9 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
|
||||
object.put(JSONResponse.KEY_MSG, msg);
|
||||
if (debug != null) {
|
||||
if (StringUtil.isNotEmpty(warn, true)) {
|
||||
debug += "\n 【警告】:" + warn;
|
||||
}
|
||||
object.put("debug:info|help", debug);
|
||||
}
|
||||
|
||||
@ -722,33 +795,51 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
|
||||
/**添加请求成功的状态内容
|
||||
/**
|
||||
* 添加请求成功的状态内容
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject extendSuccessResult(JSONObject object) {
|
||||
return extendSuccessResult(object, false);
|
||||
}
|
||||
|
||||
public static JSONObject extendSuccessResult(JSONObject object, boolean isRoot) {
|
||||
return extendSuccessResult(object, null, isRoot);
|
||||
}
|
||||
|
||||
/**添加请求成功的状态内容
|
||||
* @param object
|
||||
* @param isRoot
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject extendSuccessResult(JSONObject object, boolean isRoot) {
|
||||
return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, isRoot);
|
||||
public static JSONObject extendSuccessResult(JSONObject object, String warn, boolean isRoot) {
|
||||
return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot);
|
||||
}
|
||||
|
||||
/**获取请求成功的状态内容
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newSuccessResult() {
|
||||
return newSuccessResult(false);
|
||||
return newSuccessResult(null);
|
||||
}
|
||||
|
||||
/**获取请求成功的状态内容
|
||||
* @param warn
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newSuccessResult(String warn) {
|
||||
return newSuccessResult(warn, false);
|
||||
}
|
||||
|
||||
/**获取请求成功的状态内容
|
||||
* @param warn
|
||||
* @param isRoot
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject newSuccessResult(boolean isRoot) {
|
||||
return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, isRoot);
|
||||
public static JSONObject newSuccessResult(String warn, boolean isRoot) {
|
||||
return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot);
|
||||
}
|
||||
|
||||
/**添加请求成功的状态内容
|
||||
@ -846,7 +937,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
int code = CommonException.getCode(e);
|
||||
return extendResult(object, code, msg, isRoot);
|
||||
return extendResult(object, code, msg, null, isRoot);
|
||||
}
|
||||
|
||||
/**新建错误状态内容
|
||||
@ -870,16 +961,13 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
String msg = CommonException.getMsg(e);
|
||||
Integer code = CommonException.getCode(e);
|
||||
|
||||
return newResult(code, msg, isRoot);
|
||||
return newResult(code, msg, null, isRoot);
|
||||
}
|
||||
|
||||
return newResult(JSONResponse.CODE_SERVER_ERROR, JSONResponse.MSG_SERVER_ERROR, isRoot);
|
||||
return newResult(JSONResponse.CODE_SERVER_ERROR, JSONResponse.MSG_SERVER_ERROR, null, isRoot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO 启动时一次性加载Request所有内容,作为初始化。
|
||||
/**获取正确的请求,非GET请求必须是服务器指定的
|
||||
* @return
|
||||
* @throws Exception
|
||||
@ -900,7 +988,6 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
*/
|
||||
@Override
|
||||
public JSONObject getStructure(@NotNull String table, String method, String tag, int version) throws Exception {
|
||||
// TODO 目前只使用 Request 而不使用 Response,所以这里写死用 REQUEST_MAP,以后可能 Response 表也会与 Request 表合并,用字段来区分
|
||||
String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag);
|
||||
SortedMap<Integer, JSONObject> versionedMap = AbstractVerifier.REQUEST_MAP.get(cacheKey);
|
||||
|
||||
@ -949,7 +1036,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
// 获取指定的JSON结构 <<<<<<<<<<<<<<
|
||||
SQLConfig config = createSQLConfig().setMethod(GET).setTable(table);
|
||||
SQLConfig<T> config = createSQLConfig().setMethod(GET).setTable(table);
|
||||
config.setPrepared(false);
|
||||
config.setColumn(Arrays.asList("structure"));
|
||||
|
||||
@ -979,7 +1066,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
|
||||
protected Map<String, ObjectParser> arrayObjectParserCacheMap = new HashMap<>();
|
||||
|
||||
// protected SQLConfig itemConfig;
|
||||
// protected SQLConfig<T> itemConfig;
|
||||
/**获取单个对象,该对象处于parentObject内
|
||||
* @param request parentObject 的 value
|
||||
* @param parentPath parentObject 的路径
|
||||
@ -991,7 +1078,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
*/
|
||||
@Override
|
||||
public JSONObject onObjectParse(final JSONObject request
|
||||
, String parentPath, String name, final SQLConfig arrayConfig, boolean isSubquery) throws Exception {
|
||||
, String parentPath, String name, final SQLConfig<T> arrayConfig, boolean isSubquery) throws Exception {
|
||||
|
||||
if (Log.DEBUG) {
|
||||
Log.i(TAG, "\ngetObject: parentPath = " + parentPath
|
||||
@ -1024,7 +1111,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
boolean isArrayMainTable = isSubquery == false && isTable && type == SQLConfig.TYPE_ITEM_CHILD_0 && arrayConfig != null && RequestMethod.isGetMethod(arrayConfig.getMethod(), true);
|
||||
boolean isReuse = isArrayMainTable && position > 0;
|
||||
|
||||
ObjectParser op = null;
|
||||
ObjectParser<T> op = null;
|
||||
if (isReuse) { // 数组主表使用专门的缓存数据
|
||||
op = arrayObjectParserCacheMap.get(parentPath.substring(0, parentPath.lastIndexOf("[]") + 2));
|
||||
op.setParentPath(parentPath);
|
||||
@ -1056,7 +1143,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
if (compat != null && compat) {
|
||||
// 解决对聚合函数字段通过 query:2 分页查总数返回值错误
|
||||
// 这里可能改变了内部的一些数据,下方通过 arrayConfig 还原
|
||||
SQLConfig cfg = op.setSQLConfig(0, 0, 0).getSQLConfig();
|
||||
SQLConfig<T> cfg = op.setSQLConfig(0, 0, 0).getSQLConfig();
|
||||
boolean isExplain = cfg.isExplain();
|
||||
cfg.setExplain(false);
|
||||
|
||||
@ -1064,7 +1151,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
subqy.setFrom(cfg.getTable());
|
||||
subqy.setConfig(cfg);
|
||||
|
||||
SQLConfig countSQLCfg = createSQLConfig();
|
||||
SQLConfig<T> countSQLCfg = createSQLConfig();
|
||||
countSQLCfg.setColumn(Arrays.asList("count(*):count"));
|
||||
countSQLCfg.setFrom(subqy);
|
||||
|
||||
@ -1249,7 +1336,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
|
||||
//Table<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
response = new JSONArray();
|
||||
SQLConfig config = createSQLConfig()
|
||||
SQLConfig<T> config = createSQLConfig()
|
||||
.setMethod(requestMethod)
|
||||
.setCount(size)
|
||||
.setPage(page2)
|
||||
@ -1417,17 +1504,17 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
|
||||
index = path.lastIndexOf("/");
|
||||
String tableKey = index < 0 ? path : path.substring(0, index); // User:owner
|
||||
int index2 = tableKey.lastIndexOf("/");
|
||||
String arrKey = index2 < 0 ? null : tableKey.substring(0, index2);
|
||||
if (arrKey != null && JSONRequest.isArrayKey(arrKey) == false) {
|
||||
int index2 = tableKey.lastIndexOf("/");
|
||||
String arrKey = index2 < 0 ? null : tableKey.substring(0, index2);
|
||||
if (arrKey != null && JSONRequest.isArrayKey(arrKey) == false) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 对应的 " + arrKey + " 不是合法的数组 key[] !" +
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中不能有 join: value 键值对!");
|
||||
}
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中不能有 join: value 键值对!");
|
||||
}
|
||||
|
||||
tableKey = index2 < 0 ? tableKey : tableKey.substring(index2+1);
|
||||
tableKey = index2 < 0 ? tableKey : tableKey.substring(index2+1);
|
||||
|
||||
apijson.orm.Entry<String, String> entry = Pair.parseEntry(tableKey, true);
|
||||
String table = entry.getKey(); // User
|
||||
apijson.orm.Entry<String, String> entry = Pair.parseEntry(tableKey, true);
|
||||
String table = entry.getKey(); // User
|
||||
if (StringUtil.isName(table) == false) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":value 中 value 的 Table 值 " + table + " 不合法!"
|
||||
+ "必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式!"
|
||||
@ -1456,20 +1543,20 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
"必须是 {} 这种 JSONObject 格式!" + e2.getMessage());
|
||||
}
|
||||
|
||||
if (arrKey != null) {
|
||||
if (parentPathObj.get(JSONRequest.KEY_JOIN) != null) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 对应的 " + arrKey + ":{ join: value } 中 value 不合法!" +
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中不能有 join: value 键值对!");
|
||||
if (arrKey != null) {
|
||||
if (parentPathObj.get(JSONRequest.KEY_JOIN) != null) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 对应的 " + arrKey + ":{ join: value } 中 value 不合法!" +
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中不能有 join: value 键值对!");
|
||||
}
|
||||
|
||||
Integer subPage = parentPathObj.getInteger(JSONRequest.KEY_PAGE);
|
||||
if (subPage != null && subPage != 0) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 对应的 " + arrKey + ":{ page: value } 中 value 不合法!" +
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中 page 值只能为 null 或 0 !");
|
||||
}
|
||||
}
|
||||
|
||||
Integer subPage = parentPathObj.getInteger(JSONRequest.KEY_PAGE);
|
||||
if (subPage != null && subPage != 0) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 对应的 " + arrKey + ":{ page: value } 中 value 不合法!" +
|
||||
"@ APP JOIN 最多允许跨 1 层,只能是子数组,且数组对象中 page 值只能为 null 或 0 !");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAppJoin = "@".equals(joinType);
|
||||
boolean isAppJoin = "@".equals(joinType);
|
||||
|
||||
JSONObject refObj = new JSONObject(tableObj.size(), true);
|
||||
|
||||
@ -1487,8 +1574,8 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
if (isAppJoin && StringUtil.isName(key.substring(0, key.length() - 1)) == false) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 中 " + key + " 不合法 !" +
|
||||
"@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接!");
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 中 " + key + " 不合法 !" +
|
||||
"@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接!");
|
||||
}
|
||||
|
||||
refObj.put(key, tableObj.getString(key));
|
||||
@ -1523,20 +1610,20 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
apijson.orm.Entry<String, String> te = tk == null || p.substring(ind2 + 1).indexOf("/") >= 0 ? null : Pair.parseEntry(tk, true);
|
||||
|
||||
if (te != null && JSONRequest.isTableKey(te.getKey()) && request.get(tk) instanceof JSONObject) {
|
||||
if (isAppJoin) {
|
||||
if (refObj.size() >= 1) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":" + e.getKey() + " 中 " + k + " 不合法!"
|
||||
+ "@ APP JOIN 必须有且只有一个引用赋值键值对!");
|
||||
if (isAppJoin) {
|
||||
if (refObj.size() >= 1) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":" + e.getKey() + " 中 " + k + " 不合法!"
|
||||
+ "@ APP JOIN 必须有且只有一个引用赋值键值对!");
|
||||
}
|
||||
|
||||
if (StringUtil.isName(k.substring(0, k.length() - 1)) == false) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 中 " + k + " 不合法 !" +
|
||||
"@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接!");
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtil.isName(k.substring(0, k.length() - 1)) == false) {
|
||||
throw new IllegalArgumentException(JSONRequest.KEY_JOIN + ":'" + e.getKey() + "' 中 " + k + " 不合法 !" +
|
||||
"@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接!");
|
||||
}
|
||||
}
|
||||
|
||||
refObj.put(k, v);
|
||||
continue;
|
||||
refObj.put(k, v);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1658,7 +1745,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
// 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));
|
||||
// AbstractSQLConfig.newSQLConfig<T> 中强制把 id, id{}, userId, userId{} 放到了最前面 tableObj.put(key, tableObj.remove(key));
|
||||
|
||||
if (refObj.size() != tableObj.size()) { // 把 key 强制放最前,AbstractSQLExcecutor 中 config.putWhere 也是放尽可能最前
|
||||
refObj.putAll(tableObj);
|
||||
@ -1670,8 +1757,8 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
// 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致,生成的 SQL 也就一致 >>>>>>>>>
|
||||
}
|
||||
|
||||
//拼接多个 SQLConfig 的SQL语句,然后执行,再把结果分别缓存(Moment, User等)到 SQLExecutor 的 cacheMap
|
||||
// AbstractSQLConfig config0 = null;
|
||||
//拼接多个 SQLConfig<T> 的SQL语句,然后执行,再把结果分别缓存(Moment, User等)到 SQLExecutor 的 cacheMap
|
||||
// AbstractSQLConfig<T> config0 = null;
|
||||
// String sql = "SELECT " + config0.getColumnString() + " FROM " + config0.getTable() + " INNER JOIN " + targetTable + " ON "
|
||||
// + onList.get(0) + config0.getGroupString() + config0.getHavingString() + config0.getOrderString();
|
||||
|
||||
@ -1850,8 +1937,8 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
return target;
|
||||
}
|
||||
|
||||
Log.i(TAG, "getValueByPath return valuePath;");
|
||||
return valuePath;
|
||||
Log.i(TAG, "getValueByPath return null;");
|
||||
return null;
|
||||
}
|
||||
|
||||
//依赖引用关系 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
@ -1894,7 +1981,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Exception {
|
||||
public JSONObject executeSQL(SQLConfig<T> config, boolean isSubquery) throws Exception {
|
||||
if (config == null) {
|
||||
Log.d(TAG, "executeSQL config == null >> return null;");
|
||||
return null;
|
||||
@ -1942,7 +2029,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
else {
|
||||
sqlExecutor = getSQLExecutor();
|
||||
result = sqlExecutor.execute(config, false);
|
||||
// FIXME 改为直接在 sqlExecutor 内加好,最后 Parser 取结果,可以解决并发执行导致内部计算出错
|
||||
// FIXME 改为直接在 sqlExecutor 内加好,最后 Parser<T> 取结果,可以解决并发执行导致内部计算出错
|
||||
// executedSQLDuration += sqlExecutor.getExecutedSQLDuration() + sqlExecutor.getSqlResultDuration();
|
||||
}
|
||||
|
||||
@ -2063,7 +2150,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
queryResultMap = null;
|
||||
}
|
||||
|
||||
private void setOpMethod(JSONObject request, ObjectParser op, String key) {
|
||||
private void setOpMethod(JSONObject request, ObjectParser<T> op, String key) {
|
||||
String _method = key == null ? null : request.getString(apijson.JSONObject.KEY_METHOD);
|
||||
if (_method != null) {
|
||||
RequestMethod method = RequestMethod.valueOf(_method); // 必须精准匹配,避免缓存命中率低
|
||||
@ -2089,7 +2176,7 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
protected JSONObject batchVerify(RequestMethod method, String tag, int version, String name, @NotNull JSONObject request, int maxUpdateCount, SQLCreator creator) throws Exception {
|
||||
JSONObject jsonObject = new JSONObject(true);
|
||||
JSONObject correctRequest = new JSONObject(true);
|
||||
List<String> removeTmpKeys = new ArrayList<>(); // 请求json里面的临时变量,不需要带入后面的业务中,比如 @post、@get等
|
||||
|
||||
Set<String> reqSet = request == null ? null : request.keySet();
|
||||
@ -2098,49 +2185,82 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
for (String key : reqSet) {
|
||||
// key重复直接抛错(xxx:alias, xxx:alias[])
|
||||
if (jsonObject.containsKey(key) || jsonObject.containsKey(key + apijson.JSONObject.KEY_ARRAY)) {
|
||||
throw new IllegalArgumentException("对象名重复,请添加别名区分 ! ,重复对象名为: " + key);
|
||||
// key 重复直接抛错(xxx:alias, xxx:alias[])
|
||||
if (correctRequest.containsKey(key) || correctRequest.containsKey(key + apijson.JSONObject.KEY_ARRAY)) {
|
||||
throw new IllegalArgumentException("对象名重复,请添加别名区分 ! 重复对象名为: " + key);
|
||||
}
|
||||
|
||||
// @post、@get等RequestMethod
|
||||
// @post、@get 等 RequestMethod
|
||||
try {
|
||||
if (key.startsWith("@") && getEnum(RequestMethod.class, key.substring(1).toUpperCase(), null) != null) {
|
||||
RequestMethod keyMethod = apijson.orm.JSONRequest.KEY_METHOD_ENUM_MAP.get(key);
|
||||
if (keyMethod != null) {
|
||||
// 如果不匹配,异常不处理即可
|
||||
RequestMethod _method = RequestMethod.valueOf(key.substring(1).toUpperCase());
|
||||
removeTmpKeys.add(key);
|
||||
|
||||
JSONObject obj = request.getJSONObject(key);
|
||||
Set<String> set = obj == null ? new HashSet<>() : obj.keySet();
|
||||
Object val = request.get(key);
|
||||
JSONObject obj = val instanceof JSONObject ? request.getJSONObject(key) : null;
|
||||
if (obj == null) {
|
||||
if (val instanceof String) {
|
||||
String[] tbls = StringUtil.split((String) val);
|
||||
if (tbls != null && tbls.length > 0) {
|
||||
obj = new JSONObject(true);
|
||||
for (int i = 0; i < tbls.length; i++) {
|
||||
String tbl = tbls[i];
|
||||
if (obj.containsKey(tbl)) {
|
||||
throw new ConflictException(key + ": value 中 " + tbl + " 已经存在,不能重复!");
|
||||
}
|
||||
obj.put(tbl, new JSONObject(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(key + ": value 中 value 类型错误,只能是 String 或 JSONObject {} !");
|
||||
}
|
||||
}
|
||||
|
||||
for (String objKey : set) {
|
||||
Set<Entry<String, Object>> set = obj == null ? new HashSet<>() : obj.entrySet();
|
||||
|
||||
for (Entry<String, Object> objEntry : set) {
|
||||
String objKey = objEntry == null ? null : objEntry.getKey();
|
||||
if (objKey == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> objAttrMap = new HashMap<>();
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, _method);
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, keyMethod);
|
||||
keyObjectAttributesMap.put(objKey, objAttrMap);
|
||||
JSONObject objAttrJson = obj.getJSONObject(objKey);
|
||||
Set<Entry<String, Object>> objSet = objAttrJson == null ? new HashSet<>() : objAttrJson.entrySet();
|
||||
|
||||
for (Entry<String, Object> entry : objSet) {
|
||||
String objAttrKey = entry == null ? null : entry.getKey();
|
||||
if (objAttrKey == null) {
|
||||
continue;
|
||||
Object objVal = objEntry.getValue();
|
||||
JSONObject objAttrJson = objVal instanceof JSONObject ? obj.getJSONObject(objKey) : null;
|
||||
if (objAttrJson == null) {
|
||||
if (objVal instanceof String) {
|
||||
objAttrMap.put(JSONRequest.KEY_TAG, objVal);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(key + ": { " + objKey + ": value 中 value 类型错误,只能是 String 或 JSONObject {} !");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Set<Entry<String, Object>> objSet = objAttrJson == null ? new HashSet<>() : objAttrJson.entrySet();
|
||||
|
||||
switch (objAttrKey) {
|
||||
case apijson.JSONObject.KEY_DATASOURCE:
|
||||
case apijson.JSONObject.KEY_SCHEMA:
|
||||
case apijson.JSONObject.KEY_DATABASE:
|
||||
case JSONRequest.KEY_VERSION:
|
||||
case apijson.JSONObject.KEY_ROLE:
|
||||
case JSONRequest.KEY_TAG:
|
||||
objAttrMap.put(objAttrKey, entry.getValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
for (Entry<String, Object> entry : objSet) {
|
||||
String objAttrKey = entry == null ? null : entry.getKey();
|
||||
if (objAttrKey == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (objAttrKey) {
|
||||
case apijson.JSONObject.KEY_DATASOURCE:
|
||||
case apijson.JSONObject.KEY_SCHEMA:
|
||||
case apijson.JSONObject.KEY_DATABASE:
|
||||
case JSONRequest.KEY_VERSION:
|
||||
case apijson.JSONObject.KEY_ROLE:
|
||||
case JSONRequest.KEY_TAG:
|
||||
objAttrMap.put(objAttrKey, entry.getValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2189,15 +2309,17 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
|
||||
if (key.startsWith("@") || key.endsWith("@")) {
|
||||
jsonObject.put(key, obj);
|
||||
correctRequest.put(key, obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (obj instanceof JSONObject || obj instanceof JSONArray) {
|
||||
RequestMethod _method = null;
|
||||
RequestMethod _method;
|
||||
if (obj instanceof JSONObject) {
|
||||
_method = RequestMethod.valueOf(request.getJSONObject(key).getString(apijson.JSONObject.KEY_METHOD).toUpperCase());
|
||||
String combine = request.getJSONObject(key).getString(KEY_COMBINE);
|
||||
JSONObject tblObj = request.getJSONObject(key);
|
||||
String mn = tblObj == null ? null : tblObj.getString(apijson.JSONObject.KEY_METHOD);
|
||||
_method = mn == null ? null : RequestMethod.valueOf(mn);
|
||||
String combine = _method == null ? null : tblObj.getString(KEY_COMBINE);
|
||||
if (combine != null && RequestMethod.isPublicMethod(_method) == false) {
|
||||
throw new IllegalArgumentException(key + ":{} 里的 @combine:value 不合法!开放请求 GET、HEAD 才允许传 @combine:value !");
|
||||
}
|
||||
@ -2207,22 +2329,14 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
if (attrMap == null) {
|
||||
if (method == RequestMethod.CRUD) {
|
||||
_method = GET;
|
||||
if (attrMap == null) {
|
||||
Map<String, Object> objAttrMap = new HashMap<>();
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, GET);
|
||||
keyObjectAttributesMap.put(key, objAttrMap);
|
||||
} else {
|
||||
attrMap.put(apijson.JSONObject.KEY_METHOD, GET);
|
||||
}
|
||||
Map<String, Object> objAttrMap = new HashMap<>();
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, GET);
|
||||
keyObjectAttributesMap.put(key, objAttrMap);
|
||||
} else {
|
||||
_method = method;
|
||||
if (attrMap == null) {
|
||||
Map<String, Object> objAttrMap = new HashMap<>();
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, method);
|
||||
keyObjectAttributesMap.put(key, objAttrMap);
|
||||
} else {
|
||||
attrMap.put(apijson.JSONObject.KEY_METHOD, method);
|
||||
}
|
||||
Map<String, Object> objAttrMap = new HashMap<>();
|
||||
objAttrMap.put(apijson.JSONObject.KEY_METHOD, method);
|
||||
keyObjectAttributesMap.put(key, objAttrMap);
|
||||
}
|
||||
} else {
|
||||
_method = (RequestMethod) attrMap.get(apijson.JSONObject.KEY_METHOD);
|
||||
@ -2236,29 +2350,29 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
|
||||
// get请求不校验
|
||||
if (RequestMethod.isPublicMethod(_method)) {
|
||||
jsonObject.put(key, obj);
|
||||
correctRequest.put(key, obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(tag != null && !tag.contains(":")) {
|
||||
if (tag != null && ! tag.contains(":")) {
|
||||
JSONObject object = getRequestStructure(_method, tag, version);
|
||||
JSONObject ret = objectVerify(_method, tag, version, name, request, maxUpdateCount, creator, object);
|
||||
jsonObject.putAll(ret);
|
||||
correctRequest.putAll(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
String _tag = buildTag(request, key, method, tag);
|
||||
JSONObject object = getRequestStructure(_method, _tag, version);
|
||||
if(method == RequestMethod.CRUD && StringUtil.isEmpty(tag, true)) {
|
||||
if (method == RequestMethod.CRUD && StringUtil.isEmpty(tag, true)) {
|
||||
JSONObject requestItem = new JSONObject();
|
||||
requestItem.put(key, obj);
|
||||
JSONObject ret = objectVerify(_method, _tag, version, name, requestItem, maxUpdateCount, creator, object);
|
||||
jsonObject.put(key, ret.get(key));
|
||||
correctRequest.put(key, ret.get(key));
|
||||
} else {
|
||||
return objectVerify(_method, _tag, version, name, request, maxUpdateCount, creator, object);
|
||||
}
|
||||
} else {
|
||||
jsonObject.put(key, obj);
|
||||
correctRequest.put(key, obj);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@ -2266,12 +2380,12 @@ public abstract class AbstractParser<T extends Object> implements Parser<T>, Par
|
||||
}
|
||||
}
|
||||
|
||||
// 这里是requestObject ref request 的引用, 删除不需要的临时变量
|
||||
// 这里是 requestObject ref request 的引用, 删除不需要的临时变量
|
||||
for (String removeKey : removeTmpKeys) {
|
||||
request.remove(removeKey);
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
return correctRequest;
|
||||
}
|
||||
|
||||
public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String enumName, final E defaultEnum) {
|
||||
|
@ -118,6 +118,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
public static int MAX_COMBINE_COUNT = 5;
|
||||
public static int MAX_COMBINE_KEY_COUNT = 2;
|
||||
public static float MAX_COMBINE_RATIO = 1.0f;
|
||||
public static boolean ALLOW_MISSING_KEY_4_COMBINE = true;
|
||||
|
||||
public static String DEFAULT_DATABASE = DATABASE_MYSQL;
|
||||
public static String DEFAULT_SCHEMA = "sys";
|
||||
@ -785,13 +786,28 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
private Parser<T> parser;
|
||||
@Override
|
||||
public Parser<T> getParser() {
|
||||
if (parser == null && objectParser != null) {
|
||||
parser = objectParser.getParser();
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
@Override
|
||||
public AbstractSQLConfig setParser(Parser<T> parser) {
|
||||
public AbstractSQLConfig<T> setParser(Parser<T> parser) {
|
||||
this.parser = parser;
|
||||
return this;
|
||||
}
|
||||
public AbstractSQLConfig<T> putWarnIfNeed(String type, String warn) {
|
||||
if (Log.DEBUG && parser instanceof AbstractParser) {
|
||||
((AbstractParser<T>) parser).putWarnIfNeed(type, warn);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public AbstractSQLConfig<T> putWarn(String type, String warn) {
|
||||
if (Log.DEBUG && parser instanceof AbstractParser) {
|
||||
((AbstractParser<T>) parser).putWarn(type, warn);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private ObjectParser objectParser;
|
||||
@Override
|
||||
@ -799,7 +815,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
return objectParser;
|
||||
}
|
||||
@Override
|
||||
public AbstractSQLConfig setObjectParser(ObjectParser objectParser) {
|
||||
public AbstractSQLConfig<T> setObjectParser(ObjectParser objectParser) {
|
||||
this.objectParser = objectParser;
|
||||
return this;
|
||||
}
|
||||
@ -1665,7 +1681,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
*/
|
||||
@Override
|
||||
public String getRawSQL(String key, Object value) throws Exception {
|
||||
return getRawSQL(key, value, false);
|
||||
return getRawSQL(key, value, ! ALLOW_MISSING_KEY_4_COMBINE);
|
||||
}
|
||||
/**获取原始 SQL 片段
|
||||
* @param key
|
||||
@ -1694,6 +1710,9 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
throw new UnsupportedOperationException("@raw:value 的 value 中 " + key + " 不合法!"
|
||||
+ "对应的 " + key + ":value 中 value 值 " + value + " 未在后端 RAW_MAP 中配置 !");
|
||||
}
|
||||
|
||||
putWarnIfNeed(JSONRequest.KEY_RAW, "@raw:value 的 value 中 "
|
||||
+ key + " 不合法!对应的 " + key + ":value 中 value 值 " + value + " 未在后端 RAW_MAP 中配置 !");
|
||||
}
|
||||
else if (rawSQL.isEmpty()) {
|
||||
return (String) value;
|
||||
@ -3397,7 +3416,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
String column = getRealKey(method, key, false, true, verifyName);
|
||||
|
||||
// 原始 SQL 片段
|
||||
String rawSQL = getRawSQL(key, value, keyType != 4 || value instanceof String == false);
|
||||
String rawSQL = getRawSQL(key, value);
|
||||
|
||||
switch (keyType) {
|
||||
case 1:
|
||||
@ -4685,7 +4704,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
+ quote + on.getTargetTable() + quote + "." + quote + on.getTargetKey() + quote;
|
||||
}
|
||||
else {
|
||||
onJoinComplextRelation(sql, quote, j, jt, onList, on);
|
||||
onJoinComplexRelation(sql, quote, j, jt, onList, on);
|
||||
|
||||
if (">=".equals(rt) || "<=".equals(rt) || ">".equals(rt) || "<".equals(rt)) {
|
||||
if (isNot) {
|
||||
@ -4873,13 +4892,6 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
"性能很差、需求极少,默认只允许 = 等价关联,如要取消禁用可在后端重写相关方法!");
|
||||
}
|
||||
|
||||
/**已废弃,最早 6.2.0 移除,请改用 onJoinComplexRelation
|
||||
*/
|
||||
@Deprecated
|
||||
protected void onJoinComplextRelation(String sql, String quote, Join join, String table, List<On> onList, On on) {
|
||||
onJoinComplexRelation(sql, quote, join, table, onList, on);
|
||||
}
|
||||
|
||||
protected void onGetJoinString(Join join) throws UnsupportedOperationException {
|
||||
}
|
||||
protected void onGetCrossJoinString(Join join) throws UnsupportedOperationException {
|
||||
@ -4895,14 +4907,14 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static <T extends Object> SQLConfig newSQLConfig(RequestMethod method, String table, String alias
|
||||
public static <T extends Object> SQLConfig<T> newSQLConfig(RequestMethod method, String table, String alias
|
||||
, JSONObject request, List<Join> joinList, boolean isProcedure, Callback<T> callback) throws Exception {
|
||||
if (request == null) { // User:{} 这种空内容在查询时也有效
|
||||
throw new NullPointerException(TAG + ": newSQLConfig request == null!");
|
||||
}
|
||||
|
||||
boolean explain = request.getBooleanValue(KEY_EXPLAIN);
|
||||
if (explain && Log.DEBUG == false) { // 不在 config.setExplain 抛异常,一方面处理更早性能更好,另一方面为了内部调用可以绕过这个限制
|
||||
Boolean explain = request.getBoolean(KEY_EXPLAIN);
|
||||
if (explain != null && explain && Log.DEBUG == false) { // 不在 config.setExplain 抛异常,一方面处理更早性能更好,另一方面为了内部调用可以绕过这个限制
|
||||
throw new UnsupportedOperationException("非DEBUG模式, 不允许传 " + KEY_EXPLAIN + " !");
|
||||
}
|
||||
|
||||
@ -4915,7 +4927,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
String schema = request.getString(KEY_SCHEMA);
|
||||
String datasource = request.getString(KEY_DATASOURCE);
|
||||
|
||||
SQLConfig config = callback.getSQLConfig(method, database, schema, datasource, table);
|
||||
SQLConfig<T> config = callback.getSQLConfig(method, database, schema, datasource, table);
|
||||
config.setAlias(alias);
|
||||
|
||||
config.setDatabase(database); // 不删,后面表对象还要用的,必须放在 parseJoin 前
|
||||
@ -5065,6 +5077,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
String order = request.getString(KEY_ORDER);
|
||||
String raw = request.getString(KEY_RAW);
|
||||
String json = request.getString(KEY_JSON);
|
||||
String mthd = request.getString(KEY_METHOD);
|
||||
|
||||
try {
|
||||
// 强制作为条件且放在最前面优化性能
|
||||
@ -5286,7 +5299,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
if (StringUtil.isNotEmpty(combineExpr, true)) {
|
||||
List<String> banKeyList = Arrays.asList(idKey, idInKey, userIdKey, userIdInKey);
|
||||
for (String key : banKeyList) {
|
||||
if(keyInCombineExpr(combineExpr, key)) {
|
||||
if (isKeyInCombineExpr(combineExpr, key)) {
|
||||
throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的 value 里 " + key + " 不合法!"
|
||||
+ "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
|
||||
}
|
||||
@ -5338,6 +5351,10 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
if (request.containsKey(w) == false) { // 和 request.get(w) == null 没区别,前面 Parser 已经过滤了 null
|
||||
// throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 对应的 " + w + " 不在它里面!");
|
||||
callback.onMissingKey4Combine(table, request, combine, ws[i], w);
|
||||
if (config instanceof AbstractSQLConfig) {
|
||||
((AbstractSQLConfig<T>) config).putWarnIfNeed(KEY_COMBINE, table + ":{} 里的 @combine:value 中的 value 里 "
|
||||
+ ws[i] + " 对应的条件 " + w + ":value 中 value 必须存在且不能为 null!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5360,7 +5377,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
// 兼容 PUT @combine
|
||||
// 解决AccessVerifier新增userId没有作为条件,而是作为内容,导致PUT,DELETE出错
|
||||
if ((isWhere || (StringUtil.isName(key.replaceFirst("[+-]$", "")) == false))
|
||||
|| (isWhere == false && StringUtil.isNotEmpty(combineExpr, true) && keyInCombineExpr(combineExpr, key))) {
|
||||
|| (isWhere == false && StringUtil.isNotEmpty(combineExpr, true) && isKeyInCombineExpr(combineExpr, key))) {
|
||||
tableWhere.put(key, value);
|
||||
if (whereList.contains(key) == false) {
|
||||
andList.add(key);
|
||||
@ -5547,7 +5564,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
// @having, @haivng& >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
config.setExplain(explain);
|
||||
config.setExplain(explain != null && explain);
|
||||
config.setCache(getCache(cache));
|
||||
config.setDistinct(distinct);
|
||||
config.setColumn(column == null ? null : cs); //解决总是 config.column != null,总是不能得到 *
|
||||
@ -5587,23 +5604,60 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
}
|
||||
|
||||
// 关键词
|
||||
request.put(KEY_DATABASE, database);
|
||||
request.put(KEY_ROLE, role);
|
||||
request.put(KEY_EXPLAIN, explain);
|
||||
request.put(KEY_CACHE, cache);
|
||||
request.put(KEY_DATASOURCE, datasource);
|
||||
request.put(KEY_SCHEMA, schema);
|
||||
request.put(KEY_FROM, from);
|
||||
request.put(KEY_COLUMN, column);
|
||||
request.put(KEY_NULL, nulls);
|
||||
request.put(KEY_CAST, cast);
|
||||
request.put(KEY_COMBINE, combine);
|
||||
request.put(KEY_GROUP, group);
|
||||
request.put(KEY_HAVING, having);
|
||||
request.put(KEY_HAVING_AND, havingAnd);
|
||||
request.put(KEY_ORDER, order);
|
||||
request.put(KEY_RAW, raw);
|
||||
request.put(KEY_JSON, json);
|
||||
if (role != null) {
|
||||
request.put(KEY_ROLE, role);
|
||||
}
|
||||
if (explain != null) {
|
||||
request.put(KEY_EXPLAIN, explain);
|
||||
}
|
||||
if (cache != null) {
|
||||
request.put(KEY_CACHE, cache);
|
||||
}
|
||||
if (database != null) {
|
||||
request.put(KEY_DATABASE, database);
|
||||
}
|
||||
if (datasource != null) {
|
||||
request.put(KEY_DATASOURCE, datasource);
|
||||
}
|
||||
if (schema != null) {
|
||||
request.put(KEY_SCHEMA, schema);
|
||||
}
|
||||
if (from != null) {
|
||||
request.put(KEY_FROM, from);
|
||||
}
|
||||
if (column != null) {
|
||||
request.put(KEY_COLUMN, column);
|
||||
}
|
||||
if (nulls != null) {
|
||||
request.put(KEY_NULL, nulls);
|
||||
}
|
||||
if (cast != null) {
|
||||
request.put(KEY_CAST, cast);
|
||||
}
|
||||
if (combine != null) {
|
||||
request.put(KEY_COMBINE, combine);
|
||||
}
|
||||
if (group != null) {
|
||||
request.put(KEY_GROUP, group);
|
||||
}
|
||||
if (having != null) {
|
||||
request.put(KEY_HAVING, having);
|
||||
}
|
||||
if (havingAnd != null) {
|
||||
request.put(KEY_HAVING_AND, havingAnd);
|
||||
}
|
||||
if (order != null) {
|
||||
request.put(KEY_ORDER, order);
|
||||
}
|
||||
if (raw != null) {
|
||||
request.put(KEY_RAW, raw);
|
||||
}
|
||||
if (json != null) {
|
||||
request.put(KEY_JSON, json);
|
||||
}
|
||||
if (mthd != null) {
|
||||
request.put(KEY_METHOD, mthd);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
@ -5619,7 +5673,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static <T extends Object> SQLConfig parseJoin(RequestMethod method, SQLConfig config
|
||||
public static <T extends Object> SQLConfig<T> parseJoin(RequestMethod method, SQLConfig<T> config
|
||||
, List<Join> joinList, Callback<T> callback) throws Exception {
|
||||
boolean isQuery = RequestMethod.isQueryMethod(method);
|
||||
config.setKeyPrefix(isQuery && config.isMain() == false);
|
||||
@ -5636,8 +5690,8 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
table = j.getTable();
|
||||
alias = j.getAlias();
|
||||
//JOIN子查询不能设置LIMIT,因为ON关系是在子查询后处理的,会导致结果会错误
|
||||
SQLConfig joinConfig = newSQLConfig(method, table, alias, j.getRequest(), null, false, callback);
|
||||
SQLConfig cacheConfig = j.canCacheViceTable() == false ? null : newSQLConfig(method, table, alias
|
||||
SQLConfig<T> joinConfig = newSQLConfig(method, table, alias, j.getRequest(), null, false, callback);
|
||||
SQLConfig<T> cacheConfig = j.canCacheViceTable() == false ? null : newSQLConfig(method, table, alias
|
||||
, j.getRequest(), null, false, callback).setCount(j.getCount());
|
||||
|
||||
if (j.isAppJoin() == false) { //除了 @ APP JOIN,其它都是 SQL JOIN,则副表要这样配置
|
||||
@ -5664,7 +5718,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
joinConfig.setMain(false).setKeyPrefix(true);
|
||||
|
||||
if (j.getOuter() != null) {
|
||||
SQLConfig outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
|
||||
SQLConfig<T> outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
|
||||
outterConfig.setMain(false)
|
||||
.setKeyPrefix(true)
|
||||
.setDatabase(joinConfig.getDatabase())
|
||||
@ -5873,14 +5927,14 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
* @param table
|
||||
* @return
|
||||
*/
|
||||
SQLConfig getSQLConfig(RequestMethod method, String database, String schema, String datasource, String table);
|
||||
SQLConfig<T> getSQLConfig(RequestMethod method, String database, String schema, String datasource, String table);
|
||||
|
||||
/**combine 里的 key 在 request 中 value 为 null 或不存在,即 request 中缺少用来作为 combine 条件的 key: value
|
||||
* @param combine
|
||||
* @param key
|
||||
* @param request
|
||||
*/
|
||||
public void onMissingKey4Combine(String name, JSONObject request, String combine, String item, String key) throws Exception;
|
||||
void onMissingKey4Combine(String name, JSONObject request, String combine, String item, String key) throws Exception;
|
||||
}
|
||||
|
||||
public static Long LAST_ID;
|
||||
@ -5914,13 +5968,16 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
|
||||
@Override
|
||||
public void onMissingKey4Combine(String name, JSONObject request, String combine, String item, String key) throws Exception {
|
||||
if (ALLOW_MISSING_KEY_4_COMBINE) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException(name + ":{} 里的 @combine:value 中的value里 "
|
||||
+ item + " 对应的条件 " + key + ":value 中 value 不能为 null!");
|
||||
+ item + " 对应的条件 " + key + ":value 中 value 必须存在且不能为 null!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean keyInCombineExpr(String combineExpr, String key) {
|
||||
private static boolean isKeyInCombineExpr(String combineExpr, String key) {
|
||||
while (combineExpr.isEmpty() == false) {
|
||||
int index = combineExpr.indexOf(key);
|
||||
if (index < 0) {
|
||||
@ -5938,6 +5995,7 @@ public abstract class AbstractSQLConfig<T extends Object> implements SQLConfig<T
|
||||
}
|
||||
combineExpr = combineExpr.substring(newIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import java.io.BufferedReader;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@ -22,14 +22,7 @@ import java.time.DayOfWeek;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.Year;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
@ -57,7 +50,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
return parser;
|
||||
}
|
||||
@Override
|
||||
public AbstractSQLExecutor setParser(Parser<T> parser) {
|
||||
public AbstractSQLExecutor<T> setParser(Parser<T> parser) {
|
||||
this.parser = parser;
|
||||
return this;
|
||||
}
|
||||
@ -100,10 +93,10 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
/**保存缓存
|
||||
* @param sql key
|
||||
* @param list value
|
||||
* @param config 一般主表 SQLConfig 不为 null,JOIN 副表的为 null
|
||||
* @param config 一般主表 SQLConfig<T> 不为 null,JOIN 副表的为 null
|
||||
*/
|
||||
@Override
|
||||
public void putCache(String sql, List<JSONObject> list, SQLConfig config) {
|
||||
public void putCache(String sql, List<JSONObject> list, SQLConfig<T> config) {
|
||||
if (sql == null || list == null) { // 空 list 有效,说明查询过 sql 了 || list.isEmpty()) {
|
||||
Log.i(TAG, "saveList sql == null || list == null >> return;");
|
||||
return;
|
||||
@ -114,26 +107,26 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
|
||||
/**获取缓存
|
||||
* @param sql key
|
||||
* @param config 一般主表 SQLConfig 不为 null,JOIN 副表的为 null
|
||||
* @param config 一般主表 SQLConfig<T> 不为 null,JOIN 副表的为 null
|
||||
*/
|
||||
@Override
|
||||
public List<JSONObject> getCache(String sql, SQLConfig config) {
|
||||
public List<JSONObject> getCache(String sql, SQLConfig<T> config) {
|
||||
return cacheMap.get(sql);
|
||||
}
|
||||
|
||||
/**获取缓存
|
||||
* @param sql key
|
||||
* @param position
|
||||
* @param config 一般主表 SQLConfig 不为 null,JOIN 副表的为 null
|
||||
* @param config 一般主表 SQLConfig<T> 不为 null,JOIN 副表的为 null
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public JSONObject getCacheItem(String sql, int position, SQLConfig config) {
|
||||
public JSONObject getCacheItem(String sql, int position, SQLConfig<T> config) {
|
||||
List<JSONObject> list = getCache(sql, config);
|
||||
return getCacheItem(list, position, config);
|
||||
}
|
||||
|
||||
public JSONObject getCacheItem(List<JSONObject> list, int position, SQLConfig config) {
|
||||
public JSONObject getCacheItem(List<JSONObject> list, int position, SQLConfig<T> config) {
|
||||
// 只要 list 不为 null,则如果 list.get(position) == null,则返回 {} ,避免再次 SQL 查询
|
||||
if (list == null) {
|
||||
return null;
|
||||
@ -152,7 +145,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @param config
|
||||
*/
|
||||
@Override
|
||||
public void removeCache(String sql, SQLConfig config) {
|
||||
public void removeCache(String sql, SQLConfig<T> config) {
|
||||
if (sql == null) {
|
||||
Log.i(TAG, "removeList sql == null >> return;");
|
||||
return;
|
||||
@ -184,9 +177,8 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public JSONObject execute(@NotNull SQLConfig config, boolean unknownType) throws Exception {
|
||||
public JSONObject execute(@NotNull SQLConfig<T> config, boolean unknownType) throws Exception {
|
||||
long executedSQLStartTime = System.currentTimeMillis();
|
||||
|
||||
final String sql = config.getSQL(false);
|
||||
|
||||
if (StringUtil.isEmpty(sql, true)) {
|
||||
@ -440,7 +432,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
// 为什么 isExplain == false 不用判断?因为所有字段都在一张 Query Plan 表
|
||||
if (index <= 0 && columnIndexAndJoinMap != null) { // && viceColumnStart > length) {
|
||||
|
||||
SQLConfig curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getCacheConfig();
|
||||
SQLConfig<T> curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getCacheConfig();
|
||||
List<String> curColumn = curConfig == null ? null : curConfig.getColumn();
|
||||
String sqlTable = curConfig == null ? null : curConfig.getSQLTable();
|
||||
String sqlAlias = curConfig == null ? null : curConfig.getAlias();
|
||||
@ -469,7 +461,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
int nextViceColumnStart = lastViceColumnStart; // 主表没有 @column 时会偏小 lastViceColumnStart
|
||||
for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
|
||||
Join join = joinList.get(j);
|
||||
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
|
||||
SQLConfig<T> cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
|
||||
List<String> c = cfg == null ? null : cfg.getColumn();
|
||||
|
||||
nextViceColumnStart += (c != null && ! c.isEmpty() ?
|
||||
@ -514,7 +506,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
if (toFindJoin) { // 找到对应的副表 JOIN 配置
|
||||
for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
|
||||
Join join = joinList.get(j);
|
||||
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
|
||||
SQLConfig<T> cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
|
||||
|
||||
if (cfg != null && StringUtil.equalsIgnoreCase(sqlTable, cfg.getSQLTable())
|
||||
) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
|
||||
@ -559,7 +551,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
// 如果是主表则直接用主表对应的 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;
|
||||
SQLConfig<T> viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;
|
||||
if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
|
||||
List<On> onList = curJoin.getOnList();
|
||||
int size = onList == null ? 0 : onList.size();
|
||||
@ -575,7 +567,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
}
|
||||
}
|
||||
}
|
||||
String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
|
||||
String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig<T> 缓存 SQL,减少大量的重复生成
|
||||
|
||||
if (StringUtil.isEmpty(viceSql, true)) {
|
||||
Log.i(TAG, "execute StringUtil.isEmpty(viceSql, true) >> item = null; >> ");
|
||||
@ -683,7 +675,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @param childMap
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void executeAppJoin(SQLConfig config, List<JSONObject> resultList, Map<String, List<JSONObject>> childMap) throws Exception {
|
||||
protected void executeAppJoin(SQLConfig<T> config, List<JSONObject> resultList, Map<String, List<JSONObject>> childMap) throws Exception {
|
||||
List<Join> joinList = config.getJoinList();
|
||||
if (joinList != null) {
|
||||
|
||||
@ -693,7 +685,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
continue;
|
||||
}
|
||||
|
||||
SQLConfig cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
|
||||
SQLConfig<T> cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
|
||||
if (cc == null) {
|
||||
if (Log.DEBUG) {
|
||||
throw new NullPointerException("服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据!");
|
||||
@ -701,7 +693,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
continue;
|
||||
}
|
||||
|
||||
SQLConfig jc = join.getJoinConfig();
|
||||
SQLConfig<T> jc = join.getJoinConfig();
|
||||
|
||||
List<On> onList = join.getOnList();
|
||||
On on = onList == null || onList.isEmpty() ? null : onList.get(0); // APP JOIN 应该有且只有一个 ON 条件
|
||||
@ -908,7 +900,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @return result
|
||||
* @throws Exception
|
||||
*/
|
||||
protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
protected JSONObject onPutColumn(@NotNull SQLConfig<T> config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, Join join, Map<String, JSONObject> childMap) throws Exception {
|
||||
if (table == null) { // 对应副表 viceSql 不能生成正常 SQL, 或者是 ! - Outer, ( - ANTI JOIN 的副表这种不需要缓存及返回的数据
|
||||
Log.i(TAG, "onPutColumn table == null >> return table;");
|
||||
@ -942,7 +934,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected boolean isHideColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
protected boolean isHideColumn(@NotNull SQLConfig<T> config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map<String, JSONObject> childMap) throws SQLException {
|
||||
return rsmd.getColumnName(columnIndex).startsWith("_");
|
||||
}
|
||||
@ -956,7 +948,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @param table
|
||||
* @return resultList
|
||||
*/
|
||||
protected List<JSONObject> onPutTable(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
protected List<JSONObject> onPutTable(@NotNull SQLConfig<T> config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
, @NotNull List<JSONObject> resultList, int position, @NotNull JSONObject table) {
|
||||
|
||||
resultList.add(table);
|
||||
@ -965,7 +957,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
|
||||
|
||||
|
||||
protected String getKey(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
protected String getKey(@NotNull SQLConfig<T> config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, Map<String, JSONObject> childMap) throws Exception {
|
||||
long startTime = System.currentTimeMillis();
|
||||
String key = rsmd.getColumnLabel(columnIndex); // dotIndex < 0 ? lable : lable.substring(dotIndex + 1);
|
||||
@ -985,7 +977,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
return key;
|
||||
}
|
||||
|
||||
protected Object getValue(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
protected Object getValue(@NotNull SQLConfig<T> config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
|
||||
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, String lable, Map<String, JSONObject> childMap) throws Exception {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@ -1075,7 +1067,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable) {
|
||||
public boolean isJSONType(@NotNull SQLConfig<T> config, ResultSetMetaData rsmd, int position, String lable) {
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
String column = rsmd.getColumnTypeName(position);
|
||||
@ -1101,11 +1093,11 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
|
||||
|
||||
@Override // 重写是为了返回类型从 Statement 改为 PreparedStatement,避免其它方法出错
|
||||
public PreparedStatement getStatement(@NotNull SQLConfig config) throws Exception {
|
||||
public PreparedStatement getStatement(@NotNull SQLConfig<T> config) throws Exception {
|
||||
return getStatement(config, null);
|
||||
}
|
||||
@Override
|
||||
public PreparedStatement getStatement(@NotNull SQLConfig config, String sql) throws Exception {
|
||||
public PreparedStatement getStatement(@NotNull SQLConfig<T> config, String sql) throws Exception {
|
||||
if (StringUtil.isEmpty(sql)) {
|
||||
sql = config.getSQL(config.isPrepared());
|
||||
}
|
||||
@ -1157,7 +1149,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
return statement;
|
||||
}
|
||||
|
||||
public PreparedStatement setArgument(@NotNull SQLConfig config, @NotNull PreparedStatement statement, int index, Object value) throws SQLException {
|
||||
public PreparedStatement setArgument(@NotNull SQLConfig<T> config, @NotNull PreparedStatement statement, int index, Object value) throws SQLException {
|
||||
//JSON.isBooleanOrNumberOrString(v) 解决 PostgreSQL: Can't infer the SQL type to use for an instance of com.alibaba.fastjson.JSONArray
|
||||
if (apijson.JSON.isBooleanOrNumberOrString(value)) {
|
||||
statement.setObject(index + 1, value); //PostgreSQL JDBC 不支持隐式类型转换 tinyint = varchar 报错
|
||||
@ -1172,7 +1164,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
protected Connection connection;
|
||||
@NotNull
|
||||
@Override
|
||||
public Connection getConnection(@NotNull SQLConfig config) throws Exception {
|
||||
public Connection getConnection(@NotNull SQLConfig<T> config) throws Exception {
|
||||
String connectionKey = config.getDatasource() + "-" + config.getDatabase();
|
||||
connection = connectionMap.get(connectionKey);
|
||||
if (connection == null || connection.isClosed()) {
|
||||
@ -1202,7 +1194,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
this.transactionIsolation = transactionIsolation;
|
||||
}
|
||||
|
||||
private boolean isIsolationStatusSet = false; //已设置事务等级
|
||||
protected Map<Connection, Integer> isolationMap = new LinkedHashMap<>();
|
||||
@Override
|
||||
public void begin(int transactionIsolation) throws SQLException {
|
||||
Log.d("\n\n" + TAG, "<<<<<<<<<<<<<< TRANSACTION begin transactionIsolation = " + transactionIsolation + " >>>>>>>>>>>>>>>>>>>>>>> \n\n");
|
||||
@ -1210,27 +1202,20 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
// if (connection == null || connection.isClosed()) {
|
||||
// return;
|
||||
// }
|
||||
if (! isIsolationStatusSet) { //只设置一次Isolation等级 PG重复设置事务等级会报错
|
||||
isIsolationStatusSet = true;
|
||||
connection.setTransactionIsolation(transactionIsolation); // 这句导致 TDengine 驱动报错
|
||||
}
|
||||
connection.setAutoCommit(false); //java.sql.SQLException: Can''t call commit when autocommit=true
|
||||
}
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
Log.d("\n\n" + TAG, "<<<<<<<<<<<<<< TRANSACTION rollback >>>>>>>>>>>>>>>>>>>>>>> \n\n");
|
||||
//权限校验不通过,connection 也不会生成,还是得判断 //不做判断,如果掩盖了问题,调用层都不知道为啥事务没有提交成功
|
||||
if (connection == null) { // || connection.isClosed()) {
|
||||
return;
|
||||
}
|
||||
// 将所有连接进行回滚
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
|
||||
// 将所有连接设置隔离级别,且禁止自动提交,需要以下代码来 commit/rollback
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
if (connection != null && connection.isClosed() == false) {
|
||||
connection.rollback();
|
||||
Integer isolation = isolationMap.get(connection);
|
||||
if (isolation == null || isolation != transactionIsolation) { // 只设置一次 Isolation 等级 PG 及 MySQL 某些版本重复设置事务等级会报错
|
||||
isolationMap.put(connection, transactionIsolation);
|
||||
|
||||
connection.setTransactionIsolation(transactionIsolation); // 这句导致 TDengine 驱动报错
|
||||
if (isolation == null) {
|
||||
connection.setAutoCommit(false); // java.sql.SQLException: Can''t call commit when autocommit=true
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
@ -1239,50 +1224,83 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
Log.d("\n\n" + TAG, "<<<<<<<<<<<<<< TRANSACTION rollback >>>>>>>>>>>>>>>>>>>>>>> \n\n");
|
||||
//权限校验不通过,connection 也不会生成,还是得判断 //不做判断,如果掩盖了问题,调用层都不知道为啥事务没有提交成功
|
||||
// if (connection == null) { // || connection.isClosed()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 将所有连接进行回滚
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
if (connection != null && connection.isClosed() == false) {
|
||||
connection.rollback();
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
isolationMap.remove(connection);
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
Log.d("\n\n" + TAG, "<<<<<<<<<<<<<< TRANSACTION rollback savepoint " + (savepoint == null ? "" : "!") + "= null >>>>>>>>>>>>>>>>>>>>>>> \n\n");
|
||||
//权限校验不通过,connection 也不会生成,还是得判断 //不做判断,如果掩盖了问题,调用层都不知道为啥事务没有提交成功
|
||||
if (connection == null) { // || connection.isClosed()) {
|
||||
if (savepoint == null) {
|
||||
rollback();
|
||||
return;
|
||||
}
|
||||
|
||||
if(StringUtil.isEmpty(savepoint)) {
|
||||
// 将所有连接进行回滚
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
//权限校验不通过,connection 也不会生成,还是得判断 //不做判断,如果掩盖了问题,调用层都不知道为啥事务没有提交成功
|
||||
// if (connection == null) { // || connection.isClosed()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
if (connection != null && connection.isClosed() == false) {
|
||||
connection.rollback();
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
// 将所有连接进行回滚
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
if (connection != null && connection.isClosed() == false) {
|
||||
connection.rollback(savepoint);
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
isolationMap.remove(connection);
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connection.rollback(savepoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
Log.d("\n\n" + TAG, "<<<<<<<<<<<<<< TRANSACTION commit >>>>>>>>>>>>>>>>>>>>>>> \n\n");
|
||||
//权限校验不通过,connection 也不会生成,还是得判断 //不做判断,如果掩盖了问题,调用层都不知道为啥事务没有提交成功
|
||||
if (connection == null) { // || connection.isClosed()) {
|
||||
return;
|
||||
}
|
||||
// if (connection == null) { // || connection.isClosed()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 将所有连接进行提交
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
if (connection != null && connection.isClosed() == false) {
|
||||
connection.commit();
|
||||
|
||||
isolationMap.remove(connection);
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
@ -1309,7 +1327,6 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
}
|
||||
|
||||
Collection<Connection> connections = connectionMap.values();
|
||||
|
||||
if (connections != null) {
|
||||
for (Connection connection : connections) {
|
||||
try {
|
||||
@ -1328,13 +1345,13 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet executeQuery(@NotNull SQLConfig config, String sql) throws Exception {
|
||||
public ResultSet executeQuery(@NotNull SQLConfig<T> config, String sql) throws Exception {
|
||||
if (config.isPrepared() == false || config.isTDengine() // TDengine JDBC 不支持 PreparedStatement
|
||||
|| (config.isExplain() && (config.isPresto() || config.isTrino()))) { // Presto JDBC 0.277 在 EXPLAIN 模式下预编译值不会替代 ? 占位导致报错
|
||||
|
||||
Connection conn = getConnection(config);
|
||||
Statement stt = conn.createStatement();
|
||||
//Statement stt = config.isTDengine()
|
||||
// Statement stt = config.isTDengine()
|
||||
// ? conn.createStatement() // fix Presto: ResultSet: Exception: set type is TYPE_FORWARD_ONLY, Result set concurrency must be CONCUR_READ_ONLY
|
||||
// : conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
|
||||
|
||||
@ -1353,7 +1370,7 @@ public abstract class AbstractSQLExecutor<T extends Object> implements SQLExecut
|
||||
|
||||
|
||||
@Override
|
||||
public int executeUpdate(@NotNull SQLConfig config, String sql) throws Exception {
|
||||
public int executeUpdate(@NotNull SQLConfig<T> config, String sql) throws Exception {
|
||||
Statement stt;
|
||||
int count;
|
||||
if (config.isTDengine()) {
|
||||
|
@ -376,18 +376,18 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
|
||||
break;
|
||||
case CONTACT:
|
||||
case CIRCLE:
|
||||
//TODO 做一个缓存contactMap<visitorId, contactArray>,提高[]:{}查询性能, removeAccessInfo时map.remove(visitorId)
|
||||
//不能在Visitor内null -> [] ! 否则会导致某些查询加上不需要的条件!
|
||||
// TODO 做一个缓存contactMap<visitorId, contactArray>,提高[]:{}查询性能, removeAccessInfo时map.remove(visitorId)
|
||||
// 不能在 Visitor内null -> [] ! 否则会导致某些查询加上不需要的条件!
|
||||
List<Object> list = visitor.getContactIdList() == null
|
||||
? new ArrayList<Object>() : new ArrayList<Object>(visitor.getContactIdList());
|
||||
if (CIRCLE.equals(role)) {
|
||||
list.add(visitorId);
|
||||
}
|
||||
|
||||
//key!{}:[] 或 其它没有明确id的条件 等 可以和key{}:list组合。类型错误就报错
|
||||
requestId = config.getWhere(visitorIdKey, true);//JSON里数值不能保证是Long,可能是Integer
|
||||
// key!{}:[] 或 其它没有明确id的条件 等 可以和 key{}:[] 组合。类型错误就报错
|
||||
requestId = config.getWhere(visitorIdKey, true); // JSON 里数值不能保证是 Long,可能是 Integer
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> requestIdArray = (Collection<Object>) config.getWhere(visitorIdKey + "{}", true);//不能是 &{}, |{} 不要传,直接{}
|
||||
Collection<Object> requestIdArray = (Collection<Object>) config.getWhere(visitorIdKey + "{}", true); // 不能是 &{}, |{} 不要传,直接 {}
|
||||
if (requestId != null) {
|
||||
if (requestIdArray == null) {
|
||||
requestIdArray = new JSONArray();
|
||||
@ -395,20 +395,29 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
|
||||
requestIdArray.add(requestId);
|
||||
}
|
||||
|
||||
if (requestIdArray == null) {//可能是@得到 || requestIdArray.isEmpty()) {//请求未声明key:id或key{}:[...]条件,自动补全
|
||||
config.putWhere(visitorIdKey+"{}", JSON.parseArray(list), true); //key{}:[]有效,SQLConfig里throw NotExistException
|
||||
if (requestIdArray == null) { // 可能是 @ 得到 || requestIdArray.isEmpty()) { // 请求未声明 key:id 或 key{}:[...] 条件,自动补全
|
||||
config.putWhere(visitorIdKey+"{}", JSON.parseArray(list), true); // key{}:[] 有效,SQLConfig 里 throw NotExistException
|
||||
}
|
||||
else {//请求已声明key:id或key{}:[]条件,直接验证
|
||||
else { // 请求已声明 key:id 或 key{}:[] 条件,直接验证
|
||||
for (Object id : requestIdArray) {
|
||||
if (id == null) {
|
||||
continue;
|
||||
}
|
||||
if (id instanceof Number == false) {//不能准确地判断Long,可能是Integer
|
||||
throw new UnsupportedDataTypeException(table + ".id类型错误,id类型必须是Long!");
|
||||
|
||||
if (id instanceof Number) { // 不能准确地判断 Long,可能是 Integer
|
||||
if (((Number) id).longValue() <= 0 || list.contains(Long.valueOf("" + id)) == false) { // Integer等转为 Long 才能正确判断,强转崩溃
|
||||
throw new IllegalAccessException(visitorIdKey + " = " + id + " 的 " + table
|
||||
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
|
||||
}
|
||||
}
|
||||
if (list.contains(Long.valueOf("" + id)) == false) {//Integer等转为Long才能正确判断。强转崩溃
|
||||
throw new IllegalAccessException(visitorIdKey + " = " + id + " 的 " + table
|
||||
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
|
||||
else if (id instanceof String) {
|
||||
if (StringUtil.isEmpty(id) || list.contains(id) == false) {
|
||||
throw new IllegalAccessException(visitorIdKey + " = " + id + " 的 " + table
|
||||
+ " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedDataTypeException(table + ".id 类型错误,类型必须是 Long/String!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,41 +14,41 @@ import apijson.RequestMethod;
|
||||
/**远程函数解析器
|
||||
* @author Lemon
|
||||
*/
|
||||
public interface FunctionParser {
|
||||
public interface FunctionParser<T> {
|
||||
|
||||
Object invoke(@NotNull String function, @NotNull JSONObject currentObject) throws Exception;
|
||||
Object invoke(@NotNull String function, @NotNull JSONObject currentObject, boolean containRaw) throws Exception;
|
||||
|
||||
Parser<?> getParser();
|
||||
Parser<T> getParser();
|
||||
|
||||
AbstractFunctionParser setParser(Parser<?> parser);
|
||||
FunctionParser<T> setParser(Parser<T> parser);
|
||||
|
||||
RequestMethod getMethod();
|
||||
FunctionParser setMethod(RequestMethod method);
|
||||
FunctionParser<T> setMethod(RequestMethod method);
|
||||
|
||||
String getTag();
|
||||
FunctionParser setTag(String tag);
|
||||
FunctionParser<T> setTag(String tag);
|
||||
|
||||
int getVersion();
|
||||
AbstractFunctionParser setVersion(int version);
|
||||
FunctionParser<T> setVersion(int version);
|
||||
|
||||
@NotNull
|
||||
JSONObject getRequest();
|
||||
FunctionParser setRequest(@NotNull JSONObject request);
|
||||
FunctionParser<T> setRequest(@NotNull JSONObject request);
|
||||
|
||||
|
||||
String getKey();
|
||||
FunctionParser setKey(String key);
|
||||
FunctionParser<T> setKey(String key);
|
||||
|
||||
String getParentPath();
|
||||
FunctionParser setParentPath(String parentPath);
|
||||
FunctionParser<T> setParentPath(String parentPath);
|
||||
|
||||
String getCurrentName();
|
||||
FunctionParser setCurrentName(String currentName);
|
||||
FunctionParser<T> setCurrentName(String currentName);
|
||||
|
||||
@NotNull
|
||||
JSONObject getCurrentObject();
|
||||
FunctionParser setCurrentObject(@NotNull JSONObject currentObject);
|
||||
FunctionParser<T> setCurrentObject(@NotNull JSONObject currentObject);
|
||||
|
||||
|
||||
|
||||
|
@ -18,20 +18,22 @@ import apijson.RequestMethod;
|
||||
/**简化Parser,getObject和getArray(getArrayConfig)都能用
|
||||
* @author Lemon
|
||||
*/
|
||||
public interface ObjectParser {
|
||||
public interface ObjectParser<T extends Object> {
|
||||
|
||||
Parser<T> getParser();
|
||||
ObjectParser<T> setParser(Parser<T> parser);
|
||||
|
||||
String getParentPath();
|
||||
ObjectParser setParentPath(String parentPath);
|
||||
ObjectParser<T> setParentPath(String parentPath);
|
||||
|
||||
/**解析成员
|
||||
* response重新赋值
|
||||
* @param parentPath
|
||||
* @param name
|
||||
* @param isReuse
|
||||
* @return null or this
|
||||
* @throws Exception
|
||||
*/
|
||||
ObjectParser parse(String name, boolean isReuse) throws Exception;
|
||||
ObjectParser<T> parse(String name, boolean isReuse) throws Exception;
|
||||
|
||||
/**调用 parser 的 sqlExecutor 来解析结果
|
||||
* @param method
|
||||
@ -43,14 +45,14 @@ public interface ObjectParser {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public JSONObject parseResponse(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure) throws Exception;
|
||||
JSONObject parseResponse(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure) throws Exception;
|
||||
/**调用 parser 的 sqlExecutor 来解析结果
|
||||
* @param config
|
||||
* @param isProcedure
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public JSONObject parseResponse(SQLConfig config, boolean isProcedure) throws Exception;
|
||||
JSONObject parseResponse(SQLConfig<T> config, boolean isProcedure) throws Exception;
|
||||
|
||||
|
||||
|
||||
@ -95,20 +97,20 @@ public interface ObjectParser {
|
||||
* @return {@link #setSQLConfig(int, int, int)}
|
||||
* @throws Exception
|
||||
*/
|
||||
ObjectParser setSQLConfig() throws Exception;
|
||||
ObjectParser<T> setSQLConfig() throws Exception;
|
||||
|
||||
/**SQL 配置
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
ObjectParser setSQLConfig(int count, int page, int position) throws Exception;
|
||||
ObjectParser<T> setSQLConfig(int count, int page, int position) throws Exception;
|
||||
|
||||
|
||||
/**执行 SQL
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
ObjectParser executeSQL() throws Exception;
|
||||
ObjectParser<T> executeSQL() throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
@ -129,8 +131,8 @@ public interface ObjectParser {
|
||||
void onChildResponse() throws Exception;
|
||||
|
||||
|
||||
SQLConfig newSQLConfig(boolean isProcedure) throws Exception;
|
||||
SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure) throws Exception;
|
||||
SQLConfig<T> newSQLConfig(boolean isProcedure) throws Exception;
|
||||
SQLConfig<T> newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure) throws Exception;
|
||||
|
||||
/**
|
||||
* response has the final value after parse (and query if isTableKey)
|
||||
@ -143,7 +145,7 @@ public interface ObjectParser {
|
||||
void recycle();
|
||||
|
||||
|
||||
ObjectParser setMethod(RequestMethod method);
|
||||
ObjectParser<T> setMethod(RequestMethod method);
|
||||
RequestMethod getMethod();
|
||||
|
||||
|
||||
@ -151,9 +153,9 @@ public interface ObjectParser {
|
||||
String getPath();
|
||||
String getTable();
|
||||
String getAlias();
|
||||
SQLConfig getArrayConfig();
|
||||
SQLConfig<T> getArrayConfig();
|
||||
|
||||
SQLConfig getSQLConfig();
|
||||
SQLConfig<T> getSQLConfig();
|
||||
JSONObject getResponse();
|
||||
JSONObject getSqlRequest();
|
||||
JSONObject getSqlResponse();
|
||||
|
@ -66,7 +66,7 @@ public interface Parser<T extends Object> {
|
||||
JSONObject getStructure(String table, String method, String tag, int version) throws Exception;
|
||||
|
||||
|
||||
JSONObject onObjectParse(JSONObject request, String parentPath, String name, SQLConfig arrayConfig, boolean isSubquery) throws Exception;
|
||||
JSONObject onObjectParse(JSONObject request, String parentPath, String name, SQLConfig<T> arrayConfig, boolean isSubquery) throws Exception;
|
||||
|
||||
JSONArray onArrayParse(JSONObject request, String parentPath, String name, boolean isSubquery) throws Exception;
|
||||
|
||||
@ -81,7 +81,7 @@ public interface Parser<T extends Object> {
|
||||
*/
|
||||
Object onFunctionParse(String key, String function, String parentPath, String currentName, JSONObject currentObject, boolean containRaw) throws Exception;
|
||||
|
||||
ObjectParser createObjectParser(JSONObject request, String parentPath, SQLConfig arrayConfig, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception;
|
||||
ObjectParser<T> createObjectParser(JSONObject request, String parentPath, SQLConfig<T> arrayConfig, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception;
|
||||
|
||||
int getDefaultQueryCount();
|
||||
int getMaxQueryPage();
|
||||
@ -100,11 +100,11 @@ public interface Parser<T extends Object> {
|
||||
|
||||
void onVerifyLogin() throws Exception;
|
||||
void onVerifyContent() throws Exception;
|
||||
void onVerifyRole(SQLConfig config) throws Exception;
|
||||
void onVerifyRole(SQLConfig<T> config) throws Exception;
|
||||
|
||||
JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Exception;
|
||||
JSONObject executeSQL(SQLConfig<T> config, boolean isSubquery) throws Exception;
|
||||
|
||||
SQLExecutor getSQLExecutor();
|
||||
SQLExecutor<T> getSQLExecutor();
|
||||
Verifier<T> getVerifier();
|
||||
|
||||
|
||||
|
@ -22,21 +22,21 @@ import apijson.NotNull;
|
||||
*/
|
||||
public interface SQLExecutor<T extends Object> {
|
||||
Parser<T> getParser();
|
||||
SQLExecutor setParser(Parser<T> parser);
|
||||
SQLExecutor<T> setParser(Parser<T> parser);
|
||||
|
||||
/**保存缓存
|
||||
* @param sql
|
||||
* @param list
|
||||
* @param config
|
||||
*/
|
||||
void putCache(String sql, List<JSONObject> list, SQLConfig config);
|
||||
void putCache(String sql, List<JSONObject> list, SQLConfig<T> config);
|
||||
|
||||
/**获取缓存
|
||||
* @param sql
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
List<JSONObject> getCache(String sql, SQLConfig config);
|
||||
List<JSONObject> getCache(String sql, SQLConfig<T> config);
|
||||
|
||||
/**获取缓存
|
||||
* @param sql
|
||||
@ -44,13 +44,13 @@ public interface SQLExecutor<T extends Object> {
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
JSONObject getCacheItem(String sql, int position, SQLConfig config);
|
||||
JSONObject getCacheItem(String sql, int position, SQLConfig<T> config);
|
||||
|
||||
/**移除缓存
|
||||
* @param sql
|
||||
* @param config
|
||||
*/
|
||||
void removeCache(String sql, SQLConfig config);
|
||||
void removeCache(String sql, SQLConfig<T> config);
|
||||
|
||||
/**执行SQL
|
||||
* @param config
|
||||
@ -58,7 +58,7 @@ public interface SQLExecutor<T extends Object> {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
JSONObject execute(@NotNull SQLConfig config, boolean unknownType) throws Exception;
|
||||
JSONObject execute(@NotNull SQLConfig<T> config, boolean unknownType) throws Exception;
|
||||
|
||||
//executeQuery和executeUpdate这两个函数因为返回类型不同,所以不好合并
|
||||
/**执行查询
|
||||
@ -66,20 +66,20 @@ public interface SQLExecutor<T extends Object> {
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
default ResultSet executeQuery(@NotNull SQLConfig config) throws Exception {
|
||||
default ResultSet executeQuery(@NotNull SQLConfig<T> config) throws Exception {
|
||||
return executeQuery(config, null);
|
||||
}
|
||||
ResultSet executeQuery(@NotNull SQLConfig config, String sql) throws Exception;
|
||||
ResultSet executeQuery(@NotNull SQLConfig<T> config, String sql) throws Exception;
|
||||
|
||||
/**执行增、删、改
|
||||
* @param config
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
default int executeUpdate(@NotNull SQLConfig config) throws Exception {
|
||||
default int executeUpdate(@NotNull SQLConfig<T> config) throws Exception {
|
||||
return executeUpdate(config, null);
|
||||
}
|
||||
int executeUpdate(@NotNull SQLConfig config, String sql) throws Exception;
|
||||
int executeUpdate(@NotNull SQLConfig<T> config, String sql) throws Exception;
|
||||
|
||||
|
||||
/**判断是否为JSON类型
|
||||
@ -89,14 +89,14 @@ public interface SQLExecutor<T extends Object> {
|
||||
* @param lable
|
||||
* @return
|
||||
*/
|
||||
boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable);
|
||||
boolean isJSONType(@NotNull SQLConfig<T> config, ResultSetMetaData rsmd, int position, String lable);
|
||||
|
||||
|
||||
Connection getConnection(@NotNull SQLConfig config) throws Exception;
|
||||
default Statement getStatement(@NotNull SQLConfig config) throws Exception {
|
||||
Connection getConnection(@NotNull SQLConfig<T> config) throws Exception;
|
||||
default Statement getStatement(@NotNull SQLConfig<T> config) throws Exception {
|
||||
return getStatement(config, null);
|
||||
}
|
||||
Statement getStatement(@NotNull SQLConfig config, String sql) throws Exception;
|
||||
Statement getStatement(@NotNull SQLConfig<T> config, String sql) throws Exception;
|
||||
|
||||
int getTransactionIsolation();
|
||||
void setTransactionIsolation(int transactionIsolation);
|
||||
|
@ -585,6 +585,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
|
||||
|
||||
[APIJSON使用介绍](http://api.flyrise.cn:9099/docs/open-docs//1459)
|
||||
|
||||
[MassCMS With APIJSON最佳实践](https://zhuanlan.zhihu.com/p/655826966)
|
||||
|
||||
### 生态项目
|
||||
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
|
||||
|
||||
@ -646,6 +648,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
|
||||
|
||||
[apijson-examples](https://gitee.com/drone/apijson-examples) APIJSON 的前端、业务后端、管理后端 Demo
|
||||
|
||||
[apijson-ruoyi](https://gitee.com/yxiedd/apijson-ruoyi) APIJSON 和 RuoYi 框架整合,实现零代码生成页面模板接口,在线维护 APIJSON 数据库配置等
|
||||
|
||||
[light4j](https://github.com/xlongwei/light4j) 整合 APIJSON 和微服务框架 light-4j 的 Demo,同时接入了 Redis
|
||||
|
||||
[SpringServer1.2-APIJSON](https://github.com/Airforce-1/SpringServer1.2-APIJSON) 智慧党建服务器端,提供 上传 和 下载 文件的接口
|
||||
|
Loading…
Reference in New Issue
Block a user