函数式编程实战
项目中,有一个需求,场景是这样的,有很多类型的数据比如:客运站,火车站,高速站等,他们都分别存在不同的数据库表中,在使用excel往这些表中导入数据的时候,需要做去重验证(假设是根据name1和name2字段去重)
1.首先想到的是,在每个Controller层,查询对应的表进行对比,校验是否有重复
由于有八九张表,写着发现除了Entity和tableName不一样,查重逻辑几乎一模一样
2.怎样才能把逻辑部分提取出来复用呢,这样就不用重复劳动了。问题的关键是需要提取出Entity列表中的name1和name2,组合成新的数据集合,这样就把不同类型的数据整合成一类:StationCheckBo.class
这里可以可以使用反射进行获取:
protected <T, E> StationCheckResultBo checkRepeat(List<T> datas, StationCheckMapper stationCheckMapper, Class<E> clazz) {
List<StationCheckBo> checkBos = IntStream.range(0, datas.size()).mapToObj(i ->
new StationCheckBo(i, (String) ReflectUtil.getFieldValue(datas.get(i), "fieldName"), (String) ReflectUtil.getFieldValue(datas.get(i), "fieldName")))
.toList();
String tableName = AnnotationUtil.getAnnotationValue(clazz, TableName.class, "value");
log.info("tableName:{}", tableName);
List<StationCheckResultBo> resultBos = stationCheckMapper.batchCheckWithDynamicTable(tableName, checkBos);
log.info("resultBos:{}", resultBos);
if (ObjectUtil.isNotEmpty(resultBos)) {
return resultBos.stream().filter(resultBo -> resultBo.getCount() > 0).findFirst().orElse(null);
}
return null;
}
3.反射有两个问题,一是每个Enitiy的name1和name2的变量名不一样;二是如果datas的数据多了,反射会非常影响性能。
那怎么解决呢,关键还是怎样提取出集合中的name1和name2这两个值。
函数式接口在这里可以发挥作用,我的理解是函数式接口传递的是代码块而不是普通参数,把固定的反射代码ReflectUtil.getFieldValue替换成一个“动态的代码块”,那么执行什么由调用者决定:
实现:
protected <T, E> StationCheckResultBo checkRepeat(List<T> datas, Class<E> clazz, Function<T, String> fun1, Function<T, String> fun2,
BiFunction<List<StationCheckBo>, String, List<StationCheckResultBo>> fun3) {
//这里fun1.apply(datas.get(i)),执行的是调用者的代码块,相当于执行了datas.get(i).getName1()
List<StationCheckBo> checkBos = IntStream.range(0, datas.size()).mapToObj(i ->
new StationCheckBo(i, fun1.apply(datas.get(i)), fun2.apply(datas.get(i))))
.toList();
String tableName = AnnotationUtil.getAnnotationValue(clazz, TableName.class, "value");
log.info("tableName:{}", tableName);
List<StationCheckResultBo> resultBos = fun3.apply(checkBos, tableName);
log.info("resultBos:{}", resultBos);
if (ObjectUtil.isNotEmpty(resultBos)) {
return resultBos.stream().filter(resultBo -> resultBo.getCount() > 0).findFirst().orElse(null);
}
return null;
}
调用:
StationCheckResultBo checkResultBo = checkRepeatByStation(excelResult.getList(), SysBridge.class,
SysBridgeBo::getArea,
SysBridgeBo::getFacilityName,
(List<StationCheckBo> checkBos, String tableName) -> checkMapper.batchCheckWithDynamicTable(tableName, checkBos)
);
checkRepeat中用到的一些参数无法提前拿到,就是函数式接口发挥作用的时候,fun3的思路也是这样。