很多时候我们需要写重复性的代码来实现等效的功能,比如使用原生JDBC实现增删改查都需要创建连接、获取session、捕获异常、关闭连接等,幸好Mybatis、SpringTemplete等框架把我们解放了。
但业务代码难免会遇到类似场景,每次copy几行代码来做一些相同的事情。今天笔者遇到的问题就是Dao层批量查询时,总需要我手工去分批,然后再查库,如下:
public List<R> selectByIds(List<T> resIds) {
List<List<T>> partition = Lists.partition(resIds, Constants.DAO_BATH_SIZE);
List<R> result = Lists.newArrayListWithCapacity(resIds.size());
for (List<Long> part : partition) {
List<R> temp = resourceGroupMapper.selectByIds(part) // 执行批量查询操作
result.addAll(temp);
}
return result;
}
除了执行查询操作的那行不同之外,其它代码几乎完成类似。有没有更简洁的方式,每次只需要写查询部分,其它在模板代码里面完成呢?思路如下:
查询代码利用提供一个接口,供使用方自定义,分批、结果汇总操作使用模板代码实现。
方案一:自定义接口
PartitionQuery.java
public static <T, R> List<R> query1(List<T> list, Hander<T, R> hander) {
return hander.part(list);
}
@FunctionalInterface
public interface Hander<T, R> {
/**
* 查询接口
*/
List<R> query(List<T> list);
/**
* 分批、结果汇总
*/
default List<R> part(List<T> list) {
List<List<T>> partition = Lists.partition(list, Constants.DAO_BATH_SIZE);
List<R> result = Lists.newArrayListWithCapacity(list.size());
for (List<T> part : partition) {
result.addAll(query(part));
}
return result;
}
}
使用这种方式,业务代码变成下面这样:
public List<R> selectByResIds(List<T> resIds) {
return PartitionQuery.query1(resIds, (ids) -> {
return authMapper.selectByResIds(ids);
});
}
亲测没问题,总感觉这个调用方式有点奇怪,为啥要传入list,而不是直接使用p,一开始我也怀疑自己写的复杂了,最后明白了,因为我需要先对list分批处理,流程如下:
list分批 —> 查询(自定义) —> 结果汇总
自定代码在中间位置,只能这么写。如果网友们有好的建议,欢迎探讨。
方案二、使用Java 8的Function
public static <T, R> List<R> query2(List<T> list, Function<List<T>, List<R>> function) {
List<List<T>> partition = Lists.partition(list, Constants.DAO_BATH_SIZE);
List<R> result = Lists.newArrayListWithCapacity(list.size());
for (List<T> part : partition) {
result.addAll(function.apply(part));
}
return result;
}
使用方式相同,不再重复,这样代码会少很多,最终采用了这种方式。以上请大家指正,如果有更简洁的方法,欢迎指导,谢谢。