关于业务中Java Stream流的使用

时间:2020-09-07

问题

今天要写的业务逻辑大概是这样:

第一、前端传入一个或一些查询条件,根据这些条件查询plan表,得到一个planList

第二、再根据每个plan对象里的planID查询planDetails表,又得到n个planDetailsList

第三、从每个planDetailsList中取出planDetails里的详细数据,处理后得到想要的数据返回给planDetailsList对应的plan对象

第四、将处理完成的planList导出到Excel表格中

为了便于理解,我画了个图,如下

image-20200907150403501

解决方案

V1.0

一开始我想的是查询出planList后,在第二次查询时直接带上处理数据的条件,这样就可以跳过筛选planDetails这一步,交由数据库帮我筛选数据。画个图大概是这样:

image-20200907153146229

但是这样一来就需要拼接sql语句,还需要写mapper层接口和对应的xml实现,想想就头大,直接Pass

V2.0

换个思路我想那我就还是查询出所有planDetails后在Java层面处理数据,正好用一下还没实战用过的Stream流。

既然要用流处理数据的话,只要写个对应的Filter过滤器就OK 了。

于是我写了如下代码

planDetailsDTOList.stream()
  				 .filter(planDetails -> (过滤条件) )
  				 .forEach(planDetails -> {数据处理} );

然后发现如果这样写就需要进行多次内部迭代才能完成数据的处理,因为我不是只有一种过滤条件,每进行一次过滤就必须重新遍历planDetailsList这个集合。效率太低,代码也会随着过滤条件的增多而变得过长。

Pass

V3.0

我还是没有改变大体方向,依然将数据放进Java中处理,这次我使用简单粗暴的if-else判断来进行数据过滤和处理,这样的话就只需要遍历一次集合便可以对所有数据进行分类。

public List<ContainerPlanDTO> dataHandle(List<ContainerPlanDTO> planDTOList){
    final Integer[] sum = {0,0,0,0,0};
    planDTOList.forEach(planDTO -> {
        List<ContainerPlanDetails> planDetailsList = containerPlanDetailsMapper.selectByPlanId(planDTO.getId());
        if (ObjectUtils.isEmpty(planDetailsList) || planDetailsList.size() == 0) {
            throw new BusinessException("此计划下未查询到详细信息!");
        }
        planDetailsList.forEach(planDetails -> {
            if (ObjectUtils.isEmpty(planDetails.getApplyNum() ) ) {
                throw new BusinessException("申请数量为null!");
            } else if ("20GP".equals(planDetails.getContainerTypeCode() ) && "24T".equals(planDetails.getCarryWeightCode() ) ) {
                sum[0] += planDetails.getApplyNum();
            } else if ("20GP".equals(planDetails.getContainerTypeCode() ) && "30T".equals(planDetails.getCarryWeightCode() ) ) {
                sum[1] += planDetails.getApplyNum();
            } else if ("40GP".equals(planDetails.getContainerTypeCode() ) ) {
                sum[2] += planDetails.getApplyNum();
            } else if ("40HC".equals(planDetails.getContainerTypeCode() ) ) {
                sum[3] += planDetails.getApplyNum();
            } else {
                sum[4] += planDetails.getApplyNum();
            }
        });
        planDTO.setTypePlan2024(sum[0]);
        planDTO.setTypePlan2030(sum[1]);
        planDTO.setTypePlan40GP(sum[2]);
        planDTO.setTypePlan40HC(sum[3]);
        planDTO.setTypePlanOther(sum[4]);
    });

    return planDTOList;
}

果然,有时候最好的方法不一定是最复杂的。

其他

1、在lambda表达式中无法传入非Final的值,我们就可以在外部声明一个Final的数组进行值的传递

2、单例模式下不要声明类中非Final的全局变量,非常容易出现并发问题

3、还有就是一个多条件查询的实现,也非常简单,在mapper.xml中使用动态Sql进行拼接就可以实现

代码如下:

mapper接口

@Repository
public interface ContainerPlanMapper {
    List<ContainerPlan> selectBySimple(ContainerPlan containerPlan);
}

mapper.xml

