【性能】通过缓存及复用数组主表 ObjectParser 来大幅提升查询数组的性能

This commit is contained in:
TommyLemon 2021-01-31 19:35:52 +08:00
parent 6831cb6a6d
commit a406242a81
5 changed files with 553 additions and 506 deletions

View File

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

View File

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

View File

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

View File

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

View File

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