本篇文章主要来说明下代码模块的设计。像我们这种菜鸟级别,只有平时多读读源码,多研究和探讨其中的设计才可能提升自己,写出高质量的代码。
没有最好的设计,只有更好的设计,所以在发表我自己的愚见的同时,希望小伙伴们相互探讨更好的设计,有探讨才有更大的进步。
我们维护了一个数据中心,对外提供查询api,如何能让用户随意的添加查询条件,而不用修改后台的查询代码呢?用户如何配置查询条件,从而达到如下的sql效果呢?:
我们作为api设计者,该如何让用户方便的传递他们任意的查询需求呢?这是我们要思考的地方。
目前来看比较好的方式莫过于:用户通过json来表达他们的查询需求。
从上面的查询来看,我们可以总结出来查询条件无非就是某个字段满足什么样的条件。这里有三个对象:
查询的字段 如 b.age
条件值 如 12
怎样满足条件值 如 >
这样我们就可以清晰明了了,一个查询条件无非就是三个内容,所以可以如下配置:
很显然,上面的确很麻烦,我们无非是要表达这三个内容,所以就要简化配置:
还是不够简化,如何把操作符 > 也放置进去呢?如下
这样我们就可以把三个对象表达清楚了,将查询的字段和操作符合并起来作为key,并使用分隔符@分割两者,条件值作为value。这样就做到了,非常简化的查询配置。
接下来又面临一个问题,如何表达查询条件之间的and or 关系呢?即如何表达下面的内容呢?
借鉴mongodb的查询方案,可以如下配置:
通过配置一个$or作为key表明里面的几个查询条件是or的关系,如果是$and表明里面的查询条件之间是and的关系,外层默认是and的关系。
同时我们再回顾下,mongodb所作出的查询设计,也是通过用户配置json形式来表达查询意图,但是我们来看下它是如何查询
虽然看似我们的更加简单,mongodb的更加繁琐,主要是mongodb认为对于一个字段,可以有多个查询条件的,为了支持更加复杂的查询,如下:
各有各的好处和缺点,我就是我,颜色不一样的烟火。哈哈。。。
对题目进行分析完了之后,就要考虑如何实现这样的json配置到sql的转化。实现起来不难,最重要的是如何做出一个高扩展性的实现?
再来看下下面的例子:
其实就是针对每个自定义的操作符进行相应的处理,所以就有了解析器接口:
其中sqlparamsparseitemresult,则是把解析后的结果分别存储起来,而不是直接拼接成一个字符串,主要为了直接拼接字符串式的sql注入,它的内容如下:
上面的key oper value 则是解析后的内容。下面举例说明
以"b.age@>“:12 为例,其中getparams方法中的 key就是b.age, value就是12, oper就是> 而这个方法的返回的字符串结果为:
以"c.id@in”:[12,13,14]为例,其中getparams方法中的 key就是c.id,value就是一个list集合,oper就是in ,这个方法的返回结果为:
以"d.time@time>“:“2015-3-1"为例,其中getparams方法中的 key就是c.id,value就是一个list集合,oper就是in,这个方法的返回结果为:
解析器有很多相同的地方,这就需要我们进行抽象,抽出共性部分,留给子类去实现不同的部分。所以有了抽象类abstractsqlparamsparser
有哪些共性部分和非共性部分呢?
共性部分: 就是support方法。每个解析器支持某几种操作符,所以判断该解析器是否支持当前的操作符的逻辑是共同的,所以如下:
opers属性表示当前解析器所支持的所有操作符。ignorecase表示在匹配操作符的时候是否忽略大小写。这两个属性都设置成private,然后对子类开放了protected类型的set方法,用于子类来设置这两个属性。
非共性部分:留出了doparams方法供子类来具体实现
目前内置了几个常用的解析器实现,类图如下:
以timesqlparamsparser为例来简单说明下:
它主要是用于解析如下形式的:
最终想达到的效果是:
它的解析过程如下:
解析过程其实就是对key value oper 进行了不同程度的转换。
同时timesqlparamsparser还支持其他时间形式的解析,如"2015-3-1 12:23:12”,只需如下方式创建一个解析器:
然后他就能够解析下面的形式:
同时又能保留原有的形式,两者互不干扰。
有了解析器的一系列实现,下面就需要一个综合的类来使用这些解析器。这就是defaultsqlparamshandler:
内部已经注册了几个解析器。同时需要对外留出注册自定义解析器的方法:
这个过程不仅需要使用已经注册的解析器来解析,还包含对解析条件之间的and or 关系的递归处理。代码如下,不再详细说明:
这里进行了递归调用,主要用于处理 $and $or 的嵌套查询,getsqlwhereparamsresultbyandor可能内部调用了processmodelsqlwhereparams,processmodelsqlwhereparams内部又调用了getsqlwhereparamsresultbyandor
这里就是使用解析器进行解析的过程,先遍历每个解析器是否支持当前的操作符,如果支持则进行相应的解析
这里面的@ $or 以及 $and 都是可以自己设定的,默认值是上述形式。