使用函數(shù)式接口分離分頁與業(yè)務(wù)邏輯

場景:定時任務(wù)查詢?nèi)頂?shù)據(jù)進(jìn)行處理推送時,采用的策略是分頁每次查詢100條數(shù)據(jù),進(jìn)行業(yè)務(wù)處理,直到所有數(shù)據(jù)都處理完成。因?yàn)閿?shù)據(jù)量太大導(dǎo)致出現(xiàn):

SELECT * FROM table_name ORDER BY id ASC \color{red}{limit} \color{red}{3000000,100}

這樣的分頁sql 性能是非常低的,因?yàn)閿?shù)據(jù)庫會從第一條數(shù)據(jù)遍歷到第3000000條,再進(jìn)行分頁。

public ReturnT<String> execute(String param) {
        List<Person> persons;
        for (int i = 0; ; i += 100) {
            String limitSql = "limit " + i + ",100";
            persons = personService.selectList(new QueryWrapper<Person>()
                    .lambda().orderByAsc(Person::getId).last(limitSql));
            
//=========================業(yè)務(wù)邏輯==========================================================
            persons.forEach(e -> {
                try {
                    rocketProducer.send(RocketMqConstant.PERSON_TOPIC, RocketMqConstant.PERSON_TAG, e);
                } catch (Exception ex) {
                    log.error("異常, {}", ex);
                }
            });
//===========================================================================================
            //終止循環(huán)
            if (persons.size() < 100) break;
        }
        return ReturnT.SUCCESS;
    }

從上面的代碼可以看出來,除了分頁sql的性能問題,分頁和業(yè)務(wù)也耦合在了一起。類似的代碼有13處,全是使用類似的形式編寫,分頁每次查詢100條進(jìn)行業(yè)務(wù)處理的邏輯。

分頁與業(yè)務(wù)進(jìn)行拆分
  • 分頁
  public static <T> void selectByConPage(
            Long start, Long size, BiFunction<Long, String, List<T>> function,
            Function<List<T>, Long> functionMaxId, Consumer<List<T>> consumer) {

        Long maxId = 0L;
    
        for (; ;) {

            // 拼接分頁條件.
            String limitSql = "limit " + size;
            // 執(zhí)行分頁查詢.
            List<T> apply = function.apply(maxId, limitSql);
            // 獲取最大id.
            maxId = functionMaxId.apply(apply);
            // 處理業(yè)務(wù)邏輯.
            consumer.accept(apply);
            // 最后一頁返回.
            if (apply.size() < size) break;
        }
    }

因?yàn)槿际悄J(rèn)從0條開始,每頁100行,因此重載分頁方法

/**
     * 默認(rèn)從0開始,每頁100條.
     *
     * @param function
     * @param functionMaxId
     * @param consumer
     * @param <T>
     */
    public static <T> void selectByConPage(
            BiFunction<Long, String, List<T>> function,
            Function<List<T>, Long> functionMaxId, Consumer<List<T>> consumer) {

        selectByConPage(0L, 100L, function, functionMaxId, consumer);
    }
  • 業(yè)務(wù)
 private void business(List<Person> persons) {
        persons.forEach(e -> {
            try {
                rocketProducer.send(RocketMqConstant.PERSON_TOPIC, RocketMqConstant.PERSON_TAG, e);
            } catch (Exception ex) {
                log.error("異常, {}", ex);
            }
        });
    }
  • 查詢
 public List<Person> selectByCon(Long id, String limitSql) {
        LambdaQueryWrapper<Person> wrapper = new QueryWrapper<Person>().lambda()
                .gt(Person::getId, id)
                .orderByAsc(Person::getId).last(limitSql);

        return baseMapper.selectList(wrapper);
    }
  • 處理查詢結(jié)果,獲取最大id
 public Long selectMaxId(List<Person> list) {
        if (Optional.ofNullable(list).isPresent()) {
            Long maxId = list.stream().map(Person::getId).max(Comparator.naturalOrder()).orElse(Long.MAX_VALUE);
            return maxId;
        }
        return Long.MAX_VALUE;
    }
  • 改造完成之后
    將查詢和業(yè)務(wù)的函數(shù)以參數(shù)的形式傳入,達(dá)到分頁與業(yè)務(wù)處理邏輯的分離以及復(fù)用分頁的目的。
public ReturnT<String> execute(String param) {

     PageAllMsgUtil.selectByConPage((id, limitSql) -> personService.selectByCon(id, limitSql),
                apply -> personService.selectMaxId(apply), list -> business(list));

        return ReturnT.SUCCESS;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容