废话不多说,又是日常遇到问题的一天。需求就是在原来的列表查询接口新增一个展示字段,而这个展示字段和列表记录是一对多的关系。更详细的说,就是原本是个入库单列表分页查询接口,现在需要在这个接口上面返回入库单关联的入库设备的信息。这其实就是典型的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条记录了。