这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
前言
本文记录一下,mybatis的collection使用不当,导致接口响应慢的问题。
过程是这样的,有一个分页接口,响应很慢。按道理一条sql的查询逻辑为啥会慢呢?
定位
所以我们开启了sql的日志打印,方便我们定位问题。将mapper的日志等级设置为debug模式即可。
logging:
level:
root: info
mapper包名: debug
可以发现,第一行就是我们的主sql,一个列表条件查询。但是查询完列表sql后,后面又执行了多次子查询的sql。
现在就需要我们看一下mapper的xml是怎么写的?
<select id="getProductInfoList" resultMap="productMap">
select
t.*
from
type_product t
where 1=1 AND t.is_deleted = 'N'
<if test="productTypeId != null and productTypeId != '' ">
and t.product_type_id=#{productTypeId}
</if>
<if test="productTypeTagId != null and productTypeTagId != '' ">
and t.product_type_tag_id=#{productTypeTagId}
</if>
group by product_id
</select>
没啥问题,就是主条件查询的sql。那么后面这些子查询sql哪来的呢?我们再来看一下我们定义的resultMap
<resultMap id="productMap" type="com.baoyun.iyb.app.dao.dataobject.ProductInfo">
<id column="id" property="id"></id>
<result column="product_name" property="productName"></result>
<result column="product_id" property="productId"></result>
<result column="description" property="description"></result>
<result column="product_img_url" property="productImgUrl"></result>
<result column="gmt_modified" property="gmtModified"></result>
<!-- 查询标签 -->
<collection property="tags" column="product_id" ofType="com.baoyun.iyb.app.dao.dataobject.Tag"
select="com.baoyun.iyb.app.dao.mapper.TagMapper.getTags">
<result column="tag_name" property="tagName"></result>
<result column="tag_title" property="tagTitle"></result>
<result column="tag_content" property="tagContent"></result>
</collection>
</resultMap>
发现用到了collection来表示一对多的关系。其中定义了select,这个TagMapper.getTags就是日志打印出来的那些子查询。
方案
找到原因后,就应该想一下如何去避免它。其实collection还有另外一种用法,就是在主sql上join关联表去查询,通过使用collection来对数据进行聚合,成一个list。给一个案例
<resultMap type="Student" id="StudentMap2">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="job" property="job" />
<collection property="scores" javaType="java.util.ArrayList" ofType="Score">
<id column="id" property="id" />
<result column="num" property="num" />
<association property="subject" javaType="Subject">
<id column="id" property="id" />
<result column="name" property="name" />
</association>
</collection>
</resultMap>
<select id="queryStudents2" resultMap="StudentMap2" >
SELECT stu.id,stu.name name,stu.job,sco.id id,sco.num num,sub.id id,sub.name name
FROM t_student stu LEFT JOIN t_score sco ON stu.id = sco.sid LEFT JOIN t_subject sub ON sco.subject = sub.id
</select>
这种方案可以看出只需要执行一次sql就行,缺点也很明显,就是代码的可重用性几乎没有了。