APIJSON 博客11 AbstractSQLConfig 第十一篇

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全部内容以及分析完

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值