今天花了N个小时作了一个如今看来十分简单明了的重构,目的是少写点代码,缘由嘛,天然是万恶的需求变动。java
原始代码(操做的是mongo,使用spring-data):spring
public static void buildCountryChannelCriteria(String country, String channel, Query q) { //查询推广至全球以及包含参数country的app Criteria countryC = new Criteria(); if (!StringUtils.isBlank(country)) { countryC.orOperator(Criteria.where("countries").exists(false),Criteria.where("countries." + (country.toUpperCase())).exists(true)); } else {//国家未知 则只查询推广至全球的 countryC.and("countries").exists(false); } Criteria channelC = new Criteria(); //查找推荐至所有渠道以及指定渠道的app if (!StringUtils.isBlank(channel)) { channelC.orOperator(Criteria.where("channels").exists(false), Criteria.where("channels." + channel).exists(true)); } else {//渠道未知 则只查询推广至全渠道的 channelC.and("channels").exists(false); } q.addCriteria(new Criteria().andOperator(countryC, channelC)); }
代码很简单,拼接country和channel的Criteria,两者都有可能包含或操做(orOperator),跟sql中or查询使用括号括起or操做来避免错误的查询同样,这里使用new Criteria().andOperator来对两个or操做进行and。sql
该方法被多个外部方法调用,新需求中,外部方法也有相似or之类的操做,最开始本身设想是这样的:mongodb
public static void buildCountryChannelCriteria(String country, String channel, Query q) { //...ommit buildCountryChannelCriteria(String country, String channel, Query q, null);//调用重构后的方法 } public static void buildCountryChannelCriteria(String country, String channel, Query q, Criteria existedAndCriteria) { //...ommit q.addCriteria(existedAndCriteria.andOperator(countryC, channelC)); }
可不幸的是:Due to limitations of the com.mongodb.BasicDBObject, you can't add a second 'null' criteria. Mongo查询中貌似只容许存在一个"$and"查询属性,上面的代码中existedAndCriteria在外部方法中已经有了一个$and查询属性,再次调用andOperator时就报错了.......数组
纠结N就以后,决定把外部包含or操做的Criteria传递进来,而后在该方法中将外部的Criteria一并传递给andOperator方法,因而问题又来了,andOperator这货接受的参数是可变参数Criteria...,不过可变参数本身平时用的也不多,因此在究竟如何传参和类型转换上纠结了下。app
最终仍是重构完成了。对于可变参数主要丰富了如下两点:ui
①外部调用的时候能够传入list,而后使用list.toArray(new Criteria[list.size()])将list转换为数组,数组能够直接做为可变参数对象传递给andOperator方法(andOperator(new Criteria[]{})这样调用是ok的)。spa
②本身在最终实现时,外部调用传入的也是可变参数,可变参数转换成list可使用java.util.Arrays.asList(Object[])方法,能够将传入的可变参数直接当作数组传递进去(可变参数和数组能够直接互转?这点还须要深刻了解下)。这样作还有个明显的好处就是,不须要保留旧的接口了,直接调用新接口时可变参数部分能够不指定,也就是说外部方法什么都不用改了。甚好。code
不过须要注意的是,asList返回的是固定长度的list,不能add,因此须要将其返回值赋给一个新array对象
最终代码:
/** * 拼接country和channel的查询语句<br> * @param country * @param channel * @param q * @param criterias4And 其余须要添加至andOperator的语句子句 */ public static void buildCountryChannelCriteria(String country, String channel, Query q, Criteria... criterias4And) { //查询推广至全球以及包含参数country的app Criteria countryC = new Criteria(); if (!StringUtils.isBlank(country)) { countryC.orOperator(Criteria.where("countries").exists(false),Criteria.where("countries." + (country.toUpperCase())).exists(true)); } else {//国家未知 则只查询推广至全球的 countryC.and("countries").exists(false); } Criteria channelC = new Criteria(); //查找推荐至所有渠道以及指定渠道的app if (!StringUtils.isBlank(channel)) { channelC.orOperator(Criteria.where("channels").exists(false), Criteria.where("channels." + channel).exists(true)); } else {//渠道未知 则只查询推广至全渠道的 channelC.and("channels").exists(false); } //组合外部条件和内部条件 List<Criteria> cList = new ArrayList<Criteria>(0); if (null!=criterias4And && criterias4And.length>0) { cList.addAll(Arrays.asList(criterias4And));//Arrays.asList 长度不可变 因此addAll } cList.add(countryC); cList.add(channelC); q.addCriteria(new Criteria().andOperator(cList.toArray(new Criteria[cList.size()]))); } //外部调用依旧是: buildCountryChannelCriteria(country, channel, q);
相关错误连接参考:http://www.mkyong.com/java/due-to-limitations-of-the-basicdbobject-you-cant-add-a-second-and/