文档章节

在 flying 中实现或逻辑的思考

limeng32
 limeng32
发布于 2017/12/25 17:38
字数 2350
阅读 64
收藏 2
点赞 0
评论 0

关于或逻辑的思考  

     本篇文章我们来探讨如何使用 flying 的方式来描述带有 ”or” 关键字的 sql 语句(如果您对 flying 还不了解,请参见 https://www.oschina.net/p/flying)。一直以来,flying力求做到的就是,把每一次与数据库交互都变为对象交互,而不是字符串交互,因为对象相比字符串至少有以下好处:

  • 对象是完全解析的。比如我有一个子容器向父容器发送查询请求,然后按照业务,父容器要在这个查询请求上修改一个条件再追加一个条件。使用查询对象可以轻松做到这一点,因为它能被完全解析;但解析字符串就很麻烦了(试想一下拆分一个充满了 and 和 or 的 sql),估计只有数据库厂商才能提供可靠的工具。
  • 不同数据库的 sql 语法有区别,但它们的查询对象相同。

  • 对象可以跨语言,可以以 json 方式传输保存。

 为了做到以上这些点,作为条件的查询对象必须具有以下特点:

  1. 各条件变量的赋值顺序无关。
  2. 易于理解和修改。
  3. 各条件变量是“且”的关系。

       第一个特性很好理解,例如 personCondition.setNameLike(“张”); 和 personCondition.setAge(30); 就是顺序无关的,flying查询对象目前所有的条件赋值语句(包括判断条件、分页条件、排序条件)都是顺序无关的。 

       第二个特性是,用户一眼看到某个变量赋值语句就知道它的作用是什么,并可以按需要进行修改。

       第三个特性是,所有的条件变量其实都是用与逻辑“and”相连的。

       看到这里,大家会发现,其实 flying 查询对象只解决了一半的问题,因为对于或操作 “or”,以前根本就没有提及,而没提及的原因是,在满足以上三点的基础上解决或逻辑比较困难。而本文则尝试解决这一问题。

       首先,我们抛出一个足够复杂的sql语句:

select person.id, person.name, person.age, person.level from person 
    where (person.name like ‘张%’ and person.age = 25) 
    or (person.age = 27 and person.level = ‘B’) 
    or (person.name like ‘李%’ and person.level = ‘A’)

       这个复杂的sql语句如何用一个查询对象表示呢?这里我们需要使用一些数学工具,首先我们用逻辑变量来代替条件表达式:

A = "person.name like '张%'"

B = "person.age = '25'"

C = "person.age = '27'"

D = "person.level = 'B'"

E = "person.name like '李%'"

F = "person.level = 'A'"

       这样一来以上这个逻辑表达式就简化为:(A∩B)∪( C∩D)∪( E∩F)

       可是这样无法解决问题,因为 flying 擅长解决的是以“且”关系连接的条件,例如 X∩Y∩Z 这样,而以上表达式明显不是这样。

       但是布尔逻辑运算具有以下性质:交换律、结合律与分配律。

交换律:A∩B = B∩A

同理 A∪B = B∪A

结合律:A∩(B∩C) = A∩B∩C

同理 A∪(B∪C)= A∪B∪C

分配律:(A∩B)∪C = (A∪C)∩(B∪C)

同理(A∪B)∩C = (A∩C)∪(B∩C)

       有了这三个定律之后,我们就可以把(A∩B)∪( C∩D)∪( E∩F)变形为一连串布尔变量以“∩”相连的形式:

   (A∩B)∪(C∩D)∪( E∩F)

= (((A∩B)∪C)∩((A∩B)∪D)))∪( E∩F)

= (((A∪C)∩(B∪C))∩((A∪D)∩(B∪D)))∪( E∩F)

