mybatis中一对多分页查询结果异常问题解决

文章讲述了作者在Mybatis中遇到一对多查询分页的问题,发现分页插件PageHelper不会对Feign接口的SQL进行分页,通过调整Mapper层的SQL和使用子查询解决了返回重复记录的问题,确保了正确分页显示数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

废话不多说,又是日常遇到问题的一天。需求就是在原来的列表查询接口新增一个展示字段,而这个展示字段和列表记录是一对多的关系。更详细的说,就是原本是个入库单列表分页查询接口,现在需要在这个接口上面返回入库单关联的入库设备的信息。这其实就是典型的mybatis中一对多的查询关系。对于这个需求,当然是自信满满的一顿操作猛如虎的写完,丢给前端联调。

然而,不出意外的话,就要出意外了。

前端调试的时候分页查询的时候,传的pageSize是15,但是当前页展示的却只有11条记录。

看来,又是懵逼的一天。

突然,想起来了,写代码的这个同事,上次就因为没有把目标分页的sql查询作为PageHelper.startPage()方法后第一个查询语句,导致分页出错。这次,估计又是这样。

看了一下,他的代码,分页方法在controller层:

赶紧去看它的业务层,是不是又没把目标分页的查询语句,放在前面: 

果然,又坑我, 第一个查询语句是查询用户的。迅速调整,把PageHelper.startPage()直接放到目标查询语句的最上面。

这次应该可以了吧?!测一下,试试水。

 

我丢,还是不行! 不对,如果原本就分页有问题的话,应该早就提bug了,迅速去生产环境一看,分页对。

那不对啊,他分页的方法后第一个查询语句是查询用户的啊。按原本那样写,肯定是分页的查询用户的结果啊。点进去一看才发现,这个查询用户的方法是通过feign接口调用的。

 PageHelper是Mybatis的一个分页插件,肯定是不会对调用feign接口里面的查询sql进行分页的。

那么,现在看来,问题只能出在我对mapper层的查询修改上面。

主要做了如下修改:

