APIJSON 博客10 AbstractSQLConfig 第十篇

本文详细解析了APIJSON框架中AbstractSQLConfig类的newSQLConfig方法,涉及到JSONObject处理、Map数据结构使用、StringUtils工具类的split方法以及各种条件处理。通过分析请求方法、数据表、别名、请求参数等,构建SQL配置对象,包括设置原始值、处理WHERE条件、COMBINE组合条件等关键步骤。

2021SC@SDUSC

继续上周的分析

在方法SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure, Callback callback) throws Exception {}中,定义了如下变量

        String role = request.getString(KEY_ROLE);
		String cache = request.getString(KEY_CACHE);
		String combine = request.getString(KEY_COMBINE);
		Subquery from = (Subquery) request.get(KEY_FROM);
		String column = request.getString(KEY_COLUMN);
		String group = request.getString(KEY_GROUP);
		String having = request.getString(KEY_HAVING);
		String order = request.getString(KEY_ORDER);
		String raw = request.getString(KEY_RAW);
		String json = request.getString(KEY_JSON);

对JSONObject request 的getString方法,获得相应的变量

关于变量的解释,在JSONObject类中有详细注释如下

    public static final String KEY_TRY = "@try"; //尝试,忽略异常
	public static final String KEY_CATCH = "@catch"; //TODO 捕捉到异常后,处理方式  null-不处理;DEFAULT-返回默认值;ORIGIN-返回请求里的原始值
	public static final String KEY_DROP = "@drop"; //丢弃,不返回,TODO 应该通过 fastjson 的 ignore 之类的机制来处理,避免导致下面的对象也不返回
	//	public static final String KEY_KEEP = "@keep"; //一定会返回,为 null 或 空对象时,会使用默认值(非空),解决其它对象因为不关联的第一个对为空导致也不返回
	public static final String KEY_DEFULT = "@default"; //TODO 自定义默认值 { "@default":true },@default 可完全替代 @keep
	public static final String KEY_NULL = "@null"; //TODO 值为 null 的键值对 "@null":"tag,pictureList",允许 is NULL 条件判断, SET tag = NULL 修改值为 NULL 等

	public static final String KEY_ROLE = "@role"; //角色,拥有对某些数据的某些操作的权限
	public static final String KEY_DATABASE = "@database"; //数据库类型,默认为MySQL
	public static final String KEY_SCHEMA = "@schema"; //数据库,Table在非默认schema内时需要声明
	public static final String KEY_DATASOURCE = "@datasource"; //数据源
	public static final String KEY_EXPLAIN = "@explain"; //分析 true/false
	public static final String KEY_CACHE = "@cache"; //缓存 RAM/ROM/ALL
	public static final String KEY_COLUMN = "@column"; //查询的Table字段或SQL函数

在对APIJSON的分析中,我的同学分析了JSONObject类

地址如下

(31条消息) 代码分析七_鲲不鲲的博客-优快云博客

​​​​​​(31条消息) 代码分析八_鲲不鲲的博客-优快云博客

同学的代码详细的分析了JSONObject类的定义和用途

下面是在一个try{}内


			//强制作为条件且放在最前面优化性能
			request.remove(idKey);
			request.remove(idInKey);
			//关键词
			request.remove(KEY_ROLE);
			request.remove(KEY_EXPLAIN);
			request.remove(KEY_CACHE);
			request.remove(KEY_DATASOURCE);
			request.remove(KEY_DATABASE);
			request.remove(KEY_SCHEMA);
			request.remove(KEY_COMBINE);
			request.remove(KEY_FROM);
			request.remove(KEY_COLUMN);
			request.remove(KEY_GROUP);
			request.remove(KEY_HAVING);
			request.remove(KEY_ORDER);
			request.remove(KEY_RAW);
			request.remove(KEY_JSON);

 这里remove是移除了request的内部变量

			String[] rawArr = StringUtil.split(raw);
			config.setRaw(rawArr == null || rawArr.length <= 0 ? null : new ArrayList<>(Arrays.asList(rawArr)));

			Map<String, Object> tableWhere = new LinkedHashMap<String, Object>();//保证顺序好优化 WHERE id > 1 AND name LIKE...

这里用到了StringUtils.split(),作用是分割字符

config.setRaw(rawArr == null || rawArr.length <= 0 ? null : new ArrayList<(Arrays.asList(rawArr)));

这里设置了config的raw,rawArr == null || rawArr.length <= 0时,设为null

不为空时设为new ArrayList<(Arrays.asList(rawArr))

Map<String, Object> tableWhere = new LinkedHashMap<String, Object>();