<select id="selectBySimple" parameterType="com.wisdom.domain.container.ContainerPlan" resultType="com.wisdom.domain.container.ContainerPlan">
  select * from container_plan where 1 = 1
  <if test="id != null and id != ''">
      and id = #{id}
  </if>
  <if test="planMonth != null and planMonth != ''">
      and plan_month = #{planMonth}
  </if>
  <if test="usedPlaceId != null and usedPlaceId != ''">
      and used_place_id = #{usedPlaceId}
  </if>
  <if test="returnPlaceId != null and returnPlaceId != ''">
      and return_place_id = #{returnPlaceId}
  </if>
  <if test="businessTypeCode != null and businessTypeCode != ''">
    and business_type_code = #{businessTypeCode}
  </if>
  <if test="carriageTypeCode != null and carriageTypeCode != ''">
    and carriage_type_code = #{carriageTypeCode}
  </if>
  <if test="customerId != null and customerId != ''">
    and customer_id = #{customerId}
  </if>
  <if test="varietiesCode != null and varietiesCode != ''">
    and varieties_code = #{varietiesCode}
  </if>
  <if test="varietiesName != null and varietiesName != ''">
    and varieties_name LIKE '%'#{varietiesName}'%'
  </if>
  <if test="containerBelongCode != null and containerBelongCode != ''">
    and container_belong_code = #{containerBelongCode}
  </if>
  <if test="companyId != null and companyId != ''">
    and company_id = #{companyId}
  </if>
</select>

这里只写了部分条件,确定的条件可以使用=,模糊条件就使用likewhere = 1是保证在所有的if判断都不成立时,sql语句不会因为出现语法错误而不能执行。

3.1、还有就是关键字查询,实现原理和这个高级查询非常相似,只不过是将SearchKey和所有字段进行like判断。

大概就是这样:hhhh

<select id="selectPage" resultMap="ResultMapWithBLOBs">
    select
    <include refid="Page_Column_List"/>,
    <include refid="Blob_Column_List"/>
    from oper_sale_ship oss
    left join oper_sale_ship_fee_status ossfs on ossfs.order_no = oss.order_no
    where 1=1 AND oss.delete_flag = 0
    <if test="searchKey != null and searchKey != ''">
        and (
        oss.order_no  LIKE CONCAT('%',#{searchKey},'%') or
        cust_name LIKE CONCAT('%',#{searchKey},'%') or
        contract_no LIKE CONCAT('%',#{searchKey},'%') or
        shipper LIKE CONCAT('%',#{searchKey},'%') or
        consignee LIKE CONCAT('%',#{searchKey},'%') or
        notify_party LIKE CONCAT('%',#{searchKey},'%') or
        route_way LIKE CONCAT('%',#{searchKey},'%') or
        ssl_code LIKE CONCAT('%',#{searchKey},'%') or
        creator_name LIKE CONCAT('%',#{searchKey},'%') or
        canvasser_name LIKE CONCAT('%',#{searchKey},'%') or
        business_man_name LIKE CONCAT('%',#{searchKey},'%') or
        oper_name LIKE CONCAT('%',#{searchKey},'%') or
        service_man_name LIKE CONCAT('%',#{searchKey},'%') or
        load_port_name LIKE CONCAT('%',#{searchKey},'%') or
        pod_name LIKE CONCAT('%',#{searchKey},'%') or
        finl_dest_name LIKE CONCAT('%',#{searchKey},'%') or
        chinese_name LIKE CONCAT('%',#{searchKey},'%') or
        source_name LIKE CONCAT('%',#{searchKey},'%') or
        oss.main_business_type_name LIKE CONCAT('%',#{searchKey},'%') or
        oss.bl_no LIKE CONCAT('%',#{searchKey},'%') or
        oss.bgbl_no LIKE CONCAT('%',#{searchKey},'%') or
        oss.ref_no_a LIKE CONCAT('%',#{searchKey},'%') or
        oss.ship_vessel LIKE CONCAT('%',#{searchKey},'%') or
        oss.ship_voyage LIKE CONCAT('%',#{searchKey},'%')
        )
    </if>

Q.E.D.


ALL WILL BE CLEAR