ResultMap collection多层嵌套使用

ResultMap介绍

在Mybatis使用中,ResultMap是最复杂的一种结构,也是功能最强大的结构之一。通过ResultMap能够将复杂的1对多的结果集映射到一个实体当中去,可以借助Mybatis来将复杂结构的数据对象映射到一个结果集中组装好。

结构

ResultMap有3个属性,如下:

<resultMap id="studentMap" type="User"  extends="userMap"  ></resultMap>
名称说明
idresultMap标识,区分不同的resultMap
typeresultMap的属性,返回的类型
extends继承的resultMap,类似类的继承,resultMap也可以继承

子元素

名称说明
id子元素中的id属性扮演主键作用指定了该属性,则查询结果会以该属性字段为主键,允许多个主键,多个主键称为联合主键
result返回结果字段,一般一个字段对应一个result
collection表示一对多关系,一般用于结果集中的集合属性,如List
association表示一对一关系
discriminator鉴别器,根据实际选择来采用哪个类别作为实例
constructor配置构造方法

id和result存在一些相同的属性:

property:映射的POJO属性名,如果column元素相同,会自动映射到POJO上

column:sql查询的列名

javaType:配置java的类型,可以是特定的类完全限定名或Mybatis上下文别名

jdbcType:配置数据库类型,一般不需要限定,Mybatis已经为我们做了此工作

typeHander:类型处理器,允许你使用特定的处理器来覆盖默认处理器。需要制定jdbcType和javaType相互转换规则

使用场景

举例:
查询一个复杂的结构,真实场景是不确定度详情信息,一个不确定度详情包含10项列表信息汇总,其中多项是list集合,list中依旧嵌套list,如图所示,此例子只演示2个比较复杂的list
在这里插入图片描述
standardList是标准溶液列表,结构中standardSigns也是list结构,包含多个数值:

描述
equipList是设备列表,每个设备中包含多个内容 contentDatas:

在这里插入图片描述
每个contentDatas中包含多个计算值信息calDatas:

在这里插入图片描述
最复杂的结构如上所示,接下来说如何实现。

实现方式一

一开始我是使用以前的办法,一个sql语句通过左连接将所有表关联起来,然后只使用一个resultMap让mybatis自己去映射关系。

 <select id="getUncertainDataById1"  resultMap="calDataMap1" >
        SELECT
            a.id,
            a.urel,
            a.inclusion AS k,
            a.selected_c AS selectedC,
            a.detectability,
            a.item,
            sar.`value` AS sampleC,
            sac.`value` AS sampleSign,
            bar.`value` AS backData,
            bar.inclusion AS backData_k,
            auto.name AS autoName,
            auto.accuracy AS accuracy,
            auto.inclusion AS auto_k,
            a.standard_c AS standardC,
            a.rel_uncertainty AS relUncertainty,
            a.uncertainty,
            a.backData_critical,
            a.backData_isBack,
            a.backData_confidence,
            a.standard_inclusion,
            a.backData_avg,
            a.unit,
            a.sampleSign_avg,
            a.line_a,
            a.line_b,
            a.line_r,

            eq.id AS eqid,
            eq.`name`,
            eq.dimension,
            eq.volume,
            eq.inclusion AS eq_k,
            eq.origin,
            eqr.id AS rid,
            eqr.`value` AS content,
            eqr.inclusion,
            eqc.row AS crow,
            eqc.`value`,
            eqc.sort,
            eqc.description,

			str.id  AS strid,
            str.`value` AS standardC,
            stc.`value` AS standardSign

        FROM
            t_uncertain a
        -- 关联样品数据
        LEFT JOIN t_uncertain_row sar ON a.id = sar.`code`
        AND sar.type = 2
        LEFT JOIN t_uncertain_column sac ON sac.`row` = sar.id
        -- 关联回收率数据
        LEFT JOIN t_uncertain_row bar ON a.id = bar.`code`
        AND bar.type = 4
        LEFT JOIN t_uncertain_column bac ON bac.`row` = bar.id
        -- 关联自动进样器
        LEFT JOIN t_uncertain_equip auto ON a.id = auto.`code`
        AND auto.origin = 'UN10'
	    -- 仪器
        left join t_uncertain_equip  eq ON eq.`code` = a.id and eq.origin='UN02'
        LEFT JOIN t_uncertain_row eqr ON eq.id = eqr.equip AND eqr.type='3'
        LEFT JOIN t_uncertain_column eqc ON eqc.`row` = eqr.id
        -- 标准曲线标准溶液
		LEFT JOIN t_uncertain_row str ON str.`code` = a.id and  str.type = 1
        LEFT JOIN t_uncertain_column stc ON stc.`row` = str.id
        WHERE
            a.id = #{id}
    </select>

