import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.function.Function; /** * @author lanan */ public class PageQueryHelper <R, U> { private static final int MAX_QUERY_TIMES = 10; private static final int MAX_SORT_DATA_SIZE = 120; private final Function<PageQueryParam, Pair<Integer, List<R>>> aQuery; private final Function<PageQueryParam, Pair<Integer, List<R>>> bQuery; private final Function<R, U> compareFunction; private final Comparator<U> comparator; public PageQueryHelper( Function<PageQueryParam, Pair<Integer, List<R>>> aQuery, Function<PageQueryParam, Pair<Integer, List<R>>> bQuery, Function<R, U> compareFunction, final Comparator<U> comparator) { this.aQuery = aQuery; this.bQuery = bQuery; this.compareFunction = compareFunction; this.comparator = comparator; } /** * 双数据源混合查询 * @param globalPageParam * @return */ @SuppressWarnings({"PMD.NcssCount", "java:S3776"}) public Pair<Integer, List<R>> dualDataSourcePageQuery(PageQueryParam globalPageParam) { if (globalPageParam == null) { return Pair.of(0, Collections.emptyList()); } //分别取第一页数据,并将总页数一起取到 final PageQueryParam aPageParam = initPageParam(globalPageParam); final PageQueryParam bPageParam = initPageParam(globalPageParam); final Pair<Integer, List<R>> aStartPage = aQuery.apply(aPageParam); final Pair<Integer, List<R>> bStartPage = bQuery.apply(bPageParam); final Integer aTotal = aStartPage.getLeft(); final Integer bTotal = bStartPage.getLeft(); final Integer globalStart = globalPageParam.getStart(); //两个数据源的总数据量小于start final int total = aTotal + bTotal; if (total <= globalStart) { return Pair.of(total, Collections.emptyList()); } //定义双数据源的取值范围(globalStart可能存在的数据范围) int aRight = Math.min(Math.max(0, aTotal - 1), globalStart); int bRight = Math.min(Math.max(0, bTotal - 1 ), globalStart); int aLeft = Math.max(globalStart - bTotal, 0); int bLeft = Math.max(globalStart - aTotal, 0); final Integer pageSize = globalPageParam.getNum(); final List<R> aLeftPage = aLeft == 0 ? aStartPage.getRight() : queryA(aPageParam, aLeft, Math.min(pageSize, aTotal - aLeft)); final List<R> bLeftPage = bLeft == 0 ? bStartPage.getRight() : queryB(bPageParam, bLeft, Math.min(pageSize, bTotal - bLeft)); //优先处理这种情况:globalStart之前全是数据源a的数据或全是数据源b的数据 final List<R> rs = returnIfSorted(aLeftPage, bLeftPage, aRight, bRight, aPageParam, bPageParam, globalPageParam); if (CollectionUtils.isNotEmpty(rs)) { return Pair.of(total, rs); } int times = 0; while (aRight > aLeft && ++times < MAX_QUERY_TIMES) { int aMid = (aRight - aLeft) / 2 + aLeft; int bMid = globalStart - aMid; // 如果 aRight - aLeft <= pageSize,则page页数据的取值访问为: // A[aLeft - pageSize, aRight + pageSize] + B[bMid - pageSize, bMid + 2 * pageSize] // 直接取到这个区间的数据,进行排序后,返回globalStart后的数据即可 if (aRight - aLeft <= pageSize || bRight - bLeft <= pageSize) { if (aRight - aLeft <= pageSize) { final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE); result.addAll(queryA(aPageParam, aLeft - pageSize, aRight - aLeft + 2 * pageSize)); result.addAll(queryB(bPageParam, bMid - pageSize, 3 * pageSize)); sort(result); int skip = globalStart - Math.max(aLeft - pageSize, 0) - Math.max(bMid - pageSize, 0); return Pair.of(total, result.subList(skip, Math.min(skip + pageSize, result.size()))); } else { final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE); result.addAll(queryB(bPageParam, bLeft - pageSize, bRight - bLeft + 2 * pageSize)); result.addAll(queryA(aPageParam, aMid - pageSize, 3 * pageSize)); sort(result); int skip = globalStart - Math.max(bLeft - pageSize, 0) - Math.max(aMid - pageSize, 0); return Pair.of(total, result.subList(skip, Math.min(skip + pageSize, result.size()))); } } final List<R> aPage = queryA(aPageParam, aMid, pageSize); final List<R> bPage = queryB(bPageParam, bMid, pageSize); R aMin = aPage.get(0); R aMax = aPage.get(aPage.size() - 1); R bMin = bPage.get(0); R bMax = bPage.get(bPage.size() - 1); if (compare(aMin, bMax) > 0) { aRight = aMid - 1; bLeft = bMid; } else if (compare(bMin, aMax) > 0) { aLeft = aMid + 1; bRight = bMid; } else { //定位到了全局排序值大概位置 //此时第page页的数据范围: //A[aMid - pageSize, aMid + pageSize] //B[bMid - pageSize, bMid + pageSize] //全部取回后排序,返回globalStart之后的数据即可 final List<R> result = new ArrayList<>(MAX_SORT_DATA_SIZE); result.addAll(aPage); result.addAll(bPage); result.addAll(queryA(aPageParam, Math.max(aMid - pageSize, 0), Math.min(aMid, pageSize))); result.addAll(queryB(bPageParam, Math.max(bMid - pageSize, 0), Math.min(bMid, pageSize))); sort(result); int skip = globalStart - Math.max(aMid - pageSize, 0) - (Math.max(bMid - pageSize, 0)); return Pair.of(total, result.subList(skip, pageSize + skip)); } } LOG.warn("双数据混合查询异常"); return Pair.of(total, Collections.emptyList()); } /** * 特殊情况的处理,page页之前全是A或者全是B的数据,那么此时可直接返回需要的数据 * aLeft + b * @param aLeftPage * @param bLeftPage * @param aRight * @param bRight * @param aPageParam * @param bPageParam * @param globalPageParam * @return */ private List<R> returnIfSorted(List<R> aLeftPage, List<R> bLeftPage, int aRight, int bRight, PageQueryParam aPageParam, PageQueryParam bPageParam, PageQueryParam globalPageParam) { int pageSize = globalPageParam.getNum(); //优先处理这种情况:globalStart之前全是数据源a的数据或全是数据源b的数据 final List<R> bRightPage = queryB(bPageParam, bRight, pageSize); if (CollectionUtils.isEmpty(aLeftPage)) { return bRightPage; } int globalStart = globalPageParam.getStart(); if (CollectionUtils.isEmpty(bRightPage) || compare(aLeftPage.get(0), bRightPage.get(0)) >= 0) { //bRight < globalStart,说明此时 bRightPage取的是 B数据源的最后一条数据,且该数据小于等于aLeftPage[0] if (bRight < globalStart) { return aLeftPage; } else { //bRightPage[0]之前的数据全小于aLeftPage[0],此时第page页的数据在bRightPage+aLeftPage中 final List<R> result = new ArrayList<>(aLeftPage.size() + bRightPage.size()); result.addAll(aLeftPage); result.addAll(bRightPage); sort(result); return result.subList(0, Math.min(result.size(), pageSize)); } } final List<R> aRightPage = queryA(aPageParam, aRight, pageSize); if (CollectionUtils.isEmpty(bLeftPage)) { return aRightPage; } if (CollectionUtils.isEmpty(aRightPage) || compare(bLeftPage.get(0), aRightPage.get(0)) >= 0) { if (aRight < globalStart) { return bLeftPage; } else { final List<R> result = new ArrayList<>(aRightPage.size() + bLeftPage.size()); result.addAll(aRightPage); result.addAll(bLeftPage); sort(result); return result.subList(0, Math.min(result.size(), pageSize)); } } return Collections.emptyList(); } /** * 比较两个数据 * @param o1 * @param o2 * @return */ private int compare(R o1, R o2) { final U aCompareValue = compareFunction.apply(o1); final U bCompareValue = compareFunction.apply(o2); return comparator.compare(aCompareValue, bCompareValue); } /** * 根据指定字段进行排序 * */ private void sort(List<R> list) { Collections.sort(list, this::compare); } /** * 生成查询条件 * @param globalPageParam * @return */ private PageQueryParam initPageParam(PageQueryParam globalPageParam) { final PageQueryParam pageParam = new PageQueryParam(); pageParam.setUserId(globalPageParam.getUserId()); pageParam.setKeyword(globalPageParam.getKeyword()); pageParam.setStart(0); pageParam.setNum(globalPageParam.getNum()); return pageParam; } /** * 从A数据源查询数据 * @param pageParam 减少PageQueryParam对象创建数 * @param start 起始范围 * @param num 获取数量 * @return */ private List<R> queryA(PageQueryParam pageParam, int start, int num) { pageParam.setStart(start); pageParam.setNum(num); return queryList(aQuery, pageParam); } /** * 从B数据源查询数据 * @param pageParam 减少PageQueryParam对象创建数 * @param start 起始范围 * @param num 获取数量 * @return */ private List<R> queryB(PageQueryParam pageParam, int start, int num) { pageParam.setStart(start); pageParam.setNum(num); return queryList(bQuery, pageParam); } /** * 从数据源获取数据 * @param query * @param pageParam * @return */ private List<R> queryList(Function<PageQueryParam, Pair<Integer, List<R>>> query, PageQueryParam pageParam) { if (pageParam.getStart() < 0) { pageParam.setStart(0); } final Pair<Integer, List<R>> pair = query.apply(pageParam); if (pair == null || CollectionUtils.isEmpty(pair.getRight())) { return Collections.emptyList(); } return pair.getRight(); } }标签:return,pageSize,数据源,param,pageParam,混排,final,result From: https://www.cnblogs.com/lanan-blog/p/18534978