mybatis一对多关系中嵌套结果查询的 封装对象的原理

Mybatis 在映射文件中加载关联关系对象主要通过两种方式:嵌套查询与嵌套结果。

嵌套查询是指通过执行另外一条 SQL 映射语句来返回预期的复杂类型;
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。

 本文对嵌套结果查询一对多封装对象的原理进行解释。

首先通过sql查询出来的结果集是这样的:

 不难发现id为7的资源有多条,并且有多个角色可以访问该资源。

在mapper.xml文件中:

 这是一对多关系的封装规则

<!--1.嵌套结果-->
<select id="selectUser" resultMap="selectUser1">
        select u.id,u.name,u.age,o.id as order_id,o.name as order_name from t_p_user u left join t_p_user_order o
        on u.id = o.user_id and u.id = #{id}
    </select>

    <resultMap id="selectUser1" type="com.seakoon.model.User">
        <id column="id" property="id" />
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <collection property="orders" ofType="com.seakoon.model.UserOrder" column="id">
            <id column="order_id" property="id" />
            <result column="order_name" property="name" />
            <result column="id" property="userId" />
        </collection>
    </resultMap>

那么为什么在数据库中明明有多条记录,为什么封装成对象后只有一个对象?而不是多个对象,每个对象拥有“多”中的一个角色呢?

不难发现每个resultMap中都有一个id标签,包括一对多(一对一)的resultMap中的collection标签(association标签)中也必须写id标签,原理就在这里!

mybatis会根据id来统一封装,在上面这个例子中,有若干条id为7的记录,那么mybatis会把他当成一个对象,然后每条id为7记录中的role信息会封装到roles集合中。而id为8记录的role信息不会封装到id为7对象的roles集合中。

简而言之,就是会把id相同但rid不同的封装成一个对象,不同的rid对应不同的角色,再将其封装到这个实体类的roles集合中。

不难推论,若role实体类中又有另一个一对多关系的字段List<Other> others,按照这个规则写resultMap如下:

那么它会将rid相同但oid不同的封装成一个role对象,在这个对象中再将不同的other信息封装到others集合中。

2.1.5.1 合并的依据
mybatis 在处理结果时, 会判断对象是否相同, 如果相同则会将结果与之前的结果进行合并。

那么, 结果是否相同是如何判断的呢?

首先是通过 id的比较, 如果 id 没有配置则比较每一个字段(此时, 只要有一个字段不同的对象不同)。

2.1.5.2 id 的作用
在生成的 XML 中, 如果我们有主键, 一般会生成一个对应的 id 属性。

而 id 属性就可以用来判断获取到的数据是否属于同一个对象。

在以上的例子中, 数据库中查询出来有三个 id=1, 则 mybatis 在处理结果时就可以知道这三条数据对应相同的对象, 从而将他们合并。
2.2 collection 嵌套查询方式

 使用嵌套查询:查询订单数据时新用一条sql来查。参数是column属性的值,如果多参数的话:使用
column=“{property1 = column1,property2 = column2…}”

mapper.xml中的写法

<!--2.嵌套查询-->
    <select id="selectUserNew" resultMap="selectUser2">
        select id,name,age from t_p_user where id = #{id}
    </select>

    <resultMap id="selectUser2" type="com.seakoon.model.User">
        <id column="id" property="id" />
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <collection property="orders" ofType="com.seakoon.model.UserOrder" column="id" javaType="java.util.ArrayList"
        select="selectOrderById">
            <id column="order_id" property="id" />
            <result column="order_name" property="name" />
            <result column="id" property="userId" />
        </collection>
    </resultMap>

    <select id="selectOrderById" resultType="com.seakoon.model.UserOrder">
        select id,name,user_id as userId from t_p_user_order where user_id = #{id}
    </select>

column 中, 需要填写 {属性名=列名(别名)} 的方式(如果只有一个参数, 也可以直接填列名传入参数的即可)。 如果是传递多个参数, 则是 {属性名1=列名1, 属性名2=列名2}。

select 属性值 com.homejim.mybatis.mapper.PostMapper.selectPostByBlogId, 对应的 parameterType 是 java.util.Map(也可以不填)。

fetchType 的属性值 lazy(延迟加载, 还需要相应的配置) 或 eager。

猜你喜欢

转载自blog.csdn.net/zlfjavahome/article/details/133383505