//保证顺序好优化 WHERE id > 1 AND name LIKE...

这里用到了Map<String, Object>

Map是一个接口,即Interface Map<K,V>,其中K-key类型和V-value的类型

  它的每个元素包含一个key对象和一个value对象,且在这两个对象之间存在一种映射的对应关系,所有从Map集合中访问元素时,只有指定了key就可以找到对应的value,因此key必须是唯一的且不能重复,当key相同时,后面的value值会覆盖之前的value值。

Map定义的一些通用的方法

 

Map接口有很多实现类,如TreeMap,Hashtable,SortedMap,HashMap。

LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。LinkedHashMap实现与HashMap的不同之处在于,LinkedHashMap维护着一个运行于所有条目的双重链接列表此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序(insert-order)或者是访问顺序,其中默认的迭代访问顺序就是插入顺序,即可以按插入的顺序遍历元素,这点和HashMap有很大的不同

知识学习

org.apache.commons.lang.StringUtils中方法的操作对象是java.lang.String类型的对象,是JDK提供的String类型操作方法的补充,并且是null安全的(即如果输入参数String为null则不会抛出NullPointerException,而是做了相应处理,例如,如果输入为null则返回也是null等,具体可以查看源代码)。

除了构造器,StringUtils中一共有130多个方法,并且都是static的,

所以我们可以这样调用StringUtils.xxx()。

53.public static String[] split(String str, char separatorChar)

把字符串拆分成一个字符串数组,用指定的字符separatorChar作为分隔符。

如果字符串为null,返回null

如果字符串为"",返回空数组{}

举例(*表示任意):

StringUtils.split(null, *)           = null

StringUtils.split("", *)             = {}

StringUtils.split("as df yy",' '))   = {"as","df","yy"}       

StringUtils.split(" as df yy ",' ')) = {"as","df","yy"}       

StringUtils.split("asodfoyy",'o'))   = {"as","df","yy"}       

StringUtils.split("as.df.yy",'.'))   = {"as","df","yy"}       

StringUtils.split("as\ndf\nyy",'\n'))= {"as","df","yy"}       

StringUtils.split("as",' '))         = {"as"}       

StringUtils.split(" as ",' '))       = {"as"}

更多使用方法可以查看下面这篇文章

(30条消息) StringUtil方法全集_Logica的专栏-优快云博客_stringutil

