【性能】通过缓存及复用数组主表 ObjectParser 来大幅提升查询数组的性能
This commit is contained in:
parent
6831cb6a6d
commit
a406242a81
@ -61,12 +61,6 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
protected final List<Join> joinList;
|
||||
protected final boolean isTable;
|
||||
protected final boolean isArrayMainTable;
|
||||
protected final boolean isReuse;
|
||||
protected final String path;
|
||||
protected final String name;
|
||||
protected final String table;
|
||||
protected final String alias;
|
||||
|
||||
|
||||
protected final boolean tri;
|
||||
/**
|
||||
@ -80,26 +74,23 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
* @param name
|
||||
* @throws Exception
|
||||
*/
|
||||
public AbstractObjectParser(@NotNull JSONObject request, String parentPath, String name, SQLConfig arrayConfig, boolean isSubquery) throws Exception {
|
||||
public AbstractObjectParser(@NotNull JSONObject request, String parentPath, SQLConfig arrayConfig
|
||||
, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException(TAG + ".ObjectParser request == null!!!");
|
||||
}
|
||||
this.request = request;
|
||||
this.parentPath = parentPath;
|
||||
|
||||
this.arrayConfig = arrayConfig;
|
||||
this.isSubquery = isSubquery;
|
||||
|
||||
this.type = arrayConfig == null ? 0 : arrayConfig.getType();
|
||||
this.joinList = arrayConfig == null ? null : arrayConfig.getJoinList();
|
||||
this.name = name;
|
||||
this.path = AbstractParser.getAbsPath(parentPath, name);
|
||||
|
||||
apijson.orm.Entry<String, String> entry = Pair.parseEntry(name, true);
|
||||
this.table = entry.getKey();
|
||||
this.alias = entry.getValue();
|
||||
this.isTable = apijson.JSONObject.isTableKey(table);
|
||||
this.isArrayMainTable = isSubquery == false && this.isTable && this.type == SQLConfig.TYPE_ITEM_CHILD_0 && RequestMethod.isGetMethod(method, true);
|
||||
this.isReuse = isArrayMainTable && arrayConfig != null && arrayConfig.getPosition() > 0;
|
||||
this.isTable = isTable; // apijson.JSONObject.isTableKey(table);
|
||||
this.isArrayMainTable = isArrayMainTable; // isSubquery == false && this.isTable && this.type == SQLConfig.TYPE_ITEM_CHILD_0 && RequestMethod.isGetMethod(method, true);
|
||||
// this.isReuse = isReuse; // isArrayMainTable && arrayConfig != null && arrayConfig.getPosition() > 0;
|
||||
|
||||
this.objectCount = 0;
|
||||
this.arrayCount = 0;
|
||||
@ -117,11 +108,10 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
request.remove(KEY_DROP);
|
||||
}
|
||||
|
||||
|
||||
Log.d(TAG, "AbstractObjectParser table = " + table + "; isTable = " + isTable);
|
||||
Log.d(TAG, "AbstractObjectParser isEmpty = " + isEmpty + "; tri = " + tri + "; drop = " + drop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected int position;
|
||||
public int getPosition() {
|
||||
return position;
|
||||
@ -148,6 +138,12 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
return breakParse || isInvalidate();
|
||||
}
|
||||
|
||||
protected String name;
|
||||
protected String table;
|
||||
protected String alias;
|
||||
protected boolean isReuse;
|
||||
protected String parentName;
|
||||
protected String path;
|
||||
|
||||
protected JSONObject response;
|
||||
protected JSONObject sqlRequest;
|
||||
@ -177,15 +173,28 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public AbstractObjectParser parse() throws Exception {
|
||||
public AbstractObjectParser parse(String name, boolean isReuse) throws Exception {
|
||||
if (isInvalidate() == false) {
|
||||
this.isReuse = isReuse;
|
||||
this.name = name;
|
||||
this.path = AbstractParser.getAbsPath(parentPath, name);
|
||||
|
||||
apijson.orm.Entry<String, String> tentry = Pair.parseEntry(name, true);
|
||||
this.table = tentry.getKey();
|
||||
this.alias = tentry.getValue();
|
||||
|
||||
Log.d(TAG, "AbstractObjectParser parentPath = " + parentPath + "; name = " + name + "; table = " + table + "; alias = " + alias);
|
||||
Log.d(TAG, "AbstractObjectParser type = " + type + "; isTable = " + isTable + "; isArrayMainTable = " + isArrayMainTable);
|
||||
Log.d(TAG, "AbstractObjectParser isEmpty = " + request.isEmpty() + "; tri = " + tri + "; drop = " + drop);
|
||||
|
||||
breakParse = false;
|
||||
|
||||
response = new JSONObject(true);//must init
|
||||
sqlReponse = null;//must init
|
||||
|
||||
if (isReuse == false) {
|
||||
sqlRequest = new JSONObject(true);//must init
|
||||
|
||||
sqlReponse = null;//must init
|
||||
customMap = null;//must init
|
||||
functionMap = null;//must init
|
||||
childMap = null;//must init
|
||||
@ -198,7 +207,6 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
}
|
||||
functionMap = new LinkedHashMap<String, Map<String, String>>();//必须执行
|
||||
|
||||
|
||||
//条件<<<<<<<<<<<<<<<<<<<
|
||||
List<String> whereList = null;
|
||||
if (method == PUT) { //这里只有PUTArray需要处理 || method == DELETE) {
|
||||
@ -270,10 +278,9 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
}
|
||||
}
|
||||
|
||||
// 非Table内的函数会被滞后在onChildParse后调用! onFunctionResponse("-");
|
||||
}
|
||||
|
||||
if (isReuse == false && isTable) {
|
||||
if (isTable) {
|
||||
if (parser.getGlobleDatabase() != null && sqlRequest.get(JSONRequest.KEY_DATABASE) == null) {
|
||||
sqlRequest.put(JSONRequest.KEY_DATABASE, parser.getGlobleDatabase());
|
||||
}
|
||||
@ -291,6 +298,12 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
}
|
||||
}
|
||||
|
||||
if (isTable) { // 非Table内的函数会被滞后在onChildParse后调用
|
||||
onFunctionResponse("-");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isInvalidate()) {
|
||||
recycle();
|
||||
return null;
|
||||
@ -311,11 +324,9 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
@Override
|
||||
public boolean onParse(@NotNull String key, @NotNull Object value) throws Exception {
|
||||
if (key.endsWith("@")) { // StringUtil.isPath((String) value)) {
|
||||
String replaceKey = key.substring(0, key.length() - 1);
|
||||
|
||||
// [] 内主表 position > 0 时,用来生成 SQLConfig 的键值对全都忽略,不解析
|
||||
// if (isReuse == false || replaceKey.endsWith("()") || (replaceKey.startsWith("@") && JSONRequest.TABLE_KEY_LIST.contains(replaceKey) == false)) {
|
||||
if (value instanceof JSONObject) { // key{}@ getRealKey, SQL 子查询对象,JSONObject -> SQLConfig.getSQL
|
||||
String replaceKey = key.substring(0, key.length() - 1);
|
||||
|
||||
JSONObject subquery = (JSONObject) value;
|
||||
String range = subquery.getString(JSONRequest.KEY_SUBQUERY_RANGE);
|
||||
@ -358,9 +369,10 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
parser.putQueryResult(AbstractParser.getAbsPath(path, key), s); //字符串引用保证不了安全性 parser.getSQL(cfg));
|
||||
}
|
||||
else if (value instanceof String) { // //key{}@ getRealKey, 引用赋值路径
|
||||
String replaceKey = key.substring(0, key.length() - 1);
|
||||
|
||||
// System.out.println("getObject key.endsWith(@) >> parseRelation = " + parseRelation);
|
||||
String targetPath = AbstractParser.getValuePath(type == TYPE_ITEM
|
||||
? path : parentPath, new String((String) value));
|
||||
String targetPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, new String((String) value));
|
||||
|
||||
//先尝试获取,尽量保留缺省依赖路径,这样就不需要担心路径改变
|
||||
Object target = onReferenceParse(targetPath);
|
||||
@ -399,7 +411,6 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
else {
|
||||
throw new IllegalArgumentException(path + "/" + key + ":value 中 value 必须为 依赖路径String 或 SQL子查询JSONObject !");
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
if (key.endsWith("()")) {
|
||||
@ -410,14 +421,16 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
String k = key.substring(0, key.length() - 2);
|
||||
|
||||
String type; //远程函数比较少用,一般一个Table:{}内用到也就一两个,所以这里用 "-","0","+" 更直观,转用 -1,0,1 对性能提升不大。
|
||||
if (k.endsWith("-")) { //不能封装到functionMap后批量执行,否则会导致非Table内的 key-():function() 在onChildParse后执行!
|
||||
boolean isMinus = k.endsWith("-");
|
||||
if (isMinus) { //不能封装到functionMap后批量执行,否则会导致非Table内的 key-():function() 在onChildParse后执行!
|
||||
type = "-";
|
||||
k = k.substring(0, k.length() - 1);
|
||||
|
||||
if (isTable == false) {
|
||||
parseFunction(k, (String) value, parentPath, name, request);
|
||||
}
|
||||
else {
|
||||
if (k.endsWith("+")) {
|
||||
}
|
||||
else if (k.endsWith("+")) {
|
||||
type = "+";
|
||||
k = k.substring(0, k.length() - 1);
|
||||
}
|
||||
@ -425,6 +438,7 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
type = "0";
|
||||
}
|
||||
|
||||
if (isMinus == false || isTable) {
|
||||
//远程函数比较少用,一般一个Table:{}内用到也就一两个,所以这里循环里new出来对性能影响不大。
|
||||
Map<String, String> map = functionMap.get(type);
|
||||
if (map == null) {
|
||||
@ -439,9 +453,7 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
customMap.put(key, value);
|
||||
}
|
||||
else {
|
||||
// 导致副表从 1 开始都不查了 if (isReuse == false) {
|
||||
sqlRequest.put(key, value);
|
||||
// }
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -695,7 +707,6 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
else {
|
||||
try {
|
||||
sqlReponse = onSQLExecute();
|
||||
|
||||
}
|
||||
catch (NotExistException e) {
|
||||
// Log.e(TAG, "getObject try { response = getSQLObject(config2); } catch (Exception e) {");
|
||||
@ -799,10 +810,16 @@ public abstract class AbstractObjectParser implements ObjectParser {
|
||||
if (set != null) {
|
||||
int index = 0;
|
||||
for (Entry<String, JSONObject> entry : set) {
|
||||
if (entry != null) {
|
||||
response.put(entry.getKey(), onChildParse(index, entry.getKey(), entry.getValue()));
|
||||
index ++;
|
||||
Object child = entry == null ? null : onChildParse(index, entry.getKey(), entry.getValue());
|
||||
if (child == null
|
||||
|| (child instanceof JSONObject && ((JSONObject) child).isEmpty())
|
||||
|| (child instanceof JSONArray && ((JSONArray) child).isEmpty())
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
response.put(entry.getKey(), child );
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -752,6 +752,8 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
|
||||
|
||||
|
||||
|
||||
protected Map<String, ObjectParser> arrayObjectParserCacheMap = new HashMap<>();
|
||||
|
||||
// protected SQLConfig itemConfig;
|
||||
/**获取单个对象,该对象处于parentObject内
|
||||
* @param parentPath parentObject的路径
|
||||
@ -774,9 +776,10 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
|
||||
}
|
||||
|
||||
int type = arrayConfig == null ? 0 : arrayConfig.getType();
|
||||
int position = arrayConfig == null ? 0 : arrayConfig.getPosition();
|
||||
|
||||
String[] arr = StringUtil.split(parentPath, "/");
|
||||
if (arrayConfig == null || arrayConfig.getPosition() == 0) {
|
||||
if (position == 0) {
|
||||
int d = arr == null ? 1 : arr.length + 1;
|
||||
if (queryDepth < d) {
|
||||
queryDepth = d;
|
||||
@ -787,11 +790,23 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
|
||||
}
|
||||
}
|
||||
|
||||
ObjectParser op = createObjectParser(request, parentPath, name, arrayConfig, isSubquery).parse();
|
||||
boolean isTable = apijson.JSONObject.isTableKey(name);
|
||||
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;
|
||||
if (isReuse) { // 数组主表使用专门的缓存数据
|
||||
op = arrayObjectParserCacheMap.get(parentPath.substring(0, parentPath.lastIndexOf("[]") + 2));
|
||||
}
|
||||
|
||||
if (op == null) {
|
||||
op = createObjectParser(request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable);
|
||||
}
|
||||
op = op.parse(name, isReuse);
|
||||
|
||||
JSONObject response = null;
|
||||
if (op != null) {//SQL查询结果为空时,functionMap和customMap没有意义
|
||||
|
||||
if (arrayConfig == null) { //Common
|
||||
response = op.setSQLConfig().executeSQL().response();
|
||||
}
|
||||
@ -799,9 +814,12 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
|
||||
int query = arrayConfig.getQuery();
|
||||
|
||||
//total 这里不能用arrayConfig.getType(),因为在createObjectParser.onChildParse传到onObjectParse时已被改掉
|
||||
if (type == SQLConfig.TYPE_ITEM_CHILD_0 && query != JSONRequest.QUERY_TABLE
|
||||
&& arrayConfig.getPosition() == 0) {
|
||||
if (type == SQLConfig.TYPE_ITEM_CHILD_0 && query != JSONRequest.QUERY_TABLE && position == 0) {
|
||||
|
||||
RequestMethod method = op.getMethod();
|
||||
JSONObject rp = op.setMethod(RequestMethod.HEAD).setSQLConfig().executeSQL().getSqlReponse();
|
||||
op.setMethod(method);
|
||||
|
||||
if (rp != null) {
|
||||
int index = parentPath.lastIndexOf("]/");
|
||||
if (index >= 0) {
|
||||
@ -842,14 +860,21 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
|
||||
response = null;//不再往后查询
|
||||
} else {
|
||||
response = op
|
||||
.setSQLConfig(arrayConfig.getCount(), arrayConfig.getPage(), arrayConfig.getPosition())
|
||||
.setSQLConfig(arrayConfig.getCount(), arrayConfig.getPage(), position)
|
||||
.executeSQL()
|
||||
.response();
|
||||
// itemConfig = op.getConfig();
|
||||
}
|
||||
}
|
||||
|
||||
op.recycle();
|
||||
if (isArrayMainTable) {
|
||||
if (position == 0) { // 提取并缓存数组主表的列表数据
|
||||
arrayObjectParserCacheMap.put(parentPath.substring(0, parentPath.lastIndexOf("[]") + 2), op);
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// op.recycle();
|
||||
// }
|
||||
op = null;
|
||||
}
|
||||
|
||||
|
@ -2393,9 +2393,9 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
case POST:
|
||||
return "INSERT INTO " + tablePath + config.getColumnString() + " VALUES" + config.getValuesString();
|
||||
case PUT:
|
||||
return "UPDATE " + tablePath + config.getSetString() + config.getWhereString(true) + config.getLimitString();
|
||||
return "UPDATE " + tablePath + config.getSetString() + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : "");
|
||||
case DELETE:
|
||||
return "DELETE FROM " + tablePath + config.getWhereString(true) + config.getLimitString();
|
||||
return "DELETE FROM " + tablePath + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : ""); // PostgreSQL 不允许 LIMIT
|
||||
default:
|
||||
String explain = (config.isExplain() ? (config.isSQLServer() || config.isOracle() ? "SET STATISTICS PROFILE ON " : "EXPLAIN ") : "");
|
||||
if (config.isTest() && RequestMethod.isGetMethod(config.getMethod(), true)) {
|
||||
@ -2615,15 +2615,9 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
String userIdKey = callback.getUserIdKey(database, schema, table);
|
||||
String userIdInKey = userIdKey + "{}";
|
||||
|
||||
//对id和id{}处理,这两个一定会作为条件
|
||||
|
||||
Object idIn = request.get(idInKey); //可能是 id{}:">0"
|
||||
|
||||
if (method == POST && request.get(idKey) == null) {
|
||||
Object newId = callback.newId(method, database, schema, table); // null 表示数据库自增 id
|
||||
if (newId != null) {
|
||||
request.put(idKey, newId);
|
||||
}
|
||||
}
|
||||
|
||||
if (idIn instanceof List) { // 排除掉 0, 负数, 空字符串 等无效 id 值
|
||||
List<?> ids = ((List<?>) idIn);
|
||||
List<Object> newIdIn = new ArrayList<>();
|
||||
@ -2644,8 +2638,12 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
}
|
||||
}
|
||||
|
||||
//对id和id{}处理,这两个一定会作为条件
|
||||
Object id = request.get(idKey);
|
||||
boolean hasId = id != null;
|
||||
if (method == POST && hasId == false) {
|
||||
id = callback.newId(method, database, schema, table); // null 表示数据库自增 id
|
||||
}
|
||||
|
||||
if (id != null) { //null无效
|
||||
if (id instanceof Number) {
|
||||
if (((Number) id).longValue() <= 0) { //一定没有值
|
||||
@ -2695,6 +2693,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
String raw = request.getString(KEY_RAW);
|
||||
String json = request.getString(KEY_JSON);
|
||||
|
||||
try {
|
||||
//强制作为条件且放在最前面优化性能
|
||||
request.remove(idKey);
|
||||
request.remove(idInKey);
|
||||
@ -2942,9 +2941,12 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
|
||||
//TODO 解析JOIN,包括 @column,@group 等要合并
|
||||
|
||||
//后面还可能用到,要还原
|
||||
}
|
||||
finally {//后面还可能用到,要还原
|
||||
//id或id{}条件
|
||||
if (hasId) {
|
||||
request.put(idKey, id);
|
||||
}
|
||||
request.put(idInKey, idIn);
|
||||
//关键词
|
||||
request.put(KEY_DATABASE, database);
|
||||
@ -2960,6 +2962,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
|
||||
request.put(KEY_ORDER, order);
|
||||
request.put(KEY_RAW, raw);
|
||||
request.put(KEY_JSON, json);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
@ -20,14 +20,15 @@ import apijson.RequestMethod;
|
||||
*/
|
||||
public interface ObjectParser {
|
||||
|
||||
|
||||
/**解析成员
|
||||
* response重新赋值
|
||||
* @param config 传递给第0个Table
|
||||
* @param parentPath
|
||||
* @param name
|
||||
* @param isReuse
|
||||
* @return null or this
|
||||
* @throws Exception
|
||||
*/
|
||||
ObjectParser parse() throws Exception;
|
||||
ObjectParser parse(String name, boolean isReuse) throws Exception;
|
||||
|
||||
/**调用 parser 的 sqlExecutor 来解析结果
|
||||
* @param method
|
||||
@ -159,4 +160,5 @@ public interface ObjectParser {
|
||||
Map<String, Map<String, String>> getFunctionMap();
|
||||
Map<String, JSONObject> getChildMap();
|
||||
|
||||
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public interface Parser<T> {
|
||||
*/
|
||||
Object onFunctionParse(String key, String function, String parentPath, String currentName, JSONObject currentObject) throws Exception;
|
||||
|
||||
ObjectParser createObjectParser(JSONObject request, String parentPath, String name, SQLConfig arrayConfig, boolean isSubquery) throws Exception;
|
||||
ObjectParser createObjectParser(JSONObject request, String parentPath, SQLConfig arrayConfig, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception;
|
||||
|
||||
int getDefaultQueryCount();
|
||||
int getMaxQueryPage();
|
||||
|
Loading…
Reference in New Issue
Block a user