Java架构直通车——一对多的嵌套查询分页Bug解决方案

文章目录

引入

在这里插入图片描述
在进行商品订单的分页查询的时候,我们Mapper是这样写的:

    <select id="queryMyOrders" resultMap="myOrdersVO" parameterType="Map">
        SELECT
        od.id as orderId,
        od.created_time as createdTime,
        od.pay_method as payMethod,
        od.real_pay_amount as realPayAmount,
        od.post_amount as postAmount,
        os.order_status as orderStatus,
        oi.item_id as itemId,
        oi.item_name as itemName,
        oi.item_img as itemImg,
        oi.item_spec_name as itemSpecName,
        oi.buy_counts as buyCounts,
        oi.price as price
        FROM
        orders od
        LEFT JOIN
        order_status os
        on od.id = os.order_id
        LEFT JOIN
        order_items oi
        ON od.id = oi.order_id
        WHERE
        od.user_id = #{paramsMap.userId}
        AND
        od.is_delete = 0
        <if test="paramsMap.orderStatus != null">
            and os.order_status = #{paramsMap.orderStatus}
        </if>
        ORDER BY
        od.updated_time ASC
    </select>
    
  <resultMap id="myOrdersVO" type="com.imooc.pojo.vo.MyOrdersVO">
    <id column="orderId" property="orderId"/>
    <result column="createdTime" property="createdTime"/>
    <result column="payMethod" property="payMethod"/>
    <result column="realPayAmount" property="realPayAmount"/>
      <result column="postAmount" property="postAmount"/>
      <result column="orderStatus" property="orderStatus"/>
      <result column="isComment" property="isComment"/>

    <collection property="subOrderItemList"
                ofType="com.imooc.pojo.vo.MySubOrderItemVO">
      <result column="itemId" property="itemId"/>
      <result column="itemName" property="itemName"/>
      <result column="itemImg" property="itemImg"/>
        <result column="itemSpecId" property="itemSpecId"/>
        <result column="itemSpecName" property="itemSpecName"/>
        <result column="buyCounts" property="buyCounts"/>
        <result column="price" property="price"/>
    </collection>
  </resultMap>

上述Mapper很长(进行了三表的链接),我们只需要关注:
<resultMap>中出现了<collection>,也就是说MyOrdersVO对象的properties里面包含了一个List<MySubOrderItemVO>

当然,我们使用的分页工具是PageHelper:

        <!--pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.12</version>
        </dependency>

在后端代码中,是这样的:

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public PagedGridResult queryMyOrders(String userId,
                                         Integer orderStatus,
                                         Integer page,
                                         Integer pageSize) {

        Map<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        if (orderStatus != null) {
            map.put("orderStatus", orderStatus);
        }
        PageHelper.startPage(page, pageSize);
        List<MyOrdersVO> list = ordersMapperCustom.queryMyOrders(map);
        return setterPagedGrid(list, page);
    }
    
    public PagedGridResult setterPagedGrid(List<?> list, Integer page) {
        PageInfo<?> pageList = new PageInfo<>(list);
        PagedGridResult grid = new PagedGridResult();
        grid.setPage(page);
        grid.setRows(list);
        grid.setTotal(pageList.getPages());
        grid.setRecords(pageList.getTotal());
        return grid;
    }

是很标准的分页方法,不过上述的代码组合后,会产生一个BUG:

  • 前端分页内容数量不准确。也就是说,原本应该是MyOrdersVO的数目为pageSize,但是现在是MyOrdersVO对象中的MySubOrderItemVO对象的数目一页为pageSize。

产生这种情况的原因在于,我们使用了一对多的嵌套查询。在Github上的PageHelper.startPage,该插件作者也强调了:

1. Please do not configure more than one PageHelper
When using Spring, you can config PageHelper by mybatis-config.xml or Spring<bean>. Select one of them, do not configure PageHelper in two ways at the same time.

2. PageHelper does not support paging with for update statement
3. PageHelper does not support Nested Results Mapping
Since the nested result mode causes the resultSet to be folded, the total number of results for the paged query will decrease after folding. So It cannot guarantee the number of paged results correctly.

也就是说由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。

解决方案


  1. 方案一:分两步查询

第一步:查询MyOrdersVO的内容,但是不查询其子订单MySubOrderItemVO的内容,前端暂时以null显示。
第二步:当用户浏览到该条订单的时候,才进行子订单的查询。

该方法的缺点就是会增加查询请求。


  1. 方法二:采用<collection>中的select属性

该方法和上一个方法差不多,其实也是分了两步,第一步也是只查询订单不查询子订单,其select将三表链接改为两表链接:

  <select id="queryMyOrders" resultMap="myOrdersVO" parameterType="Map">
    SELECT
        od.id as orderId,
        od.created_time as createdTime,
        od.pay_method as payMethod,
        od.real_pay_amount as realPayAmount,
        od.post_amount as postAmount,
        os.order_status as orderStatus,
        od.is_comment as isComment
    FROM
        orders od
    LEFT JOIN
        order_status os
    on od.id = os.order_id
    WHERE
        od.user_id = #{paramsMap.userId}
    AND
        od.is_delete = 0
        <if test="paramsMap.orderStatus != null">
          and os.order_status = #{paramsMap.orderStatus}
        </if>
    ORDER BY
        od.updated_time ASC
  </select>

修改后,将<resultMap>改为:

  <resultMap id="myOrdersVO" type="com.imooc.pojo.vo.MyOrdersVO">
    <id column="orderId" property="orderId"/>
    <result column="createdTime" property="createdTime"/>
    <result column="payMethod" property="payMethod"/>
    <result column="realPayAmount" property="realPayAmount"/>
      <result column="postAmount" property="postAmount"/>
      <result column="orderStatus" property="orderStatus"/>
      <result column="isComment" property="isComment"/>

    <collection property="subOrderItemList"
                select="getSubItems"
                column="orderId"
                ofType="com.imooc.pojo.vo.MySubOrderItemVO">
      <result column="itemId" property="itemId"/>
      <result column="itemName" property="itemName"/>
      <result column="itemImg" property="itemImg"/>
        <result column="itemSpecId" property="itemSpecId"/>
        <result column="itemSpecName" property="itemSpecName"/>
        <result column="buyCounts" property="buyCounts"/>
        <result column="price" property="price"/>
    </collection>
  </resultMap>

注意,这里的<collection>新增了两个属性,一个是select一个是column
我们在查询完queryMyOrders时,它会发现它对应的resultMap里面,带有subOrderItemList,这个属性会根据select来进行对应的查询。查询就会有查询的属性值,我们希望将订单id<id column="orderId" property="orderId"/>传到子订单来查询,所以就使用column属性来指定resultMap传入collection的行。

所以这里我们会再写一个<select>,其id与之对应:

    <select id="getSubItems" parameterType="String" resultType="com.imooc.pojo.vo.MySubOrderItemVO">

      select
        oi.item_id as itemId,
        oi.item_name as itemName,
        oi.item_img as itemImg,
        oi.item_spec_name as itemSpecName,
        oi.buy_counts as buyCounts,
        oi.price as price
      from
        order_items oi
      where
        oi.order_id = #{orderId}

    </select>

我们也能够看出,这其实就是方法一第二步的内容。

发布了385 篇原创文章 · 获赞 326 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/No_Game_No_Life_/article/details/104196346