[Javaer]可变参数新接触

    今天花了N个小时做了一个现在看来十分简单明了的重构,目的是少写点代码,原因嘛,自然是万恶的需求变更保密

    原始代码(操作的是mongo,使用spring-data):

 

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。

    该方法被多个外部方法调用,新需求中,外部方法也有类似or之类的操作,最开始自己设想是这样的:

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...,不过可变参数自己平时用的也很少,所以在究竟如何传参和类型转换上纠结了下。

 

    最终还是重构完成了。对于可变参数主要丰富了以下两点:

    ①外部调用的时候可以传入list,然后使用list.toArray(new Criteria[list.size()])将list转换为数组,数组可以直接作为可变参数对象传递给andOperator方法(andOperator(new Criteria[]{})这样调用是ok的)。

    ②自己在最终实现时,外部调用传入的也是可变参数,可变参数转换成list可以使用java.util.Arrays.asList(Object[])方法,可以将传入的可变参数直接当做数组传递进去(可变参数和数组可以直接互转?这点还需要深入了解)。这样做还有个明显的好处就是,不需要保留旧的接口了,直接调用新接口时可变参数部分可以不指定,也就是说外部方法什么都不用改了。甚好。

    不过需要注意的是,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/

 

你可能感兴趣的:(java,mongodb,Criteria,可变参数转换,'null',object[)