Java 开发笔记:多线程查询逻辑的抽象与优化

一、背景

在项目开发中,我们遇到了一个需要处理多种类型查询的场景。每种类型的查询逻辑相似,但又存在细微差别。为了提高代码的复用性和可维护性,我们决定对多线程查询逻辑进行抽象化处理。

二、问题描述

  • 重复代码:每种查询类型都有一套类似的多线程查询逻辑,导致代码冗余。

  • 扩展性差:新增查询类型时,需要重复编写类似的逻辑,增加了开发成本和出错的概率。

  • 维护困难:由于代码重复,修改一处逻辑时需要在多个地方同步修改,容易遗漏。

三、优化思路

  1. 提取公共逻辑:将多线程查询的公共逻辑提取到一个通用方法中。

  2. 使用函数式接口:通过函数式接口将查询逻辑作为参数传入通用方法,避免重复代码。

  3. 策略模式:利用策略模式,将每种查询类型封装为一个策略类,继承自通用的父类。

四、实现步骤

1. 定义通用查询逻辑

AbstractFormFillHandler 类中,定义一个通用的多线程查询方法 queryData,并使用函数式接口 QueryFunction 来传入具体的查询逻辑。

复制代码
@Slf4j
@Service
public abstract class AbstractFormFillHandler implements FormFillHandler {

    @Autowired
    protected FormFillConfService fillConfService;

    @Autowired
    protected FormFillDataQueryService dataQueryService;

    /**
     * 通用的多线程查询方法
     *
     * @param request 查询请求
     * @param queryFunction 查询逻辑的函数
     * @return 查询结果
     */
    protected List<FormFillResponse> queryData(FormFillRequest request, QueryFunction queryFunction) {
        List<FormFillConf> fillConfList = fillConfService.selectConfigList(request);
        List<CompletableFuture<FormFillResponse>> futures = new ArrayList<>();

        for (FormFillConf config : fillConfList) {
            log.info("数据库配置信息查询入参{}", JSONUtil.toJsonStr(request));
            log.info("表单查询入参{}", JSONUtil.toJsonStr(config));

            int controlSeqId = config.getControlSeqId() == null ? 99999 : config.getControlSeqId();
            FormFillConvert formFillConvert = new FormFillConvert();
            BeanUtils.copyProperties(request, formFillConvert);
            formFillConvert.setControlSeqId(controlSeqId);
            formFillConvert.setColName(config.getColName());
            formFillConvert.setTableName(config.getTableName());

            // 使用 CompletableFuture 异步查询
            CompletableFuture<FormFillResponse> future = CompletableFuture.supplyAsync(() -> {
                FormFillResponse response = new FormFillResponse();
                response.setTitle(config.getControlName());
                response.setControlSeqId(controlSeqId);

                try {
                    // 尝试从主表查询
                    List<FormFillItemResult> itemResults = queryFunction.apply(formFillConvert);
                    response.setResultList(itemResults);
                } catch (Exception e) {
                    log.error("主表查询失败,尝试从备份表查询", e);
                    // 如果主表查询失败,尝试从备份表查询
                    formFillConvert.setTableName(config.getBakTableName()); // 设置备份表名
                    try {
                        List<FormFillItemResult> bakItemResults = queryFunction.apply(formFillConvert);
                        response.setResultList(bakItemResults);
                    } catch (Exception bakException) {
                        log.error("备份表查询失败,忽略此次失败结果", bakException);
                        // 忽略此次失败结果,返回空结果
                        response.setResultList(Collections.emptyList());
                    }
                }
                return response;
            });

            futures.add(future);
        }

        // 等待所有异步任务完成,并收集结果
        return futures.stream()
                .map(CompletableFuture::join) // 等待每个异步任务完成
                .filter(response -> response.getResultList() != null && !response.getResultList().isEmpty()) // 过滤掉空结果
                .sorted(Comparator.comparingInt(FormFillResponse::getControlSeqId)) // 按照 ControlSeqId 排序
                .collect(Collectors.toList());
    }

    /**
     * 查询逻辑的函数接口
     */
    @FunctionalInterface
    public interface QueryFunction {
        List<FormFillItemResult> apply(FormFillConvert formFillConvert) throws Exception;
    }
}

2. 创建具体策略类

根据不同的查询类型,创建具体的策略类,并在其中调用通用查询方法。

单选策略类
复制代码
@Slf4j
@Service
public class SingleStrategy extends AbstractFormFillHandler {

