Merge branch 'Tencent:master' into master

This commit is contained in:
cloudAndMonkey 2023-09-20 14:54:57 +08:00 committed by GitHub
commit 98ec0931f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 948 additions and 414 deletions

View File

@ -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>

View File

@ -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 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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";
}

View File

@ -35,20 +35,24 @@ import static apijson.RequestMethod.GET;
/**简化ParsergetObject和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;
}

View File

@ -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) {

View File

@ -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没有作为条件而是作为内容导致PUTDELETE出错
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;
}

View File

@ -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 不为 nullJOIN 副表的为 null
* @param config 一般主表 SQLConfig<T> 不为 nullJOIN 副表的为 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 不为 nullJOIN 副表的为 null
* @param config 一般主表 SQLConfig<T> 不为 nullJOIN 副表的为 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 不为 nullJOIN 副表的为 null
* @param config 一般主表 SQLConfig<T> 不为 nullJOIN 副表的为 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()) {

View File

@ -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");
}
}
}

View File

@ -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);

View File

@ -18,20 +18,22 @@ import apijson.RequestMethod;
/**简化ParsergetObject和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();

View File

@ -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();

View File

@ -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);

View File

@ -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) 智慧党建服务器端,提供 上传 和 下载 文件的接口