= ((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪(E∩F)

= (((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪E)∩(((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪F)

= (A∪C∪E)∩(B∪C∪E)∩(A∪D∪E)∩(B∪D∪E)∩(A∪C∪F)∩(B∪C∪F)∩(A∪D∪F)∩(B∪D∪F)

       最后的这个形式看起来是我们用 flying 能描述的了的。实际上,对于布尔运算式有以下定理:

任何一个布尔表达式都能被转换为一个等价的合取范式(CNF),合取范式格式为:C1∩C2∩……Cn;其中,Ck(1<=k<=n)称为合取项,每个合取项是不包含∩的表达式。 

       这个归并是关系型数据库自己也会做的,因为它具有以下好处: 

  1. 合取项只要有一个为假,整个表达式就为假,故代码中可以在发现一个合取项为假时,即停止其他合取项的判断,加快判断速度,如:WHERE(0 > 1 AND s1 = 5)
  2. 因为AND操作符是可交换的,所以优化器可以按照先易后难的顺序计算表达式,一旦发现一个合取项为假时,即停止其他合取项的判断,加快判断速度。
  3. 如果一个合取项上存在索引,则先判断索引是否可用,如能利用索引快速得出合取项的值,则能加快判断速度。如:WHERE (A.a> 100 AND A.b = 5 AND... )

    情况1:A表的a列上存在索引,b列无索引,则利用a上的索引找出元组,“A.b = 5” 作为过滤条件使用;情况2:A表的a列上不存在索引,b列有索引,则利用b上的索引找出元组,“A.a> 100” 作为过滤条件使用。

       所以,相对于(A∩B)∪( C∩D)∪( E∩F),我们将(A∪C∪E)∩(B∪C∪E)∩(A∪D∪E)∩(B∪D∪E)∩(A∪C∪F)∩(B∪C∪F)∩(A∪D∪F)∩(B∪D∪F)传给数据库,并不会增加它的查询时间,因为它原本也需要归并。 

       那么接下来的问题就变成,我们如何用代码描述(A∪C∪E),或者更具体地说,如何用代码描述:"person.name like '张%' or person.age = 27 or person.name like '李%'" 这样一个查询条件,这个解决了其它查询条件同理也就都解决了。

       在这里,flying新增了Or标签类(见https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/Or.java),这个标签的内容是ConditionMapperAnnotation标签的数组,所以在查询条件类中可以有如下标签代码:

@Or({
  @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike),
  @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal), 
  @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike) 
})

       同时为了赋值方便,我们强烈建议采用不定参数的Object[]作为变量,于是整个代码变成了:

@Or({
  @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike),
  @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal), 
  @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike) 
})
private Object[] condition1;

public Object[] getCondition1 () {
	return condition1;
}
public void setCondition1 (Object... condition1) {
	this. condition1 = condition1;
}

       我们描述 "person.name like '张%' or person.age = 27 or person.name like '李%' "的代码变为:

personCondition.setCondition1("张", 27, "李");
/* 注意参数顺序和 condition1 上 @ConditionMapperOrAnnotation 的内部顺序一致 */

       于是问题就全解决了。您也许会觉得这个解决方案过于复杂,但对于(A∩B)∪( C∩D)∪( E∩F)来说,用其它代码方式描述一样复杂(纯sql除外,但我们知道使用查询对象代替 sql 的好处)。

       接下来我们再给出一些常见一点的使用或逻辑的场景,例如我想选择所有30岁以下或50岁以上的人员:

@Or({
	@ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.LessThan),
	@ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.GreaterThan)
})
private Object[] ageFilter;
public Object[] getAgeFilter () {
	return ageFilter;
}
public void setAgeFilter (Object... ageFilter) {
	this. ageFilter = ageFilter;
}

personCondition.setAgeFilter(30,50);

       或者我们找所有姓张或者姓李的人:

@Or({
	@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike),
	@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike)})
private Object[] nameFilter;
/* 相关getter和setter请自行添加 */

personCondition.setAgeFilter("张", "李");

       或者我们找年龄在 40 以上或者 level 为 A 的人:

@Or({
	@ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.GreaterThan),
	@ConditionMapperAnnotation(dbFieldName = "level", conditionType = ConditionType.Equals)})
private Object[] filter;
/* 相关getter和setter请自行添加 */