将所有表关联起来查询,查询结果是呈笛卡尔积增加,仅仅查询一个结构该sql结果就已经达到了7200条数据!

之后再通过resultMap进行映射:

 <resultMap id="calDataMap1" type="com.koron.laboratory.web.business.others.bean.CalDataBean">
        <id column="id" property="id"  />
        <!--样品数据-->
        <collection property="sampleList" ofType="com.koron.laboratory.web.business.others.bean.UncertaintyBean" >
            <result column="sampleC" property="sampleC" />
            <result column="sampleSign" property="sampleSign" />
        </collection>

        <!--标准溶液-->
        <collection property="standardList" column="id"  ofType="com.koron.laboratory.web.business.others.bean.UncertaintyBean"  >
            <id  column="strid" property="id" />
            <result column="standardId" property="id" />
            <result column="standardC" property="standardC" />
            <collection property="standardSigns"  ofType="java.lang.Double" >
                <result column="standardSign"  />
            </collection>
        </collection>

        <!--设备数据-->
        <collection property="equipList" column="id" ofType="com.koron.laboratory.web.business.others.bean.EquipUncertainBean" >
            <id  column="eqid" property="id" />
            <result column="name" property="name" />
            <result column="dimension" property="dimension" />
            <result column="volume" property="volume" />
            <result column="eq_k" property="inclusion" />
            <result column="origin" property="origin" />
            <result column="inclusion" property="inclusion" />
            <collection property="contentDatas"  ofType="com.koron.laboratory.web.business.others.bean.UncertaintyBean" >
                <id column="crow" />
                <result column="content" property="content" />
                <result column="inclusion" property="inclusion" />
                <collection property="calDatas"  ofType="com.koron.laboratory.web.business.others.bean.EquipCalBean" >
                    <result column="value" property="value" />
                    <result column="description" property="description" />
                    <result column="sort" property="sort" />
                </collection>
            </collection>
        </collection>
    </resultMap>

这里只保留了几个复杂list结构在resultMap中,其他不是list结构的数据省略了,但是由于也是需要去关联表,所以只要list结构数据量一旦大起来,最终结果都会变得很庞大。

这里需要注意的是 collection中的id属性,当在关联多个表的sql中 id属性作为主键来让mybatis知道该如何映射。即使collection中依旧包含collection,都会通过id属性来判断是否属于一个实体里面,当id相同时,collection中的属性就会自动映射进去。
在这里插入图片描述
这里我遇见的问题就是一开始不知道通过id属性来区分list中,以前遇见的结构最多嵌套1层list,导致这里的多层list嵌套无法识别,仅仅都映射在第一个数据里了。

这种方法虽然能成功将数据映射好,但是将查询的多个结果直接交给mybatis处理,还是比较影响性能的。

实现方式二

将sql拆分成多个select,每个复杂的list结构的select 都用resultMap来接收,这里将standardList和equipList都通过一个select来将查询分开,resultMap具体结构如下:

