发现要坚持写博客真的是一件很困难的事情,各种原因都会导致顾不上博客。本来打算写自己动手实现orm,看看时间,还是先实现一个动态sql,下次有时间再补上orm完整的实现吧。
用过mybatis的人,估计对动态sql都不陌生,如果没有用过,就当看看热闹吧。我第一次接触mysql是在大四的时候,当时就觉得动态sql这东西很牛,很灵活,一直想搞明白怎么实现的,尽管当时已经能够写ioc,mvc和简单的orm框架(仿mybaits但是没有动态sql部分),但是仍然找不到mybatis核心的动态sql到底在哪实现的,怎么实现的,可能是那些代码太绕根本没法看懂,直到目前,我都没有勇气去看mybatis的动态sql部分,大概是天生对算法有莫名其妙的敬畏吧。
几年前因为想做一个配置平台,想用解析型语言替代java的实现,可以让配置人员在页面上方便的编写少量代码实现复杂的业务逻辑(包括数据库操作)。当时java已经有js解析引擎,但是大多数人都说效率太低,不知道我发什么疯就想到自己实现一个解析语言。不过实现自己的语言也是我一直的梦想,解析语言相对编译型语言入手简单,于是我就果断动手了,写完才知道,其实自己的实现估计还没有当时的js引擎效率高,那时的我真的是很年轻很简单。今天谈到的动态sql实现其实就是受到那时候解析语言的启发。
废话不多说直接开始聊动态sql,请看下面例子,先声明这里的例子并不是一个正确的sql的写法,只是想写一个尽量复杂的嵌套结构,如果把这种复杂的情况实现了,那么简单一点的就更加不在话下了。
| 
								1
 
								2
 
								3
 
								4
 
								5
 
								6
 
								7
 
								8
 
								9
 
								10
 
								11
 
								12
 
								13
 
								14
 
								15
						 | delete from pl_pagewidget<iftest="widgetcodes != null">where pagewidgetcode in<foreachcollection="widgetcodes"item="item"index="index"open="("separator=","close=")"><iftest="index == 0">#{item}</if><foreachcollection="bs"item="b"index="index1"open="("separator=","close=")">#{b}</foreach></foreach></if><iftest="a != null">and a = #{a}</if> | 
要实现解析出上面例子的sql,首先一个难点类似是test属性里的条件怎么判断真假,不过这个难点在struts2中学到的ognl表达式面前就比较小儿科了。不知道有么有朋友遇到过一个比较奇葩的现象,就是有时候明明在mybatis动态sql中写如下表达式,但是当n=0的时候居然是满足条件的也就是test里的值是false,0居然不能满足这个表达式的条件,这里就是ognl库的原因了。没办法它就是这么玩的,当成特殊情况记住就可以了
| 
								1
						 | test="n != null and n !=''" | 
