问题背景
需求:从第三方获取数据列表,传到前台进行展示,目前前台需要可以根据任意字段进行排序(升序和降序),但是第三方接口不支持排序功能,所以需要手动实现数据的排序。
问题分析
方案一:数据入库
- 将第三方数据存储到数据库中,直接可以根据sql进行排序输出。
- 缺点:本来就是实时数据,但是从数据库饶了一圈,先将数据写入,再将数据读出,增加了两次的数据库IO
方案二:根据字段进行排序
- 根据前台上送的字段信息和升降序信息,实现列表的排序功能。
- 缺点:因为字段不确定,如果针对每个字段都实现一种排序方式,再加上升降序的情况,比如有M个字段,则需要编写2M种排序实现。
- 优化:可以动态的获取排序字段实现自定义的比较器,这样就和前台传输的字段进行了解耦,而且也增加了扩展性。
解决方案
思路
数据列表的排序主要是针对字段的比较器进行的,那我们的目标就是创建基于自定义字段的比较器,那大小关系的比较必定设计到字段的get方法,由于字段名不定,所以可以通过反射动态的调用字段的get方法。
实现
/**
* 自定义排序器
*/
public class HighQuelitySeedComp implements Comparator<HighQualitySeed> {
@Override
public int compare(HighQualitySeed o1,HighQualitySeed o2) {
//降序升降序规则
String isAsc = o1.getIsAsc();
//拼接排序字段的get方法
String firstLetter = o1.getOrderBy().substring(0, 1).toUpperCase();
String getMethodName = "get" + firstLetter + o1.getOrderBy().substring(1);
Class<? extends HighQualitySeed> class1 = o1.getClass();
Method getMethod;
try {
//获取动态字段的get方法
getMethod = class1.getMethod(getMethodName);
//分别调用两个对象的get方法,并比较大小
BigDecimal value1 = (BigDecimal)getMethod.invoke(o1, new Object[] {});
BigDecimal value2 = (BigDecimal)getMethod.invoke(o2, new Object[] {});
return "asc".equals(isAsc)? value1.compareTo(value2):value2.compareTo(value1);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
解释
对象属性中并没有isAsc和OrderBy属性,我是在获取数据列表之后,手动将前台上送的值,传入到对象中,便于在比较器中使用;毕竟重写的compare方法签名不支持传输更多的字段。
//手动排序
String orderBy = pageDomain.getOrderByColumn();
String isAsc = pageDomain.getIsAsc();
if (StringUtils.isNotEmpty(orderBy)) {
list.forEach(s -> {
s.setOrderBy(orderBy);
s.setIsAsc(isAsc);
});
调用
list.sort(new HighQuelitySeedComp());
优化空间
- 因为我是针对于某个具体的对象写的,所以里面的比较类型我是已知的为BigDecimal,后面的比较大小当然也是使用的BigDecimal;所以这里可以动态的获取字段的类型,实现多个数据类型的自定义排序器。
- 利用反射调用get方式不是很优雅,可以尝试使用PropertyDescriptor进行优化替换。