2021SC@SDUSC
继续上周的分析,在在方法
SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure, Callback callback) throws Exception {}
完结后是方法
public static SQLConfig parseJoin(RequestMethod method, SQLConfig config, List<Join> joinList, Callback callback)throws Exception {}
意为对Join进行语法分析的方法
boolean isQuery = RequestMethod.isQueryMethod(method);
config.setKeyPrefix(isQuery && config.isMain() == false);
boolean isQuery是判断传入的RequestMethod是否是QueryMethod,判断查询语句
config的关键词前缀在查询语句并且isMain的时候为false
//TODO 解析出 SQLConfig 再合并 column, order, group 等
if (joinList == null || joinList.isEmpty() || RequestMethod.isQueryMethod(method) == false) {
return config;
}
这里joinList为空或者RequestMethod.isQueryMethod(method) == false时
直接返回config,意思是传入的配置没有Join方法或者不是查询语句时直接返回config
String table;
String alias;
for (Join j : joinList) {
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, j.getRequest(), null, false, callback).setCount(1);
if (j.isAppJoin() == false) { //除了 @ APP JOIN,其它都是 SQL JOIN,则副表要这样配置
if (joinConfig.getDatabase() == null) {
joinConfig.setDatabase(config.getDatabase()); //解决主表 JOIN 副表,引号不一致
}
else if (joinConfig.getDatabase().equals(config.getDatabase()) == false) {
throw new IllegalArgumentException("主表 " + config.getTable() + " 的 @database:" + config.getDatabase() + " 和它 SQL JOIN 的副表 " + table + " 的 @database:" + joinConfig.getDatabase() + " 不一致!");
}
if (joinConfig.getSchema() == null) {
joinConfig.setSchema(config.getSchema()); //主表 JOIN 副表,默认 schema 一致
}
if (cacheConfig != null) {
cacheConfig.setDatabase(joinConfig.getDatabase()).setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
}
if (isQuery) {
config.setKeyPrefix(true);
}
joinConfig.setMain(false).setKeyPrefix(true);
if (j.isLeftOrRightJoin()) {
SQLConfig outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
outterConfig.setMain(false).setKeyPrefix(true).setDatabase(joinConfig.getDatabase()).setSchema(joinConfig.getSchema()); //解决主表 JOIN 副表,引号不一致
j.setOuterConfig(outterConfig);
}
}
//解决 query: 1/2 查数量时报错
/* SELECT count(*) AS count FROM sys.Moment AS Moment
LEFT JOIN ( SELECT count(*) AS count FROM sys.Comment ) AS Comment ON Comment.momentId = Moment.id LIMIT 1 OFFSET 0 */
if (RequestMethod.isHeadMethod(method, true)) {
joinConfig.setMethod(GET); //子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
joinConfig.setColumn(Arrays.asList(j.getKey())); //优化性能,不取非必要的字段
if (cacheConfig != null) {
cacheConfig.setMethod(GET); //子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
cacheConfig.setColumn(Arrays.asList(j.getKey())); //优化性能,不取非必要的字段
}
}
j.setJoinConfig(joinConfig);
j.setCacheConfig(cacheConfig);
}
首先对Joinlist中的每个join进行分析
创建两个SQLconfig joinConfig和cacheConfig
排除APPJOIN的情况下进行副表配置
joinConfig.getDatabase() == null则进行joinConfig.setDatabase(config.getDatabase())
对joinConfig的Database进行配置,配置为传入的config的Database
joinConfig.getDatabase().equals(config.getDatabase()) == false
joinConfig和config的Database不一致时报错
throw new IllegalArgumentException("主表 " + config.getTable() + " 的 @database:" + config.getDatabase() + " 和它 SQL JOIN 的副表 " + table + " 的 @database:" + joinConfig.getDatabase() + " 不一致!");
joinConfig.getSchema() == null
在joinConfig的Schema为空时
joinConfig.setSchema(config.getSchema());
设置主表的Schema, 与JOIN 副表默认 schema 一致
cacheConfig != null,cacheConfig为空时
cacheConfig.setDatabase(joinConfig.getDatabase()).setSchema(joinConfig.getSchema());
将cacheConfig的Database和Schema设置为joinConfig的内容
j.isLeftOrRightJoin(),j时左连接或右连接时,
SQLConfig outterConfig = newSQLConfig(method, table, alias, j.getOuter(), null, false, callback);
进行配置后,j.setOuterConfig(outterConfig);在j的外配置上条件
RequestMethod.isHeadMethod(method, true),方法是Head方法时,
joinConfig.setMethod(GET);
子查询不能为 SELECT count(*) ,而应该是 SELECT momentId
joinConfig.setColumn(Arrays.asList(j.getKey()));
优化性能,不取非必要的字段
将配置好的joinConfig和CacheConfig传入J
j.setJoinConfig(joinConfig);
j.setCacheConfig(cacheConfig);
然后对Config设定joinlist
config.setJoinList(joinList);
返回config
return config;
public static String getRealKey(RequestMethod method, String originKey
, boolean isTableKey, boolean saveLogic) throws Exception {
return getRealKey(method, originKey, isTableKey, saveLogic, true);
}
/**获取客户端实际需要的key
* verifyName = true
* @param method
* @param originKey
* @param isTableKey
* @param saveLogic 保留逻辑运算符 & | !
* @return
*/
这里是获取实际需要的关键词
/**获取客户端实际需要的key
* @param method
* @param originKey
* @param isTableKey
* @param saveLogic 保留逻辑运算符 & | !
* @param verifyName 验证key名是否符合代码变量/常量名
* @return
*/
public static String getRealKey(RequestMethod method, String originKey
, boolean isTableKey, boolean saveLogic, boolean verifyName) throws Exception {
Log.i(TAG, "getRealKey saveLogic = " + saveLogic + "; originKey = " + originKey);
if (originKey == null || apijson.JSONObject.isArrayKey(originKey)) {
Log.w(TAG, "getRealKey originKey == null || apijson.JSONObject.isArrayKey(originKey) >> return originKey;");
return originKey;
}
String key = new String(originKey);
if (key.endsWith("$")) {//搜索 LIKE,查询时处理
key = key.substring(0, key.length() - 1);
}
else if (key.endsWith("~")) {//匹配正则表达式 REGEXP,查询时处理
key = key.substring(0, key.length() - 1);
if (key.endsWith("*")) {//忽略大小写
key = key.substring(0, key.length() - 1);
}
}
else if (key.endsWith("%")) {//数字、文本、日期范围 BETWEEN AND
key = key.substring(0, key.length() - 1);
}
else if (key.endsWith("{}")) {//被包含 IN,或者说key对应值处于value的范围内。查询时处理
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith("}{")) {//被包含 EXISTS,或者说key对应值处于value的范围内。查询时处理
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith("<>")) {//包含 json_contains,或者说value处于key对应值的范围内。查询时处理
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith("()")) {//方法,查询完后处理,先用一个Map<key,function>保存
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith("@")) {//引用,引用对象查询完后处理。fillTarget中暂时不用处理,因为非GET请求都是由给定的id确定,不需要引用
key = key.substring(0, key.length() - 1);
}
else if (key.endsWith(">=")) {//比较。查询时处理
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith("<=")) {//比较。查询时处理
key = key.substring(0, key.length() - 2);
}
else if (key.endsWith(">")) {//比较。查询时处理
key = key.substring(0, key.length() - 1);
}
else if (key.endsWith("<")) {//比较。查询时处理
key = key.substring(0, key.length() - 1);
}
else if (key.endsWith("+")) {//延长,PUT查询时处理
if (method == PUT) {//不为PUT就抛异常
key = key.substring(0, key.length() - 1);
}
}
else if (key.endsWith("-")) {//缩减,PUT查询时处理
if (method == PUT) {//不为PUT就抛异常
key = key.substring(0, key.length() - 1);
}
}
String last = null;//不用Logic优化代码,否则 key 可能变为 key| 导致 key=value 变成 key|=value 而出错
if (RequestMethod.isQueryMethod(method)) {//逻辑运算符仅供GET,HEAD方法使用
last = key.isEmpty() ? "" : key.substring(key.length() - 1);
if ("&".equals(last) || "|".equals(last) || "!".equals(last)) {
key = key.substring(0, key.length() - 1);
} else {
last = null;//避免key + StringUtil.getString(last)错误延长
}
}
//"User:toUser":User转换"toUser":User, User为查询同名Table得到的JSONObject。交给客户端处理更好
if (isTableKey) {//不允许在column key中使用Type:key形式
key = Pair.parseEntry(key, true).getKey();//table以左边为准
} else {
key = Pair.parseEntry(key).getValue();//column以右边为准
}
if (verifyName && StringUtil.isName(key.startsWith("@") ? key.substring(1) : key) == false) {
throw new IllegalArgumentException(method + "请求,字符 " + originKey + " 不合法!"
+ " key:value 中的key只能关键词 '@key' 或 'key[逻辑符][条件符]' 或 PUT请求下的 'key+' / 'key-' !");
}
if (saveLogic && last != null) {
key = key + last;
}
Log.i(TAG, "getRealKey return key = " + key);
return key;
}
方法public static String getRealKey(RequestMethod method, String originKey
, boolean isTableKey, boolean saveLogic, boolean verifyName) throws Exception {}
首先是在Log里存入
"getRealKey saveLogic = " + saveLogic + "; originKey = " + originKey
和
getRealKey originKey == null || apijson.JSONObject.isArrayKey(originKey) >> return originKey;
String key = new String(originKey);定义关键词key时String类型的originKey
然后对key根据key.endsWith()进行分类处理
RequestMethod.isQueryMethod(method)查询语句时
last = key.isEmpty() ? "" : key.substring(key.length() - 1);
对key进行处理"&".equals(last) || "|".equals(last) || "!".equals(last)
在末尾是&或I或!情况下key = key.substring(0, key.length() - 1);
isTableKey时key = Pair.parseEntry(key, true).getKey();//table以左边为准
否则key = Pair.parseEntry(key).getValue();//column以右边为准
verifyName && StringUtil.isName(key.startsWith("@") ? key.substring(1) : key) == false时
报错
method + "请求,字符 " + originKey + " 不合法!"+ " key:value 中的key只能关键词 '@key' 或 'key[逻辑符][条件符]' 或 PUT请求下的 'key+' / 'key-' !"
public static interface IdCallback {
/**为 post 请求新建 id, 只能是 Long 或 String
* @param method
* @param database
* @param schema
* @param table
* @return
*/
Object newId(RequestMethod method, String database, String schema, String table);
/**已废弃,最早 5.0.0 移除,改用 {@link #getIdKey(String, String, String, String)}
* @param database
* @param schema
* @param table
* @return
*/
@Deprecated
String getIdKey(String database, String schema, String table);
/**获取主键名
* @param database
* @param schema
* @param table
* @return
*/
String getIdKey(String database, String schema, String datasource, String table);
/**已废弃,最早 5.0.0 移除,改用 {@link #getUserIdKey(String, String, String, String)}
* @param database
* @param schema
* @param table
* @return
*/
@Deprecated
String getUserIdKey(String database, String schema, String table);
/**获取 User 的主键名
* @param database
* @param schema
* @param table
* @return
*/
String getUserIdKey(String database, String schema, String datasource, String table);
}
public static interface Callback extends IdCallback {
/**获取 SQLConfig 的实例
* @param method
* @param database
* @param schema
* @param table
* @return
*/
SQLConfig getSQLConfig(RequestMethod method, String database, String schema, 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;
}
public static abstract class SimpleCallback implements Callback {
@Override
public Object newId(RequestMethod method, String database, String schema, String table) {
return System.currentTimeMillis();
}
@Override
public String getIdKey(String database, String schema, String table) {
return KEY_ID;
}
@Override
public String getIdKey(String database, String schema, String datasource, String table) {
return getIdKey(database, schema, table);
}
@Override
public String getUserIdKey(String database, String schema, String table) {
return KEY_USER_ID;
}
@Override
public String getUserIdKey(String database, String schema, String datasource, String table) {
return getUserIdKey(database, schema, table);
}
@Override
public void onMissingKey4Combine(String name, JSONObject request, String combine, String item, String key) throws Exception {
throw new IllegalArgumentException(name + ":{} 里的 @combine:value 中的value里 " + item + " 对应的条件 " + key + ":value 中 value 不能为 null!");
}
}
idcallback是一些接口,有些已经废弃掉了
编程分为两类:系统编程(system programming)和应用编程(application programming)。所谓系统编程,简单来说,就是编写库;而应用编程就是利用写好的各种库来编写具某种功用的程序,也就是应用。系统程序员会给自己写的库留下一些接口,即API(application programming interface,应用编程接口),以供应用程序员使用。所以在抽象层的图示里,库位于应用的底下。
当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。
上图来源于维基百科。
可以看到,回调函数通常和应用处于同一抽象层(因为传入什么样的回调函数是在应用级别决定的)。而回调就成了一个高层调用底层,底层再回过头来调用高层的过程。这应该是回调最早的应用之处,也是其得名如此的原因。
AbstractSQLConfig全部内容以及分析完