    @Override
    public ControlTypeEnum getControlTypeEnum() {
        return ControlTypeEnum.RADIO;
    }

    @Override
    public List<FormFillResponse> getQueryData(FormFillRequest request) {
        return queryData(request, formFillConvert -> dataQueryService.selectSingleResultList(formFillConvert));
    }
}
多选策略类
复制代码
@Slf4j
@Service
public class MultipleStrategy extends AbstractFormFillHandler {

    @Override
    public ControlTypeEnum getControlTypeEnum() {
        return ControlTypeEnum.CHECK;
    }

    @Override
    public List<FormFillResponse> getQueryData(FormFillRequest request) {
        return queryData(request, formFillConvert -> dataQueryService.selectMultipleResultList(formFillConvert));
    }
}
级联选择器策略类
复制代码
@Slf4j
@Service
public class CascaderStrategy extends AbstractFormFillHandler {

    @Override
    public ControlTypeEnum getControlTypeEnum() {
        return ControlTypeEnum.CASCADER;
    }

    @Override
    public List<FormFillResponse> getQueryData(FormFillRequest request) {
        return queryData(request, formFillConvert -> dataQueryService.selectSingleResultList(formFillConvert));
    }
}

3. 配置工厂类

在工厂类 FormFillQueryFactory 中,注册所有策略类,以便根据不同的查询类型动态选择对应的策略。

复制代码
@Slf4j
@Component
public class FormFillQueryFactory {
    private final Map<ControlTypeEnum, FormFillHandler> queryHandlerMaps = Maps.newHashMap();

    @Autowired
    public FormFillQueryFactory(List<FormFillHandler> handlerList) {
        for (FormFillHandler handler : handlerList) {
            queryHandlerMaps.put(handler.getControlTypeEnum(), handler);
        }
    }

    public List<FormFillResponse> getQueryData(FormFillRequest request) {
        checkExportType(request);
        FormFillHandler formFillHandler = queryHandlerMaps.get(ControlTypeEnum.getByCode(request.getControlType()));
        return formFillHandler.getQueryData(request);
    }

    private void checkExportType(FormFillRequest request) {
        log.info("查询请求入参:{}", JSONUtil.toJsonStr(request));
        if (ControlTypeEnum.getByCode(request.getControlType()) == null) {
            log.error("查询类型不存在{}", request.getControlType());
            throw new BusinessException("查询类型不存在");
        }
        if (!queryHandlerMaps.containsKey(ControlTypeEnum.getByCode(request.getControlType()))) {
            log.error("查询类型不支持{}", request.getControlType());
            throw new BusinessException("查询类型不支持");
        }
    }
}

五、优化效果

  1. 减少重复代码:通过提取公共逻辑,避免了在每个策略类中重复编写类似的多线程查询逻辑。

  2. 提高扩展性:新增查询类型时,只需添加一个新的策略类并实现对应的查询逻辑,无需修改现有代码。

  3. 增强可维护性:公共逻辑集中管理,修改一处逻辑即可在所有策略类中生效,降低了维护成本。

六、总结

通过将多线程查询逻辑抽象化并结合策略模式,我们成功优化了代码结构,提高了代码的复用性和可维护性。这种方法不仅适用于当前项目,还可以推广到其他类似的多类型查询场景中。

相关推荐
秋千码途27 分钟前
小架构step系列08:logback.xml的配置
xml·java·logback
飞翔的佩奇28 分钟前
Java项目:基于SSM框架实现的旅游协会管理系统【ssm+B/S架构+源码+数据库+毕业论文】
java·数据库·mysql·毕业设计·ssm·旅游·jsp
时来天地皆同力.1 小时前
Java面试基础:概念
java·开发语言·jvm
Code Warrior1 小时前
【Linux】基础开发工具(3)
linux·服务器
找不到、了1 小时前
Spring的Bean原型模式下的使用
java·spring·原型模式
阿华的代码王国2 小时前
【Android】搭配安卓环境及设备连接
android·java
YuTaoShao2 小时前
【LeetCode 热题 100】141. 环形链表——快慢指针
java·算法·leetcode·链表
巴伦是只猫2 小时前
【机器学习笔记 Ⅲ】4 特征选择
人工智能·笔记·机器学习
铲子Zzz2 小时前
Java使用接口AES进行加密+微信小程序接收解密
java·开发语言·微信小程序
霖檬ing2 小时前
K8s——配置管理(1)
java·贪心算法·kubernetes