如何高效重构包含数据库调用的Java业务代码
Java业务代码重构方案
原有代码存在的核心问题
- N+1数据库查询问题:循环遍历员工时每次都调用
employeeRepository.findInvalidCodesByEmployeeId查询数据库,员工数量越多,DB请求次数越多,IO开销会线性增长,是最大的性能瓶颈 - 集合操作bug&性能问题:遍历
ArrayList的同时调用remove方法删除元素,一方面会出现遍历漏项的逻辑bug,另一方面每次删除都需要移动数组元素,时间复杂度达到O(n²) - 冗余中间集合:额外创建了不必要的临时集合,增加了内存开销
- 无效编码判断用
List的contains方法,每次判断时间复杂度O(n),效率低
重构实现
第一步:新增DAO层批量查询方法
首先在EmployeeRepository中新增批量查询接口:
/** * 批量查询多个员工对应的无效编码集合 * @param employeeIds 员工ID集合 * @return key为员工ID,value为该员工对应的无效编码Set */ Map<Long, Set<Long>> findInvalidCodesByEmployeeIds(Collection<Long> employeeIds);
第二步:重构业务方法
/** * 计算符合要求的员工薪资总和 * @param employeeList 员工列表 * @return 活跃且编码有效的员工薪资总和 */ public BigDecimal getActiveAndValidEmployeesSalariesTotal(final List<Employee> employeeList) { // 1、先过滤出活跃员工,收集所有需要查询无效编码的员工ID List<Employee> activeEmployees = employeeList.stream() .filter(e -> e.active) .toList(); if (activeEmployees.isEmpty()) { return BigDecimal.ZERO; } // 2、批量查询所有活跃员工的无效编码,一次DB请求完成 Map<Long, Set<Long>> empInvalidCodeMap = employeeRepository.findInvalidCodesByEmployeeIds( activeEmployees.stream().map(e -> e.id).toList() ); // 3、流式过滤有效员工,直接累加薪资 return activeEmployees.stream() .filter(emp -> { Set<Long> invalidCodes = empInvalidCodeMap.get(emp.id); // 无无效编码 或者 当前编码不在无效列表中 则保留 return invalidCodes == null || !invalidCodes.contains(emp.code); }) .map(emp -> emp.salary) .reduce(BigDecimal.ZERO, BigDecimal::add); }
优化点说明
- 核心性能提升:将N次DB查询优化为1次批量查询,IO开销从O(n)降到O(1),在员工数量大的场景下性能提升尤为明显
- 解决了原有遍历删除集合的逻辑bug,同时消除了O(n²)的集合操作开销
- 用
Set存储无效编码,contains判断复杂度降到O(1) - 使用
BigDecimal.ZERO代替new BigDecimal(0),更规范且避免不必要的对象创建 - 逻辑分层更清晰,可读性更高
内容的提问来源于stack exchange,提问作者Carlos Gonzalez
相关产品推荐
相关产品推荐