ognl表达式使用很方便如下
| 
								1
 
								2
 
								3
 
								4
 
								5
 
								6
 
								7
 
								8
 
								9
 
								10
 
								11
 
								12
						 | import java.util.HashMap;import java.util.Map;import ognl.Ognl;public class OgnlTest {//输出结果:falsepublic static void main(String[] args) throws Exception {String con1 = "n != null and n != ''";Map<String,Object> root = new HashMap<>();root.put("n", 0);System.out.println(Ognl.getValue(con1,root));}} | 
要实现解析上面例子的sql,第二个难点就是虽然这个sql披上一层xml的皮就是一个标准的sql,如下
| 
								1
 
								2
 
								3
 
								4
 
								5
 
								6
 
								7
 
								8
 
								9
 
								10
 
								11
 
								12
 
								13
 
								14
 
								15
 
								16
 
								17
						 | <sql>delete from pl_pagewidget<iftest="widgetcodes != null">where pagewidgetcode in<foreachcollection="widgetcodes"item="item"index="index"open="("separator=","close=")"><iftest="index == 0">#{item}</if><foreachcollection="bs"item="b"index="index1"open="("separator=","close=")">#{b}</foreach></foreach></if><iftest="a != null">and a = #{a}</if></sql> | 
但是要解析上面的xml和我们平时不一样,这个xml是标签和文本混合的,正常我们开发中应该很少会用到解析这种xml。不过我们常用的解析xml的工具dom4j其实可以很好的解析这种sql,只不过很少可能用到。Element类的content()方法就可以返回一个Node的集合,再通过遍历这个集合,判断每个Node的类型就可以了。解决了这两个重点,只需要加上一点技巧就可以解析这种动态sql了。
我用到的技巧是根据java语法格式得到的启发。比如java中有局部变量和全局变量,不考虑引用传递这种情况,如果全局变量int i = 1;方法里面传入这个全局变量,然后在方法里面修改,在方法里面看到的是改变后的值,但是在方法外面看到的仍然是1。这个现象其实学过java应该都知道。还有就是当方法调用的时候,方法里面可以看到全局变量,也可以看到局部变量,方法调用结束后局部变量会被清空释放(看垃圾搜集器高兴)。介绍了这些直接上代码了
| 
								1
 
								2
 
								3
 
								4
 
								5
 
								6
 
								7
 
								8
 
								9
 
								10
 
								11
 
								12
 
								13
 
								14
 
								15
 
								16
 
								17
 
								18
 
								19
 
								20
 
								21
 
								22
 
								23
 
								24
 
								25
 
								26
 
								27
 
								28
 
								29
 
								30
 
								31
 
								32
 
								33
 
								34
 
								35
 
								36
 
								37
 
								38
 
								39
 
								40
 
								41
 
								42
 
								43
 
								44
 
								45
 
								46
 
								47
 
								48
 
								49
 
								50
 
								51
 
								52
 
								53
 
								54
 
								55
 
								56
 
								57
 
								58
 
								59
 
								60
 
								61
 
								62
 
								63
 
								64
 
								65
 
								66
 
								67
 
								68
 
								69
 
								70
 
								71
 
								72
 
								73
 
								74
 
								75
 
								76
 
								77
 
								78
 
								79
 
								80
 
								81
 
								82
 
								83
 
								84
 
								85
 
								86
 
								87
 
								88
 
								89
 
								90
 
								91
 
								92
 
								93
 
								94
 
								95
 
								96
 
								97
 
								98
 
								99
 
								100
 
								101
 
								102
 
								103
 
								104
 
								105
 
								106
 
								107
 
								108
 
								109
 
								110
 
								111
 
								112
 
								113
 
								114
 
								115
 
								116
 
								117
 
								118
 
								119
 
								120
 
								121
 
								122
 
								123
 
								124
 
								125
 
								126
 
								127
 
								128
 
								129
 
								130
 
								131
 
								132
 
								133
 
								134
 
								135
 
								136
 
								137
 
								138
 
								139
 
								140
 
								141
 
								142
 
								143
 
								144
 
								145
 
								146
 
								147
 
								148
 
								149
 
								150
 
								151
 
								152
 
								153
 
								154
 
								155
 
								156
 
								157
 
								158
 
								159
 
								160
 
								161
 
								162
 
								163
 
								164
 
								165
 
								166
 
								167
 
								168
 
								169
 
								170
 
								171
 
								172
 
								173
 
								174
 
								175
 
								176
 
								177
 
								178
 
								179
 
								180
 
								181
 
								182
 
								183
 
								184
 
								185
 
								186
 
								187
 
								188
 
								189
 
								190
 
								191
 
								192
 
								193
 
								194
 
								195
 
								196
 
								197
 
								198
 
								199
 
								200
 
								201
 
								202
 
								203
 
								204
 
								205
 
								206
 
								207
 
								208
 
								209
 
								210
 
								211
 
								212
 
								213
 
								214
 | 
 
        