Fun论设计模式之4:标准模式(Criteria Pattern)与MyBatis的Example原理
标准模式,又叫过滤器模式(Filter Pattern),这个设计模式在我们常用的工具里面会大量体现,尤其是在数据处理方面,但我们却很难发现。
意图:允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。
主要解决:对象运算过程的直觉化。
何时使用:当您想让对象本身进行运算,并且方法可串一起运行。
如何解决:使用的方法在对象内部进行运算,改变内部数据,同时返回参与计算的对象本身,方便下一次引用计算。
关键代码:对象内部运算改变结果,返回对象引用本身。
Mybatis和hibernates如果要用Example直接生成一条SQL语句,中间肯定会使用createCriteria()和andNotEqualTo()之类的函数,前者还有类似于把不同criteria的条件进行并集(or())或交集(and(),createCriteria())操作;后者还可以是andIn()之类的子条件,组合在一起,成为一个criteria。
比如说使用tk.mybatis里面的包,建立查询语句:new Example(Student.class).createCriteria().andEqualTo("studentId",3303).andIn("classId",{2,4,40}).or().andEqualTo("name","funcfans"),这里实际上可以分成以下几条语句:
Example example = new Example(Student.class);
example.createCriteria().andEqualTo("studentId",3303).andIn("classId",{2,4,40});
example.or().andEqualTo("name","funcfans")
组合成这样的SQL语句:select * from student where (studentId=3303 and classId in (2,4,40) ) or (name='funcfans')
这里就可以看出来,这个条件语句的拼接,或者说对数据的过滤流,就属于上面说的过滤器模式。example传入criteria过滤数据流,criteria传入各种数据和条件过滤数据流。(主要解决)
这里摘取部分Example代码:
1 package tk.mybatis.mapper.entity;
2
3 import org.apache.ibatis.reflection.MetaObject;
4 import org.apache.ibatis.reflection.SystemMetaObject;
5 import org.apache.ibatis.type.TypeHandler;
6 import tk.mybatis.mapper.MapperException;
7 import tk.mybatis.mapper.mapperhelper.EntityHelper;
8 import tk.mybatis.mapper.util.Sqls;
9 import tk.mybatis.mapper.util.StringUtil;
10
11 import java.util.*;
12
13 /**
14 * 通用的Example查询对象
15 *
16 * @author liuzh
17 */
18 public class Example implements IDynamicTableName {
19 protected String orderByClause;
20
21 protected boolean distinct;
22
23 protected boolean exists;
24
25 protected boolean notNull;
26
27 protected boolean forUpdate;
28
29 //查询字段
30 protected Set<String> selectColumns;
31
32 //排除的查询字段
33 protected Set<String> excludeColumns;
34
35 protected String countColumn;
36
37 protected List<Criteria> oredCriteria;
38
39 protected Class<?> entityClass;
40
41 protected EntityTable table;
42 //属性和列对应
43 protected Map<String, EntityColumn> propertyMap;
44 //动态表名
45 protected String tableName;
46
47 protected OrderBy ORDERBY;
48
49 /**
50 * 默认exists为true
51 *
52 * @param entityClass
53 */
54 public Example(Class<?> entityClass) {
55 this(entityClass, true);
56 }
57
58 /**
59 * 带exists参数的构造方法,默认notNull为false,允许为空
60 *
61 * @param entityClass
62 * @param exists - true时,如果字段不存在就抛出异常,false时,如果不存在就不使用该字段的条件
63 */
64 public Example(Class<?> entityClass, boolean exists) {
65 this(entityClass, exists, false);
66 }
67
68 /**
69 * 带exists参数的构造方法
70 *
71 * @param entityClass
72 * @param exists - true时,如果字段不存在就抛出异常,false时,如果不存在就不使用该字段的条件
73 * @param notNull - true时,如果值为空,就会抛出异常,false时,如果为空就不使用该字段的条件
74 */
75 public Example(Class<?> entityClass, boolean exists, boolean notNull) {
76 this.exists = exists;
77 this.notNull = notNull;
78 oredCriteria = new ArrayList<Criteria>();
79 this.entityClass = entityClass;
80 table = EntityHelper.getEntityTable(entityClass);
81 propertyMap = table.getPropertyMap();
82 this.ORDERBY = new OrderBy(this, propertyMap);
83 }
84
85 public Criteria or() {
86 Criteria criteria = createCriteriaInternal();
87 criteria.setAndOr("or");
88 oredCriteria.add(criteria);
89 return criteria;
90 }
91
92 public Criteria createCriteria() {
93 Criteria criteria = createCriteriaInternal();
94 if (oredCriteria.size() == 0) {
95 criteria.setAndOr("and");
96 oredCriteria.add(criteria);
97 }
98 return criteria;
99 }
100
101 protected Criteria createCriteriaInternal() {
102 Criteria criteria = new Criteria(propertyMap, exists, notNull);
103 return criteria;
104 }
105
106 }
以及部分Criteria代码:
1 protected abstract static class GeneratedCriteria {
2 protected List<Criterion> criteria;
3 //字段是否必须存在
4 protected boolean exists;
5 //值是否不能为空
6 protected boolean notNull;
7 //连接条件
8 protected String andOr;
9 //属性和列对应
10 protected Map<String, EntityColumn> propertyMap;
11
12 protected GeneratedCriteria(Map<String, EntityColumn> propertyMap, boolean exists, boolean notNull) {
13 super();
14 this.exists = exists;
15 this.notNull = notNull;
16 criteria = new ArrayList<Criterion>();
17 this.propertyMap = propertyMap;
18 }
19
20 protected void addCriterion(String condition) {
21 if (condition == null) {
22 throw new MapperException("Value for condition cannot be null");
23 }
24 if (condition.startsWith("null")) {
25 return;
26 }
27 criteria.add(new Criterion(condition));
28 }
29
30 protected void addCriterion(String condition, Object value, String property) {
31 if (value == null) {
32 if (notNull) {
33 throw new MapperException("Value for " + property + " cannot be null");
34 } else {
35 return;
36 }
37 }
38 if (property == null) {
39 return;
40 }
41 criteria.add(new Criterion(condition, value));
42 }
43
44 protected void addCriterion(String condition, Object value1, Object value2, String property) {
45 if (value1 == null || value2 == null) {
46 if (notNull) {
47 throw new MapperException("Between values for " + property + " cannot be null");
48 } else {
49 return;
50 }
51 }
52 if (property == null) {
53 return;
54 }
55 criteria.add(new Criterion(condition, value1, value2));
56 }
57
58 protected void addOrCriterion(String condition) {
59 if (condition == null) {
60 throw new MapperException("Value for condition cannot be null");
61 }
62 if (condition.startsWith("null")) {
63 return;
64 }
65 criteria.add(new Criterion(condition, true));
66 }
67
68 protected void addOrCriterion(String condition, Object value, String property) {
69 if (value == null) {
70 if (notNull) {
71 throw new MapperException("Value for " + property + " cannot be null");
72 } else {
73 return;
74 }
75 }
76 if (property == null) {
77 return;
78 }
79 criteria.add(new Criterion(condition, value, true));
80 }
81
82 protected void addOrCriterion(String condition, Object value1, Object value2, String property) {
83 if (value1 == null || value2 == null) {
84 if (notNull) {
85 throw new MapperException("Between values for " + property + " cannot be null");
86 } else {
87 return;
88 }
89 }
90 if (property == null) {
91 return;
92 }
93 criteria.add(new Criterion(condition, value1, value2, true));
94 }
95
96 public Criteria andIsNull(String property) {
97 addCriterion(column(property) + " is null");
98 return (Criteria) this;
99 }
100
101 public Criteria andIsNotNull(String property) {
102 addCriterion(column(property) + " is not null");
103 return (Criteria) this;
104 }
105
106 public Criteria andEqualTo(String property, Object value) {
107 addCriterion(column(property) + " =", value, property(property));
108 return (Criteria) this;
109 }
110
111 public Criteria andNotEqualTo(String property, Object value) {
112 addCriterion(column(property) + " <>", value, property(property));
113 return (Criteria) this;
114 }
115
116 public Criteria andGreaterThan(String property, Object value) {
117 addCriterion(column(property) + " >", value, property(property));
118 return (Criteria) this;
119 }
120
121 public Criteria andGreaterThanOrEqualTo(String property, Object value) {
122 addCriterion(column(property) + " >=", value, property(property));
123 return (Criteria) this;
124 }
125
126 public Criteria andLessThan(String property, Object value) {
127 addCriterion(column(property) + " <", value, property(property));
128 return (Criteria) this;
129 }
130
131 public Criteria andLessThanOrEqualTo(String property, Object value) {
132 addCriterion(column(property) + " <=", value, property(property));
133 return (Criteria) this;
134 }
135
136 public Criteria andIn(String property, Iterable values) {
137 addCriterion(column(property) + " in", values, property(property));
138 return (Criteria) this;
139 }
140
141 public Criteria andNotIn(String property, Iterable values) {
142 addCriterion(column(property) + " not in", values, property(property));
143 return (Criteria) this;
144 }
145
146 public Criteria andBetween(String property, Object value1, Object value2) {
147 addCriterion(column(property) + " between", value1, value2, property(property));
148 return (Criteria) this;
149 }
150
151 public Criteria andNotBetween(String property, Object value1, Object value2) {
152 addCriterion(column(property) + " not between", value1, value2, property(property));
153 return (Criteria) this;
154 }
155
156 public Criteria andLike(String property, String value) {
157 addCriterion(column(property) + " like", value, property(property));
158 return (Criteria) this;
159 }
160
161 public Criteria andNotLike(String property, String value) {
162 addCriterion(column(property) + " not like", value, property(property));
163 return (Criteria) this;
164 }
165
166 /**
167 * 手写条件
168 *
169 * @param condition 例如 "length(countryname)<5"
170 * @return
171 */
172 public Criteria andCondition(String condition) {
173 addCriterion(condition);
174 return (Criteria) this;
175 }
176
177 /**
178 * 手写左边条件,右边用value值
179 *
180 * @param condition 例如 "length(countryname)="
181 * @param value 例如 5
182 * @return
183 */
184 public Criteria andCondition(String condition, Object value) {
185 criteria.add(new Criterion(condition, value));
186 return (Criteria) this;
187 }
188
189 /**
190 * 手写左边条件,右边用value值
191 *
192 * @param condition 例如 "length(countryname)="
193 * @param value 例如 5
194 * @param typeHandler 类型处理
195 * @return
196 * @deprecated 由于typeHandler起不到作用,该方法会在4.x版本去掉
197 */
198 @Deprecated
199 public Criteria andCondition(String condition, Object value, String typeHandler) {
200 criteria.add(new Criterion(condition, value, typeHandler));
201 return (Criteria) this;
202 }
203
204 /**
205 * 手写左边条件,右边用value值
206 *
207 * @param condition 例如 "length(countryname)="
208 * @param value 例如 5
209 * @param typeHandler 类型处理
210 * @return
211 * @deprecated 由于typeHandler起不到作用,该方法会在4.x版本去掉
212 */
213 @Deprecated
214 public Criteria andCondition(String condition, Object value, Class<? extends TypeHandler> typeHandler) {
215 criteria.add(new Criterion(condition, value, typeHandler.getCanonicalName()));
216 return (Criteria) this;
217 }
218
219 /**
220 * 将此对象的不为空的字段参数作为相等查询条件
221 *
222 * @param param 参数对象
223 * @author Bob {@link}0haizhu0@gmail.com
224 * @Date 2015年7月17日 下午12:48:08
225 */
226 public Criteria andEqualTo(Object param) {
227 MetaObject metaObject = SystemMetaObject.forObject(param);
228 String[] properties = metaObject.getGetterNames();
229 for (String property : properties) {
230 //属性和列对应Map中有此属性
231 if (propertyMap.get(property) != null) {
232 Object value = metaObject.getValue(property);
233 //属性值不为空
234 if (value != null) {
235 andEqualTo(property, value);
236 }
237 }
238 }
239 return (Criteria) this;
240 }
241
242 /**
243 * 将此对象的所有字段参数作为相等查询条件,如果字段为 null,则为 is null
244 *
245 * @param param 参数对象
246 */
247 public Criteria andAllEqualTo(Object param) {
248 MetaObject metaObject = SystemMetaObject.forObject(param);
249 String[] properties = metaObject.getGetterNames();
250 for (String property : properties) {
251 //属性和列对应Map中有此属性
252 if (propertyMap.get(property) != null) {
253 Object value = metaObject.getValue(property);
254 //属性值不为空
255 if (value != null) {
256 andEqualTo(property, value);
257 } else {
258 andIsNull(property);
259 }
260 }
261 }
262 return (Criteria) this;
263 }
264
265 public Criteria orIsNull(String property) {
266 addOrCriterion(column(property) + " is null");
267 return (Criteria) this;
268 }
269
270 public Criteria orIsNotNull(String property) {
271 addOrCriterion(column(property) + " is not null");
272 return (Criteria) this;
273 }
274
275 public Criteria orEqualTo(String property, Object value) {
276 addOrCriterion(column(property) + " =", value, property(property));
277 return (Criteria) this;
278 }
279
280 public Criteria orNotEqualTo(String property, Object value) {
281 addOrCriterion(column(property) + " <>", value, property(property));
282 return (Criteria) this;
283 }
284
285 public Criteria orGreaterThan(String property, Object value) {
286 addOrCriterion(column(property) + " >", value, property(property));
287 return (Criteria) this;
288 }
289
290 public Criteria orGreaterThanOrEqualTo(String property, Object value) {
291 addOrCriterion(column(property) + " >=", value, property(property));
292 return (Criteria) this;
293 }
294
295 public Criteria orLessThan(String property, Object value) {
296 addOrCriterion(column(property) + " <", value, property(property));
297 return (Criteria) this;
298 }
299
300 public Criteria orLessThanOrEqualTo(String property, Object value) {
301 addOrCriterion(column(property) + " <=", value, property(property));
302 return (Criteria) this;
303 }
304
305 public Criteria orIn(String property, Iterable values) {
306 addOrCriterion(column(property) + " in", values, property(property));
307 return (Criteria) this;
308 }
309
310 public Criteria orNotIn(String property, Iterable values) {
311 addOrCriterion(column(property) + " not in", values, property(property));
312 return (Criteria) this;
313 }
314
315 public Criteria orBetween(String property, Object value1, Object value2) {
316 addOrCriterion(column(property) + " between", value1, value2, property(property));
317 return (Criteria) this;
318 }
319
320 public Criteria orNotBetween(String property, Object value1, Object value2) {
321 addOrCriterion(column(property) + " not between", value1, value2, property(property));
322 return (Criteria) this;
323 }
324
325 public Criteria orLike(String property, String value) {
326 addOrCriterion(column(property) + " like", value, property(property));
327 return (Criteria) this;
328 }
329
330 public Criteria orNotLike(String property, String value) {
331 addOrCriterion(column(property) + " not like", value, property(property));
332 return (Criteria) this;
333 }
334
335 /**
336 * 手写条件
337 *
338 * @param condition 例如 "length(countryname)<5"
339 * @return
340 */
341 public Criteria orCondition(String condition) {
342 addOrCriterion(condition);
343 return (Criteria) this;
344 }
345
346 /**
347 * 手写左边条件,右边用value值
348 *
349 * @param condition 例如 "length(countryname)="
350 * @param value 例如 5
351 * @return
352 */
353 public Criteria orCondition(String condition, Object value) {
354 criteria.add(new Criterion(condition, value, true));
355 return (Criteria) this;
356 }
357
358 /**
359 * 将此对象的不为空的字段参数作为相等查询条件
360 *
361 * @param param 参数对象
362 * @author Bob {@link}0haizhu0@gmail.com
363 * @Date 2015年7月17日 下午12:48:08
364 */
365 public Criteria orEqualTo(Object param) {
366 MetaObject metaObject = SystemMetaObject.forObject(param);
367 String[] properties = metaObject.getGetterNames();
368 for (String property : properties) {
369 //属性和列对应Map中有此属性
370 if (propertyMap.get(property) != null) {
371 Object value = metaObject.getValue(property);
372 //属性值不为空
373 if (value != null) {
374 orEqualTo(property, value);
375 }
376 }
377 }
378 return (Criteria) this;
379 }
380
381 /**
382 * 将此对象的所有字段参数作为相等查询条件,如果字段为 null,则为 is null
383 *
384 * @param param 参数对象
385 */
386 public Criteria orAllEqualTo(Object param) {
387 MetaObject metaObject = SystemMetaObject.forObject(param);
388 String[] properties = metaObject.getGetterNames();
389 for (String property : properties) {
390 //属性和列对应Map中有此属性
391 if (propertyMap.get(property) != null) {
392 Object value = metaObject.getValue(property);
393 //属性值不为空
394 if (value != null) {
395 orEqualTo(property, value);
396 } else {
397 orIsNull(property);
398 }
399 }
400 }
401 return (Criteria) this;
402 }
403
404 public List<Criterion> getAllCriteria() {
405 return criteria;
406 }
407
408 public String getAndOr() {
409 return andOr;
410 }
411
412 public void setAndOr(String andOr) {
413 this.andOr = andOr;
414 }
415
416 public List<Criterion> getCriteria() {
417 return criteria;
418 }
419
420 public boolean isValid() {
421 return criteria.size() > 0;
422 }
423 }
424
425 public static class Criteria extends GeneratedCriteria {
426
427 protected Criteria(Map<String, EntityColumn> propertyMap, boolean exists, boolean notNull) {
428 super(propertyMap, exists, notNull);
429 }
430 }
这些代码,都是在内部运算数据然后返回引用本身(如何解决),使得方法可以像做数学公式一样进行运算,过滤(或者说改变)数据流,Spark的RDD就是受了这个设计模式的启发,在action触发之前,运行的函数本身不会对数据流运算,不过内部会标记数据流需要进行的运算,这也是标准模式的运用。
图1.像不像标准模式的过滤数据流?