Skip to content

Commit 5d59b35

Browse files
committed
优化 JOIN 副表解析结果集 ResultSet 的性能(减少同副表字段的重复逻辑)
1 parent 61883bc commit 5d59b35

File tree

4 files changed

+90
-85
lines changed

4 files changed

+90
-85
lines changed

APIJSONORM/src/main/java/apijson/StringUtil.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.File;
99
import java.math.BigDecimal;
1010
import java.text.DecimalFormat;
11+
import java.util.Objects;
1112
import java.util.regex.Pattern;
1213

1314
/**通用字符串(String)相关类,为null时返回""
@@ -891,4 +892,18 @@ public static String concat(String left, String right, String split, boolean tri
891892

892893
//校正(自动补全等)字符串>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
893894

895+
896+
public static boolean equals(Object s1, Object s2) {
897+
return Objects.equals(s1, s2);
898+
}
899+
public static boolean equalsIgnoreCase(String s1, String s2) {
900+
if (s1 == s2) {
901+
return true;
902+
}
903+
if (s1 == null || s2 == null) {
904+
return false;
905+
}
906+
return s1.equalsIgnoreCase(s2);
907+
}
908+
894909
}

APIJSONORM/src/main/java/apijson/orm/AbstractParser.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,8 @@ public JSONObject executeSQL(SQLConfig config, boolean isSubquery) throws Except
18511851
return result;
18521852
}
18531853
catch (Exception e) {
1854-
if (Log.DEBUG && e.getMessage().contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
1854+
String msg = e.getMessage();
1855+
if (Log.DEBUG && msg != null && msg.contains(Log.KEY_SYSTEM_INFO_DIVIDER) == false) {
18551856
try {
18561857
String db = config.getDatabase();
18571858
if (db == null) {
@@ -1880,7 +1881,7 @@ else if (config.isClickHouse()) {
18801881

18811882
Class<? extends Exception> clazz = e.getClass();
18821883
e = clazz.getConstructor(String.class).newInstance(
1883-
e.getMessage()
1884+
msg
18841885
+ " " + Log.KEY_SYSTEM_INFO_DIVIDER + " \n **环境信息** "
18851886
+ " \n 系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version")
18861887
+ " \n 数据库: " + db + " " + config.getDBVersion()

APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java

Lines changed: 68 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public int getExecutedSQLCount() {
7878
* @param isStatic
7979
*/
8080
@Override
81-
public synchronized void putCache(String sql, List<JSONObject> list, int type) {
81+
public void putCache(String sql, List<JSONObject> list, int type) {
8282
if (sql == null || list == null) { //空map有效,说明查询过sql了 || list.isEmpty()) {
8383
Log.i(TAG, "saveList sql == null || list == null >> return;");
8484
return;
@@ -90,7 +90,7 @@ public synchronized void putCache(String sql, List<JSONObject> list, int type) {
9090
* @param isStatic
9191
*/
9292
@Override
93-
public synchronized void removeCache(String sql, int type) {
93+
public void removeCache(String sql, int type) {
9494
if (sql == null) {
9595
Log.i(TAG, "removeList sql == null >> return;");
9696
return;
@@ -115,7 +115,8 @@ public JSONObject getCacheItem(String sql, int position, int type) {
115115
//只要map不为null,则如果 list.get(position) == null,则返回 {} ,避免再次SQL查询
116116
if (list == null) {
117117
return null;
118-
}
118+
}
119+
119120
JSONObject result = position >= list.size() ? null : list.get(position);
120121
return result != null ? result : new JSONObject();
121122
}
@@ -124,20 +125,14 @@ public JSONObject getCacheItem(String sql, int position, int type) {
124125

125126
@Override
126127
public ResultSet executeQuery(@NotNull Statement statement, String sql) throws Exception {
127-
executedSQLCount ++;
128-
129128
return statement.executeQuery(sql);
130129
}
131130
@Override
132131
public int executeUpdate(@NotNull Statement statement, String sql) throws Exception {
133-
executedSQLCount ++;
134-
135132
return statement.executeUpdate(sql);
136133
}
137134
@Override
138135
public ResultSet execute(@NotNull Statement statement, String sql) throws Exception {
139-
executedSQLCount ++;
140-
141136
statement.execute(sql);
142137
return statement.getResultSet();
143138
}
@@ -186,6 +181,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
186181
try {
187182
if (unknowType) {
188183
Statement statement = getStatement(config);
184+
185+
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
186+
executedSQLCount ++;
187+
}
189188
rs = execute(statement, sql);
190189

191190
result = new JSONObject(true);
@@ -199,8 +198,9 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
199198
case POST:
200199
case PUT:
201200
case DELETE:
202-
executedSQLCount ++;
203-
201+
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
202+
executedSQLCount ++;
203+
}
204204
int updateCount = executeUpdate(config);
205205
if (updateCount <= 0) {
206206
throw new IllegalAccessException("没权限访问或对象不存在!"); // NotExistException 会被 catch 转为成功状态
@@ -222,7 +222,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
222222
case GETS:
223223
case HEAD:
224224
case HEADS:
225-
result = isHead ? null : getCacheItem(sql, position, config.getCache());
225+
result = isHead || isExplain ? null : getCacheItem(sql, position, config.getCache());
226226
Log.i(TAG, ">>> execute result = getCache('" + sql + "', " + position + ") = " + result);
227227
if (result != null) {
228228
cachedSQLCount ++;
@@ -231,11 +231,10 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
231231
return result;
232232
}
233233

234-
rs = executeQuery(config); //FIXME SQL Server 是一次返回两个结果集,包括查询结果和执行计划,需要 moreResults
235-
236234
if (isExplain == false) { //只有 SELECT 才能 EXPLAIN
237235
executedSQLCount ++;
238236
}
237+
rs = executeQuery(config); //FIXME SQL Server 是一次返回两个结果集,包括查询结果和执行计划,需要 moreResults
239238
break;
240239

241240
default://OPTIONS, TRACE等
@@ -270,6 +269,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
270269
childMap = new HashMap<>(); //要存到cacheMap
271270
// Map<Integer, Join> columnIndexAndJoinMap = new HashMap<>(length);
272271
String lastTableName = null; // 默认就是主表 config.getTable();
272+
String lastAliasName = null; // 默认就是主表 config.getAlias();
273273
int lastViceTableStart = 0;
274274
int lastViceColumnStart = 0;
275275
Join lastJoin = null;
@@ -289,6 +289,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
289289
Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n execute while (rs.next()){ index = " + index + "\n\n");
290290

291291
JSONObject item = new JSONObject(true);
292+
JSONObject curItem = item;
292293
boolean isMain = true;
293294

294295
for (int i = 1; i <= length; i++) {
@@ -301,17 +302,19 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
301302
Join curJoin = columnIndexAndJoinMap == null ? null : columnIndexAndJoinMap[i - 1]; // columnIndexAndJoinMap.get(i);
302303

303304
// 为什么 isExplain == false 不用判断?因为所有字段都在一张 Query Plan 表
304-
if (index <= 0 && hasJoin && ! isExplain) { // && viceColumnStart > length) {
305+
if (index <= 0 && columnIndexAndJoinMap != null) { // && viceColumnStart > length) {
305306

306307
SQLConfig curConfig = curJoin == null || ! curJoin.isSQLJoin() ? null : curJoin.getCacheConfig();
307308
List<String> curColumn = curConfig == null ? null : curConfig.getColumn();
308309
String sqlTable = curConfig == null ? null : curConfig.getSQLTable();
310+
String sqlAlias = curConfig == null ? null : curConfig.getAlias();
309311

310312
List<String> column = config.getColumn();
311313
int mainColumnSize = column == null ? 0 : column.size();
314+
// FIXME 主副表同名导致主表数据当成副表数据 { "[]": { "join": "</Comment:to/id@", "Comment": { "toId>": 0 }, "Comment:to": { "@column": "id,content", "id@": "/Comment/toId" } }, "@explain": true }
312315
boolean toFindJoin = mainColumnSize <= 0 || i > mainColumnSize; // 主表就不用找 JOIN 配置
313316

314-
if (StringUtil.isEmpty(sqlTable , true)) {
317+
if (StringUtil.isEmpty(sqlTable, true)) {
315318
if (toFindJoin) { // 在主表字段数量内的都归属主表
316319
sqlTable = rsmd.getTableName(i); // SQL 函数甚至部分字段都不返回表名,当然如果没传 @column 生成的 Table.* 则返回的所有字段都会带表名
317320

@@ -323,12 +326,15 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
323326
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
324327
List<String> c = cfg == null ? null : cfg.getColumn();
325328

326-
nextViceColumnStart += (
327-
c != null && ! c.isEmpty() ? c.size()
328-
: (Objects.equals(sqlTable, lastTableName) || sqlTable.equalsIgnoreCase(lastTableName) ? 1 : 0)
329+
nextViceColumnStart += (c != null && ! c.isEmpty() ?
330+
c.size() : (
331+
StringUtil.equalsIgnoreCase(sqlTable, lastTableName)
332+
&& StringUtil.equals(sqlAlias, lastAliasName) ? 1 : 0
333+
)
329334
);
330335
if (i < nextViceColumnStart) {
331336
sqlTable = cfg.getSQLTable();
337+
sqlAlias = cfg.getAlias();
332338
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
333339

334340
curJoin = join;
@@ -345,6 +351,7 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
345351
// 没有 @column,仍然定位不了,用前一个 table 名。FIXME 如果刚好某个表内第一个字段是就是 SQL 函数?
346352
if (StringUtil.isEmpty(sqlTable, true)) {
347353
sqlTable = lastTableName;
354+
sqlAlias = lastAliasName;
348355
toFindJoin = false;
349356
}
350357
}
@@ -353,16 +360,18 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
353360
sqlTable = sqlTable.substring(1, sqlTable.length() - 1);
354361
}
355362

356-
if ((sqlTable == null && lastTableName != null) || (sqlTable != null && ! sqlTable.equalsIgnoreCase(lastTableName))) {
363+
if (StringUtil.equalsIgnoreCase(sqlTable, lastTableName) == false || StringUtil.equals(sqlAlias, lastAliasName) == false) {
357364
lastTableName = sqlTable;
365+
lastAliasName = sqlAlias;
358366
lastViceColumnStart = i;
359367

360368
if (toFindJoin) { // 找到对应的副表 JOIN 配置
361369
for (int j = lastViceTableStart; j < joinList.size(); j++) { // 查找副表 @column,定位字段所在表
362370
Join join = joinList.get(j);
363371
SQLConfig cfg = join == null || ! join.isSQLJoin() ? null : join.getJoinConfig();
364372

365-
if (cfg != null && sqlTable != null && sqlTable.equalsIgnoreCase(cfg.getSQLTable())) {
373+
if (cfg != null && StringUtil.equalsIgnoreCase(sqlTable, cfg.getSQLTable())
374+
) { // FIXME 导致副表字段错放到主表 && StringUtil.equals(sqlAlias, cfg.getAlias())) {
366375
lastViceTableStart = j; // 避免后面的空 @column 表内字段被放到之前的空 @column 表
367376

368377
curJoin = join;
@@ -378,7 +387,7 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
378387

379388
if (isMain) {
380389
lastViceColumnStart ++;
381-
}
390+
}
382391
else {
383392
if (curJoin == null) {
384393
curJoin = lastJoin;
@@ -398,23 +407,42 @@ else if (config.isClickHouse() && (sqlTable.startsWith("`") || sqlTable.startsWi
398407
}
399408
}
400409

401-
columnIndexAndJoinMap[i - 1] = curJoin; // columnIndexAndJoinMap.put(i, curJoin); // TODO columnIndexAndTableMap[i] = sqlTable 提速?
402-
403-
// if (column != null && column.isEmpty() == false) {
404-
// viceColumnStart = column.size() + 1;
405-
// }
406-
// else if (config.getSQLTable().equalsIgnoreCase(sqlTable) == false) {
407-
// viceColumnStart = i;
408-
// }
410+
columnIndexAndJoinMap[i - 1] = curJoin;
409411
}
412+
413+
// 如果是主表则直接用主表对应的 item,否则缓存副表数据到 childMap
414+
Join prevJoin = columnIndexAndJoinMap == null || i < 2 ? null : columnIndexAndJoinMap[i - 2];
415+
if (curJoin != prevJoin) { // 前后字段不在同一个表对象,即便后面出现 null,也不该是主表数据,而是逻辑 bug 导致
416+
SQLConfig viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;
417+
if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
418+
viceConfig.putWhere(curJoin.getKey(), item.get(curJoin.getTargetKey()), true);
419+
}
420+
String viceSql = viceConfig == null ? null : viceConfig.getSQL(false); //TODO 在 SQLConfig 缓存 SQL,减少大量的重复生成
410421

411-
item = onPutColumn(config, rs, rsmd, index, item, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
422+
if (StringUtil.isEmpty(viceSql, true)) {
423+
Log.i(TAG, "execute StringUtil.isEmpty(viceSql, true) >> item = null; >> ");
424+
curItem = null;
425+
}
426+
else if (curJoin.isOuterJoin() || curJoin.isAntiJoin()) {
427+
Log.i(TAG, "execute curJoin.isOuterJoin() || curJoin.isAntiJoin() >> item = null; >> ");
428+
curItem = null; // 肯定没有数据,缓存也无意义
429+
// 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 childMap.put(viceSql, new JSONObject()); // 缓存固定空数据,避免后续多余查询
430+
}
431+
else {
432+
curItem = (JSONObject) childMap.get(viceSql);
433+
if (curItem == null) {
434+
curItem = new JSONObject(true);
435+
childMap.put(viceSql, curItem);
436+
}
437+
}
438+
}
439+
440+
curItem = onPutColumn(config, rs, rsmd, index, curItem, i, curJoin, childMap); // isExplain == false && hasJoin && i >= viceColumnStart ? childMap : null);
412441
}
413442

414443
resultList = onPutTable(config, rs, rsmd, resultList, index, item);
415444

416-
Log.d(TAG, "\n execute while (rs.next()) { resultList.put( " + index + ", result); "
417-
+ "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
445+
Log.d(TAG, "execute while (rs.next()) { resultList.put( " + index + ", result); " + "\n >>>>>>>>>>>>>>>>>>>>>>>>>>> \n\n");
418446
}
419447
}
420448
}
@@ -622,61 +650,21 @@ protected void executeAppJoin(SQLConfig config, List<JSONObject> resultList, Map
622650
*/
623651
protected JSONObject onPutColumn(@NotNull SQLConfig config, @NotNull ResultSet rs, @NotNull ResultSetMetaData rsmd
624652
, final int tablePosition, @NotNull JSONObject table, final int columnIndex, Join join, Map<String, JSONObject> childMap) throws Exception {
625-
653+
if (table == null) { // 对应副表 viceSql 不能生成正常 SQL, 或者是 ! - Outer, ( - ANTI JOIN 的副表这种不需要缓存及返回的数据
654+
Log.i(TAG, "onPutColumn table == null >> return table;");
655+
return table;
656+
}
657+
626658
if (isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap)) {
627659
Log.i(TAG, "onPutColumn isHideColumn(config, rs, rsmd, tablePosition, table, columnIndex, childMap) >> return table;");
628660
return table;
629661
}
630662

631-
//已改为 rsmd.getTableName(columnIndex) 支持副表不传 @column , 但如何判断是副表?childMap != null
632-
// String lable = rsmd.getColumnLabel(columnIndex);
633-
// int dotIndex = lable.indexOf(".");
634663
String lable = getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap);
635-
636-
// String childTable = childMap == null ? null : sqlTableName; // rsmd.getTableName(columnIndex); //dotIndex < 0 ? null : lable.substring(0, dotIndex);
637-
638-
JSONObject finalTable = null;
639-
String childSql = null;
640-
641-
SQLConfig childConfig = join == null || join.isSQLJoin() == false ? null : join.getCacheConfig();
642-
if (childConfig == null) {
643-
finalTable = table;
644-
}
645-
else {
646-
// lable = column;
647-
648-
//<sql, Table>
649-
650-
// List<Join> joinList = config.getJoinList();
651-
// if (joinList != null) {
652-
// for (Join j : joinList) {
653-
// childConfig = j.isAppJoin() ? null : j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
654-
655-
// FIXME 副表的 SQL 函数,甚至普通字段都可能从 rsmd.getTableName(columnIndex) 拿到 ""
656-
// if (childConfig != null && childTable.equalsIgnoreCase(childConfig.getSQLTable())) {
657-
658-
childConfig.putWhere(join.getKey(), table.get(join.getTargetKey()), true);
659-
childSql = childConfig.getSQL(false);
660-
661-
if (StringUtil.isEmpty(childSql, true)) {
662-
return table;
663-
}
664-
665-
finalTable = (JSONObject) childMap.get(childSql);
666-
// break;
667-
// }
668-
// }
669-
// }
670-
671-
}
672-
673664
Object value = getValue(config, rs, rsmd, tablePosition, table, columnIndex, lable, childMap);
665+
674666
// 必须 put 进去,否则某个字段为 null 可能导致中断后续正常返回值 if (value != null) {
675-
if (finalTable == null) {
676-
finalTable = new JSONObject(true);
677-
childMap.put(childSql, finalTable);
678-
}
679-
finalTable.put(lable, value);
667+
table.put(lable, value);
680668
// }
681669

682670
return table;

APIJSONORM/src/main/java/apijson/orm/Join.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,13 @@ public boolean isLeftOrRightJoin() {
198198
String jt = getJoinType();
199199
return "<".equals(jt) || ">".equals(jt);
200200
}
201-
201+
202202
public boolean canCacheViceTable() {
203203
String jt = getJoinType();
204204
return "@".equals(jt) || "<".equals(jt) || ">".equals(jt) || "&".equals(jt) || "*".equals(jt) || ")".equals(jt);
205+
// 副表是按常规条件查询,缓存会导致其它同表同条件对象查询结果集为空 return ! isFullJoin(); // ! - OUTER, ( - FOREIGN 都需要缓存空副表数据,避免多余的查询
205206
}
206-
207+
207208
public boolean isSQLJoin() {
208209
return ! isAppJoin();
209210
}
@@ -215,7 +216,7 @@ public static boolean isSQLJoin(Join j) {
215216
public static boolean isAppJoin(Join j) {
216217
return j != null && j.isAppJoin();
217218
}
218-
219+
219220
public static boolean isLeftOrRightJoin(Join j) {
220221
return j != null && j.isLeftOrRightJoin();
221222
}

0 commit comments

Comments
 (0)