下面是POST操作和非POST操作

			if (method == POST) { //POST操作
				if (idIn != null) {
					throw new IllegalArgumentException("POST 请求中不允许传 " + idInKey + " !");
				}

				if (set != null && set.isEmpty() == false) { //不能直接return,要走完下面的流程
					String[] columns = set.toArray(new String[]{});

					Collection<Object> valueCollection = request.values();
					Object[] values = valueCollection == null ? null : valueCollection.toArray();

					if (values == null || values.length != columns.length) {
						throw new Exception("服务器内部错误:\n" + TAG
								+ " newSQLConfig  values == null || values.length != columns.length !");
					}
					column = (id == null ? "" : idKey + ",") + StringUtil.getString(columns); //set已经判断过不为空

					List<List<Object>> valuess = new ArrayList<>(1);
					List<Object> items; //(item0, item1, ...)
					if (id == null) { //数据库自增 id
						items = Arrays.asList(values); //FIXME 是否还需要进行 add 或 remove 操作?Arrays.ArrayList 不允许修改,会抛异常
					}
					else {
						int size = columns.length + (id == null ? 0 : 1); //以key数量为准

						items = new ArrayList<>(size);
						items.add(id); //idList.get(i)); //第0个就是id

						for (int j = 1; j < size; j++) {
							items.add(values[j-1]); //从第1个开始,允许"null"
						}
					}

					valuess.add(items);
					config.setValues(valuess);
				}
			} 
			else { //非POST操作
				final boolean isWhere = method != PUT;//除了POST,PUT,其它全是条件!!!

				//条件<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
				List<String> whereList = null;

				Map<String, List<String>> combineMap = new LinkedHashMap<>();
				List<String> andList = new ArrayList<>();
				List<String> orList = new ArrayList<>();
				List<String> notList = new ArrayList<>();

				//强制作为条件且放在最前面优化性能
				if (id != null) {
					tableWhere.put(idKey, id);
					andList.add(idKey);
				}
				if (idIn != null) {
					tableWhere.put(idInKey, idIn);
					andList.add(idInKey);
				}

				String[] ws = StringUtil.split(combine);
				if (ws != null) {
					if (method == DELETE || method == GETS || method == HEADS) {
						throw new IllegalArgumentException("DELETE,GETS,HEADS 请求不允许传 @combine:value !");
					}
					whereList = new ArrayList<>();

					String w;
					for (int i = 0; i < ws.length; i++) { //去除 &,|,! 前缀
						w = ws[i];
						if (w != null) {
							if (w.startsWith("&")) {
								w = w.substring(1);
								andList.add(w);
							}
							else if (w.startsWith("|")) {
								if (method == PUT) {
									throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
											+ "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
								}
								w = w.substring(1);
								orList.add(w);
							}
							else if (w.startsWith("!")) {
								if (method == PUT) {
									throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!"
											+ "PUT请求的 @combine:\"key0,key1,...\" 不允许传 |key 或 !key !");
								}
								w = w.substring(1);
								notList.add(w);
							}
							else {
								orList.add(w);
							}

							if (w.isEmpty()) {
								throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里条件 " + ws[i] + " 不合法!不允许为空值!");
							}
							else {
								if (idKey.equals(w) || idInKey.equals(w) || userIdKey.equals(w) || userIdInKey.equals(w)) {
									throw new UnsupportedOperationException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 不合法!"
											+ "不允许传 [" + idKey + ", " + idInKey + ", " + userIdKey + ", " + userIdInKey + "] 其中任何一个!");
								}
							}

							whereList.add(w);
						}

						// 可重写回调方法自定义处理 // 动态设置的场景似乎很少,而且去掉后不方便用户排错!//去掉判断,有时候不在没关系,如果是对增删改等非开放请求强制要求传对应的条件,可以用 Operation.NECESSARY
						if (request.containsKey(w) == false) {  //和 request.get(w) == null 没区别,前面 Parser 已经过滤了 null
							//	throw new IllegalArgumentException(table + ":{} 里的 @combine:value 中的value里 " + ws[i] + " 对应的 " + w + " 不在它里面!");
							callback.onMissingKey4Combine(table, request, combine, ws[i], w);
						}
					}

				}

				//条件>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				Map<String, Object> tableContent = new LinkedHashMap<String, Object>();
				Object value;
				for (String key : set) {
					value = request.get(key);

					if (value instanceof Map) {//只允许常规Object
						throw new IllegalArgumentException("不允许 " + key + " 等任何key的value类型为 {JSONObject} !");
					}

					//解决AccessVerifier新增userId没有作为条件,而是作为内容,导致PUT,DELETE出错
					if (isWhere || (StringUtil.isName(key) == false)) {
						tableWhere.put(key, value);
						if (whereList == null || whereList.contains(key) == false) {
							andList.add(key);
						}
					}
					else if (whereList != null && whereList.contains(key)) {
						tableWhere.put(key, value);
					}
					else {
						tableContent.put(key, value);//一样 instanceof JSONArray ? JSON.toJSONString(value) : value);
					}
				}

				combineMap.put("&", andList);
				combineMap.put("|", orList);
				combineMap.put("!", notList);
				config.setCombine(combineMap);

				config.setContent(tableContent);
			}

POST操作时,

set != null && set.isEmpty() == false,set不为空的情况下

定义了以下变量

Collection<Object> valueCollection,Object[] values,

List<List<Object>> valuess,List<Object> items;

然后对变量进行处理

valuess.add(items);
config.setValues(valuess);

对于非POST操作

final boolean isWhere = method != PUT;//除了POST,PUT,其它全是条件!!!

                List<String> whereList = null;

                Map<String, List<String>> combineMap = new LinkedHashMap<>();
                List<String> andList = new ArrayList<>();
                List<String> orList = new ArrayList<>();
                List<String> notList = new ArrayList<>();

定义了以上条件列表

然后String[] ws = StringUtil.split(combine);进行拆分操作

                    if (method == DELETE || method == GETS || method == HEADS) {
                        throw new IllegalArgumentException("DELETE,GETS,HEADS 请求不允许传 @combine:value !");
                    }

要注意的是DELETE,GETS,HEADS 请求不允许传 @combine:value 

对拆分后得到的ws进行字符处理,根据不同的字符加入到不同的list

最后添加数据到定义的List

                combineMap.put("&", andList);
                combineMap.put("|", orList);
                combineMap.put("!", notList);
                config.setCombine(combineMap);

                config.setContent(tableContent);

			List<String> cs = new ArrayList<>();

			List<String> rawList = config.getRaw();
			boolean containColumnRaw = rawList != null && rawList.contains(KEY_COLUMN);

			String rawColumnSQL = null;
			if (containColumnRaw) {
				try {
					rawColumnSQL = config.getRawSQL(KEY_COLUMN, column);
					if (rawColumnSQL != null) {
						cs.add(rawColumnSQL);
					}
				} catch (Exception e) {
					Log.e(TAG, "newSQLConfig  config instanceof AbstractSQLConfig >> try {  "
							+ "  rawColumnSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, column); "
							+ "} catch (Exception e) = " + e.getMessage());
				}
			}