修改sql查询语句,并增加resultMap映射,实现mybatis的一对多的查询

 <select id="getInWarehouse" resultMap="getInWarehouseMap">
    SELECT
        t1.id,
        (SELECT `name` FROM `t_dic_item` where type_code="in_warehouse_type" and `code`=t1.in_warehouse_type) inWarehouseType,
        t2.warehouse_name,
        t1.num,
        t1.	in_warehouse_code,
        t1.attachment_id,
        t1.in_warehouse_time,
        t1.state,
        u.name crName,
        t1.cr_time,
        t3.handle_finish_time,
        t4.equip_name,
        t4.num equipNum
    FROM
        t_wm_in_warehouse t1
        left join t_wm_warehouse_info t2 on t1.warehouse_id=t2.id
        left join `user` u on t1.cr_uid=u.id
        left join work_order t3 on t1.work_order_id=t3.id
        left join (
          SELECT
          tcweij.id,
          tcweij.equip_name,
          tcwre.relation_id,
          tcwre.num
          FROM
          t_wm_relation_equip tcwre
          LEFT JOIN (
          SELECT
          tcwei.id,
          tcwei.equip_name
          FROM
          t_wm_equip_info tcwei
          WHERE
          tcwei.removed = 0
          ) tcweij ON tcwre.equip_id = tcweij.id
          where tcwre.type =2
        ) t4 ON t1.id = t4.relation_id
        <where>
        <trim prefixOverrides="and">
          <if test="inWarehouseCode!=null and inWarehouseCode!=''">
            AND t1.in_warehouse_code like concat('%',#{inWarehouseCode},'%')
          </if>
          <if test="inWarehouseType!=null and inWarehouseType!=''">
            AND t1.in_warehouse_type = #{inWarehouseType}
          </if>
          <if test="warehouseId">
            AND t1.warehouse_id = #{warehouseId}
          </if>
          <if test="state!=null">
            AND t1.state = #{state}
          </if>
          <if test="inWarehouseTime">
            AND DATE(t1.in_warehouse_time) = #{inWarehouseTime}
          </if>
          <if test="userId!=null">
            AND t1.cr_uid=#{userId}
          </if>
        </trim>
      </where>
        ORDER BY
        t1.cr_time DESC
  </select>
<resultMap id="getInWarehouseMap" type="com.domain.warehousemanage.vo.WmInWarehouseListVo">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="inWarehouseType" property="inWarehouseType"/>
        <result column="warehouse_name" property="warehouseName"/>
        <result column="num" property="num"/>
        <result column="in_warehouse_code" property="inWarehouseCode"/>
        <result column="state" property="state"/>
        <result column="name" property="crName"/>
        <result column="in_warehouse_time" property="inWarehouseTime"/>
        <result column="cr_time" property="crTime"/>
        <collection property="relationEquipInfoDtoList" ofType="com.domain.warehousemanage.dto.RelationEquipInfoDto">
            <result column="equip_name" property="equipName"/>
            <result column="equipNum" property="equipNum"/>
        </collection>
    </resultMap>

好像,也并没有啥问题。不对,有问题。这样虽然映射的时候可以一对多的映射,但是,调用查询sql方法getInWarehouse的时候,返回的结果如果入库单关联了两个设备,就会返回两条记录。也就是以入库单记录的角度看,查询的结果是有重复的记录了。而mybatis是先进行分页截取,然后将重复的入库单对象中的关联设备RelationEquipInfoDto合并到relationEquipInfoDtoList中。这也就是为什么设置的pagesize是15,却返回的只有不到15条记录的原因。

知道问题的所在,便有了对应的解决办法

解决办法主要就是通过collection标签的 column , select 属性,设置mybatis子查询的方式,同时修改select id="getInWarehouse" 中的sql语句,使其返回结果不存在一对多关系。

getInWarehouse方法修改如下,不再关联查询入库单关联的设备:

 <select id="getInWarehouse" resultMap="getInWarehouseMap">
    SELECT
        t1.id,
        (SELECT `name` FROM `t_dic_item` where type_code="in_warehouse_type" and `code`=t1.in_warehouse_type) inWarehouseType,
        t2.warehouse_name,
        t1.num,
        t1.	in_warehouse_code,
        t1.attachment_id,
        t1.in_warehouse_time,
        t1.state,
        u.name crName,
        t1.cr_time,
        t3.handle_finish_time
    FROM
        t_wm_in_warehouse t1
        left join t_wm_warehouse_info t2 on t1.warehouse_id=t2.id
        left join `user` u on t1.cr_uid=u.id
        left join work_order t3 on t1.work_order_id=t3.id
        <where>
        <trim prefixOverrides="and">
          <if test="inWarehouseCode!=null and inWarehouseCode!=''">
            AND t1.in_warehouse_code like concat('%',#{inWarehouseCode},'%')
          </if>
          <if test="inWarehouseType!=null and inWarehouseType!=''">
            AND t1.in_warehouse_type = #{inWarehouseType}
          </if>
          <if test="warehouseId">
            AND t1.warehouse_id = #{warehouseId}
          </if>
          <if test="state!=null">
            AND t1.state = #{state}
          </if>
          <if test="inWarehouseTime">
            AND DATE(t1.in_warehouse_time) = #{inWarehouseTime}
          </if>
          <if test="userId!=null">
            AND t1.cr_uid=#{userId}
          </if>
        </trim>
      </where>
        ORDER BY
        t1.cr_time DESC
  </select>

 resultMap映射修改如下:

  <resultMap id="getInWarehouseMap" type="com.domain.warehousemanage.vo.WmInWarehouseListVo">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="inWarehouseType" property="inWarehouseType"/>
        <result column="warehouse_name" property="warehouseName"/>
        <result column="num" property="num"/>
        <result column="in_warehouse_code" property="inWarehouseCode"/>
        <result column="state" property="state"/>
        <result column="crName" property="crName"/>
        <result column="in_warehouse_time" property="inWarehouseTime"/>
        <result column="cr_time" property="crTime"/>
        <collection property="relationEquipInfoDtoList" javaType="ArrayList"
                    ofType="com.domain.warehousemanage.dto.RelationEquipInfoDto"
        select="getInWarehouseEquipInfo" column="id">
        </collection>
    </resultMap>

新增根据入库单id查询关联设备方法getInWarehouseEquipInfo:

<select id="getInWarehouseEquipInfo" resultMap="getInWarehouseEquipInfoMap">
          SELECT
          tcweij.id,
          tcweij.equip_name,
          tcwre.relation_id,
          tcwre.num equipNum
          FROM
          t_wm_relation_equip tcwre
          LEFT JOIN (
          SELECT
          tcwei.id,
          tcwei.equip_name
          FROM
          t_wm_equip_info tcwei
          WHERE
          tcwei.removed = 0
          ) tcweij ON tcwre.equip_id = tcweij.id
          where tcwre.type =2
          and  tcwre.relation_id = #{id}
    </select>

方法getInWarehouseEquipInfo的resultMap映射:

  <resultMap id="getInWarehouseEquipInfoMap" type="com.domain.warehousemanage.dto.RelationEquipInfoDto">
        <result column="equip_name" property="equipName"/>
        <result column="equipNum" property="equipNum"/>
    </resultMap>

此时,再用postman测试。当传参pageSize为15的时候,便能够正确的返回15条记录了。

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值