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.像不像标准模式的过滤数据流?