<resultMap id="calDataMap" type="com.koron.laboratory.web.business.others.bean.CalDataBean">
        <id column="id" property="id"  />
        <!--标准溶液-->
        <collection property="standardList" column="id"  select="getStandardList" />
        <!--样品数据-->
        <collection property="sampleList" ofType="com.koron.laboratory.web.business.others.bean.UncertaintyBean" >
            <result column="sampleC" property="sampleC" />
            <result column="sampleSign" property="sampleSign" />
        </collection>
        <!--设备数据-->
        <collection property="equipList" column="id" select="getEquipList" />
        <!--稀释设备数据-->
</resultMap>

<!--    标准溶液查询resultMap-->
    <resultMap id="standardListMap" type="com.koron.laboratory.web.business.others.bean.UncertaintyBean">
        <id  column="id" property="id" />
        <result column="standardId" property="id" />
        <result column="standardC" property="standardC" />
        <collection property="standardSigns"  ofType="java.lang.Double" >
            <result column="standardSign"  />
        </collection>
    </resultMap>

    <!--    设备查询resultMap-->
    <resultMap id="equipListMap" type="com.koron.laboratory.web.business.others.bean.EquipUncertainBean">
        <id  column="id" property="id" />
        <result column="name" property="name" />
        <result column="dimension" property="dimension" />
        <result column="volume" property="volume" />
        <result column="eq_k" property="inclusion" />
        <result column="origin" property="origin" />
        <result column="inclusion" property="inclusion" />
        <collection property="contentDatas"  ofType="com.koron.laboratory.web.business.others.bean.UncertaintyBean" >
            <id column="crow" />
            <result column="content" property="content" />
            <result column="inclusion" property="inclusion" />
            <collection property="calDatas"  ofType="com.koron.laboratory.web.business.others.bean.EquipCalBean" >
                <result column="value" property="value" />
                <result column="description" property="description" />
                <result column="sort" property="sort" />
            </collection>
        </collection>
    </resultMap>
    


标准溶液和仪器的select如下:

<!--    查询标准溶液信息-->
    <select id="getStandardList" resultMap="standardListMap" >
        SELECT
            str.id,
            str.`value` AS standardC,
            stc.`value` AS standardSign
        FROM
            t_uncertain_row str
        LEFT JOIN t_uncertain_column stc ON stc.`row` = str.id
        WHERE
            `code` = #{id}
        AND str.type = 1
        ORDER BY
            str.id
    </select>

<!--    查询仪器信息-->
    <select id="getEquipList" resultMap="equipListMap" >
         SELECT
            eq.id,
            eq.`name`,
            eq.dimension,
            eq.volume,
            eq.inclusion AS eq_k,
            eq.origin,
            eqr.id AS rid,
            eqr.`value` AS content,
            eqr.inclusion,
            eqc.row AS crow,
            eqc.`value`,
            eqc.sort,
            eqc.description
         FROM t_uncertain_equip  eq
        LEFT JOIN t_uncertain_row eqr ON eq.id = eqr.equip
        LEFT JOIN t_uncertain_column eqc ON eqc.`row` = eqr.id
        where eq.`code`=#{id} AND eqr.type='3' and eq.origin='UN02'
        ORDER BY eq.`name`,eqr.`value`
    </select>

在主体calDataMap中将复杂的collection拆分成select结构,每个select结构有自己独有的resultMap来接收,拆分后每个sql单独执行单独进行数据映射,最终映射好直接插入主体的calDataMap中,这样的做法可以大大的提高效率,以我个人简单的测试结果来看,当方式一查询结果达到7200数据时,方式二可以比方式一快10倍速度左右,当然这个倍数并不准确,但是可以明确的是速度可以快好几倍,效率可以大大提升。
在这里插入图片描述
上图是我进行测试的数据,第一个时间是方式二执行的,第二个是方式一执行的,很明显可以看出2种方式在性能上的差距。

总结

当结构并不是特别复杂的情况下,可以通过使用一个sql将resultMap映射好,当结构中嵌套多层类似list结构的集合时,则需要考虑性能问题,最好将每个collocation拆分开单独对应一个select,这样可以大大提高效率。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