personCondition.setFilter(40, "A");

       是不是使用起来还是挺简单的?flying的设计哲学是“使您写下的每一行代码的回报率最大化”,当您的项目变得越来越庞大时,您会越来越明显感受到这一点。

自定义主键生成器

       flying-初雪另一个特色是增加了自定义主键生成器,为此我们在flying:insert语句中新增了括号元素,比如:

flying:insert(uuid)                            使用标准uuid作主键

flying:insert(uuid_no_line)              使用无下横线的uuid作主键

flying:insert(millisecond)                使用毫秒数作主键(需保证每秒并发在1000以下)

       以上这些可以在 https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/statics/KeyGeneratorType.java 看到,当然更多的情况是您会自定义自己的主键生成器,只要您的主键生成器实现了 flying 中的 indi.mybatis.flying.type.KeyHandler 接口即可,比如这样调用一个自定义的主键生成器类:

flying:insert(indi.mybatis.flying.handlers.MySnowFlakeKeyHandler)

       (上面的 indi.mybatis.flying.handlers.MySnowFlakeKeyHandler 是一个雪花主键生成器的 java 版本实现。雪花主键生成器由 tweeter 发明用于处理大规模并行写入,主键采用 float 类型存储以节省资源,自带递增无需 order by,单台主机每秒可产生 400 万个不同主键,最多可 1024 台主机集群同时工作)

       或者您有某几个表的主键要共享一个连续数列的需求(比如工作流),就可以开发自己的主键生成器。