这里定义了List<String> cs,List<String> rawList

判断条件boolean containColumnRaw = rawList != null && rawList.contains(KEY_COLUMN);

rawColumnSQL = config.getRawSQL(KEY_COLUMN, column);
 if (rawColumnSQL != null) {
           cs.add(rawColumnSQL);
}

cs添加了 config.getRawSQL(KEY_COLUMN, column)不为空的内容

			boolean distinct = column == null || rawColumnSQL != null ? false : column.startsWith(PREFFIX_DISTINCT);
			if (rawColumnSQL == null) {
				String[] fks = StringUtil.split(distinct ? column.substring(PREFFIX_DISTINCT.length()) : column, ";"); // key0,key1;fun0(key0,...);fun1(key0,...);key3;fun2(key0,...)
				if (fks != null) {
					String[] ks;
					for (String fk : fks) {
						if (containColumnRaw) {
							try {
								String rawSQL = config.getRawSQL(KEY_COLUMN, fk);
								if (rawSQL != null) {
									cs.add(rawSQL);
									continue;
								}
							} catch (Exception e) {
								Log.e(TAG, "newSQLConfig  rawColumnSQL == null >> try {  "
										+ "  String rawSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, fk); ... "
										+ "} catch (Exception e) = " + e.getMessage());
							}
						}

						if (fk.contains("(")) {  // fun0(key0,...)
							cs.add(fk);
						}
						else { //key0,key1...
							ks = StringUtil.split(fk);
							if (ks != null && ks.length > 0) {
								cs.addAll(Arrays.asList(ks));
							}
						}
					}
				}
			}

定义的判断条件是boolean distinct = column == null || rawColumnSQL != null ? false : column.startsWith(PREFFIX_DISTINCT);

rawColumnSQL == null时,

String[] fks = StringUtil.split(distinct ? column.substring(PREFFIX_DISTINCT.length()) : column, ";");

// key0,key1;fun0(key0,...);fun1(key0,...);key3;fun2(key0,...)

将column.substring(PREFFIX_DISTINCT.length()) 或column拆分

fks 不为空时,用for (String fk : fks)对fks循环处理

String rawSQL = config.getRawSQL(KEY_COLUMN, fk);
加入到cs: cs.add(rawSQL);

设置了异常                              

 Log.e(TAG, "newSQLConfig  rawColumnSQL == null >> try {  "
                                        + "  String rawSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, fk); ... "
                                        + "} catch (Exception e) = " + e.getMessage());

			config.setExplain(explain);
			config.setCache(getCache(cache));
			config.setFrom(from);
			config.setDistinct(distinct);
			config.setColumn(column == null ? null : cs); //解决总是 config.column != null,总是不能得到 *
			config.setWhere(tableWhere);					

			config.setId(id);
			//在	tableWhere 第0个		config.setIdIn(idIn);

			config.setRole(RequestRole.get(role));
			config.setGroup(group);
			config.setHaving(having);
			config.setOrder(order);

			String[] jsonArr = StringUtil.split(json);
			config.setJson(jsonArr == null || jsonArr.length <= 0 ? null : new ArrayList<>(Arrays.asList(jsonArr)));

			//TODO 解析JOIN,包括 @column,@group 等要合并

然后设置config的配置

最后还有一个Finally,还原了配置,对应前文的remove

finally {//后面还可能用到,要还原
			//id或id{}条件
			if (hasId) {
				request.put(idKey, id);
			}
			request.put(idInKey, idIn);
			//关键词
			request.put(KEY_DATABASE, database);
			request.put(KEY_ROLE, role);
			request.put(KEY_EXPLAIN, explain);
			request.put(KEY_CACHE, cache);
			request.put(KEY_DATASOURCE, datasource);
			request.put(KEY_SCHEMA, schema);
			request.put(KEY_COMBINE, combine);
			request.put(KEY_FROM, from);
			request.put(KEY_COLUMN, column);
			request.put(KEY_GROUP, group);
			request.put(KEY_HAVING, having);
			request.put(KEY_ORDER, order);
			request.put(KEY_RAW, raw);
			request.put(KEY_JSON, json);
		}

自此return config;

 方法SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List<Join> joinList, boolean isProcedure, Callback callback) throws Exception {}结束

3268

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值