总结

  • 本文是本人开源项目 flying (地址见 https://www.oschina.net/p/flying)在开发最新版本时需要用到的理论基础之二。
  • flying 解决或逻辑问题的代码实现还没有写完,但理论已经完全梳理清楚,可以说完成了90%。
  • 这段时间除了flying外同时进行两个大项目,整个人感觉像脱了一层皮,我不能说太多,只能透露其中一个和熬夜洗尿布有关。
  • 最后请您静待 flying-初雪 降临。

© 著作权归作者所有

共有 人打赏支持
limeng32
粉丝 2
博文 4
码字总数 8406
作品 1
东城
高级程序员
mybatis 插件组 flying-阳春 发布

flying 是一个可以极大增加 mybatis 开发速度的插件组,它提供了一种全新的操作数据的方式,目前更新到“阳春”版本。 mybatis 版本与 flying-阳春 的对应关系见下: mybatis 版本 flying 版...

limeng32
04/09
0
0
flying-初雪 发布,mybatis 插件组

flying 是一个可以极大增加 mybatis 开发速度的插件组,它提供了一种全新的操作数据的方式,希望能对您有所帮助。 众所周知,mybatis 虽然易于上手,但放到互联网环境下使用时,不可避免的要...

limeng32
01/22
0
0
limeng32/mybatis.flying

mybatis.flying 项目介绍请见 flying-doc.limeng32.com ,国内代码托管网站请见 gitee.com/limeng32/mybatis.flying,我们为开发最好的 mybatis 插件而努力。 flying 是一个可以极大增加 my...

limeng32
2017/10/19
0
0
MyBatis 的扩展插件--flying

flying 是一个可以极大增加 mybatis 开发速度的插件组,它提供了一种全新的操作数据的方式,希望能对您有所帮助。 众所周知,mybatis 虽然易于上手,但放到互联网环境下使用时,不可避免的要...

limeng32
2017/10/10
445
2
flying 0.9.2 发布,mybatis 插件组

flying 是一个可以极大增加 mybatis 开发速度的插件组,它提供了一种全新的操作数据的方式,希望能对您有所帮助。 众所周知,mybatis 虽然易于上手,但放到互联网环境下使用时,不可避免的要...

limeng32
2017/10/28
777
1
SpringBoot 自动代码生成三层

前言 虽然mybatis已经有了代码生成,但是对于SpringBoot 项目来说生成的还是需要改动,而且也没得逻辑层,和控制层。但是这些东西是逃避不了,所以我就针对单表,做了一个代码生成器。 myba...

边鹏_尛爺鑫
07/10
0
1
Java世界的HTML转PDF调研

背景: 尝试过的几个方案如下: 1、使用HtmlCleaner 2、使用flying sauser flying saucer 使用中的一些问题 (java导出pdf) flying saucer(源代码托管在github https://github.com/flyingsau...

醉公子
2016/06/22
391
2
UE4移动组件详解(一)——移动框架与实现原理

前言 关于UE4的移动组件,我写了一篇非常详细的分析文档。由于篇幅比较大,我将其拆分成三个部分。分别从移动框架与实现原理,移动的网络同步,移动组件的优化与改造三个方面来写。这三篇文档...

sinat_28941165
04/11
0
0
Groovy 2.3 trait语法介绍

Groovy 2.3 trait语法介绍 http://www.javacodegeeks.com/2014/04/groovy-2-3-introduces-traits.html 前几天,Groovy 2.3的第二个beta版本发布。Groovy 2.3最重要的新特性之一是trait语法。...

翟志军
2014/04/29
0
4
Android GestureDetector在flying监听事件中实现对当前页面的图片的刷新,怎么加上点特效呢?没有使用

Android GestureDetector在flying监听事件中实现对当前页面的图片的刷新,怎么加上点特效呢?没有使用ViewFlipper?

淡淡滴小白
2012/07/18
435
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

spring-@RequestBody

@RequestMapping("/login")    public void login(@RequestBody String userName,@RequestBody String pwd){      System.out.println(userName+" :"+pwd);    }    ......

说回答
3分钟前
0
0
Redis安装

大家可以通过该链接获取安装详情(这是一个Word文档,支持下载): http://note.youdao.com/noteshare?id=7a327ed6c58fb2037ba537e58ecf7510&sub=480DB8EF349747C3983B73AE94D45BB1 其他参考...

一梦心草
4分钟前
0
0
MySQL按天,按周,按月,按时间段统计【转载】

https://blog.csdn.net/qq_28056641/article/details/78306870 select DATE_FORMAT(create_time,'%Y%m%d') days,count(caseid) count from tc_case group by days; select DATE_FORMAT(creat......

李道福
6分钟前
0
0
浅谈parallelStream

parallelStream是什么,它是一个集合的并发处理流.其作用是把一个集合中的数据分片,进行一个多线程的处理,增快运行速度. 比如说这样一段代码 private Set<SysRole> sysRoles;private Set<St...

算法之名
7分钟前
2
0
器者,道之所载

形而上者谓之道,形而下者谓之器,化而裁之谓之变;推而行之谓之通,举而措之天下之民,谓之事业。—— 《道德经》

了凡川
9分钟前
0
0
C#命名规范中文版/C#编码规范中文版

最新文档地址https://github.com/hiramtan/CSharpNamingGuidelines_Chinese C#命名规范中文版/C#编码规范中文版 示例 /*****************************************************************......

海贝Hibey
10分钟前
0
0
刚从eclipse转到Intellij IDEA,分享一些配置经验

刚从eclipse转到Intellij IDEA,分享一些配置经验,IntelliJ IDEA作为最好的Java开发工具,在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、 创新的GUI设计等...

舒文joven
12分钟前
1
0
lombok 引入后,测试类始终找不到get,set方法。

开发环境为idea,jdk1.7,maven3.5. 网上直接搜出来的方法有: 1、在setting里安装lombok的plugins; 2、如下图,勾选enable annocation processing选项 3、升级maven plugins插件 我尝试了以...

Kidult
18分钟前
0
0
Duang,HUAWEI DevEco IDE全面升级啦

想感受全新UI带来的视觉及交互体验、 HiKey970开发板调测、 HiAI API推荐和收藏、 深度AI模型分析等新功能, 体验高清晰度和流畅度的远程AI真机调测吗? 全新的UI设计 采用最优秀的视觉及交互...

华为终端开放实验室
26分钟前
1
0
阻止事件冒泡,阻止默认事件

1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开, 2.event....

闫亚亚
28分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部