【MyBatis学习11】关联关系collection:1对多关联的两种方法

本文博客地址:http://blog.csdn.net/soonfly/article/details/63688582 (转载请注明出处)

接着上文《关联关系association:1对1关联的三种方法 》的例子
一对多关系
[order表]–>[order_detail表]是1对多(1.n)关系。1个订单可以由多个订单分录(购买的商品)组成的。1对多关联关系可以理解为一个对象的内部有一个列表。
在后台系统中我们点击订单列表会进到订单详情页
这里写图片描述

有两种方式可以实现:

一、(推荐)用sql联合查询,使用collection标签

sql查询语句是这样的:

SELECT
    `order`.*,`user`.username,`user`.address,`user`.cellphone,
    `order_detail`.productname,`order_detail`.price,`order_detail`.num
    FROM `order`
    ,`user`,`order_detail` 
    WHERE `order`.create_userid=`user`.id AND
    `order_detail`.`order_id`=`order`.id AND 
    `order`.orderno='M201209012578917'

step1.我们已经有一个与表对应的Order类,但是没有分录列表字段。所以在Order类里新增List<OrderDetail> orderdetailList属性:

public class Order {
    Integer id;
    String orderno;
    Integer create_time;
    Integer create_userid;
    User user;

    /* 添加分录列表 */
    List<OrderDetail> orderdetailList;

    /*下面get和set方法*/
    getter and setter....
}

step2.创建接口及Xml
在twm.mybatisdemo.mapper包下的OrderMapper.xml和OrderMapper.java的基础上改进。
OrderMapper.java里添加方法:

//查询单个订单详情,关联查询用户信息以及订单明细
public List<Order> getDetailByOrderno() throws Exception;

OrderMapper.xml里添加一个resultMap和一个select:

<resultMap type="Order" id="Contain_DetailList_OrderMap" extends="OrderMap">
    <!-- order中订单信息字段及关联的用户字段,和上面的OrderMap映射一样,这里只是扩充订单明细,extends继承上面的即可 -->
    <!-- 配置关联订单明细信息 -->
    <collection property="orderdetailList" ofType="OrderDetail">
        <id column="id" property="id" />
        <result column="productname" property="productname" />
        <result column="num" property="num" />
        <result column="price" property="price" />
    </collection>
</resultMap>

<select id="getDetailByOrderno" parameterType="String" resultMap="Contain_DetailList_OrderMap">
    SELECT
    `order`.*,`user`.username,`user`.address,`user`.cellphone,
    `order_detail`.productname,`order_detail`.price,`order_detail`.num
    FROM `order`
    ,`user`,`order_detail` 
    WHERE `order`.create_userid=`user`.id AND
    `order_detail`.`order_id`=`order`.id AND 
    `order`.orderno=#{orderno}
</select>

step3.调用

public static void main(String[] args) throws Exception {
        SqlSession session = SqlSessionAssist.getSession();

        OrderMapper ordermapper = session.getMapper(OrderMapper.class);
        Order order = ordermapper.getDetailByOrderno("M201209012578917");
        System.out.println(order.getOrderno() + ","
                + order.getUser().getUsername() + ","
                + order.getUser().getAddress() + ","
                + order.getUser().getCellphone());
        List<OrderDetail> detailList=order.getOrderdetailList();
        for (OrderDetail orderDetail : detailList) {
            System.out.println(orderDetail.getProductname() + ","
                    + orderDetail.getNum() + ","
                    + orderDetail.getPrice());
        }
    }

结果:

M201209012578917,高天仪,北冥山庄,18911110000 iphone 8 plus,3,5700.0

擦~怎么只有一条明细呢?

看mybatis的日志

DEBUG [main] - ==> Preparing: SELECT
order.*,user.username,user.address,user.cellphone,
order_detail.productname,order_detail.price,order_detail.num
FROM order ,user,order_detail WHERE
order.create_userid=user.id AND
order_detail.order_id=order.id AND order.orderno=? DEBUG
[main] - ==> Parameters: M201209012578917(String) DEBUG [main] - <==
Total: 2

SQL查询出来确实是2条啊。
上网搜了一下,发现问题出在<id column="id" property="id" />
如果两表联查,主表和明细表的主键都是id的话,明细表的多条只能查询出来第一条。
解决办法:在SQL中加上order_detail.id as detail_id,定义一个别名<id column="detail_id" property="id" />
再把<id column="id" property="id" />改为<id column="detail_id" property="id" />

千万不要试图删了Contain_DetailList_OrderMap里的<id column="id" property="id" />这句。这样没有主键,系统不会distinct去重记录的。

二、(不推荐)不使用sql联合查询,通过collection的延迟加载来实现
在上面的基础上修改:
step1.添加select片段,根据订单id,查询所有订单明细记录

<select id="SelectOrderDetail" parameterType="int" resultType="OrderDetail">
    select * from `order_detail` where order_id=#{id}
</select>

step2.修改collection
collection的几个属性:
property:指定内部对象属性名
javaType:内部映射的对象的类型。
column:要传给select语句的参数,相当于指定外键字段。
select:指定用户查询语句的ID

<resultMap type="Order" id="Contain_DetailList_OrderMap" extends="OrderMap">
    <!-- order中订单信息字段及关联的用户字段,和上面的OrderMap映射一样,这里只是扩充订单明细,extends继承上面的即可 -->
    <!-- 配置关联订单明细信息 -->
    <collection property="orderdetailList" select="SelectOrderDetail" column="id" />
</resultMap>

step3.修改getDetailByOrderno的select片段,删除联查order_detail相关的部分

<select id="getDetailByOrderno" parameterType="String" resultMap="Contain_DetailList_OrderMap">
    SELECT `order`.*,`user`.username,`user`.address,`user`.cellphone    
    FROM `order` ,`user`
    WHERE `order`.create_userid=`user`.id and
    `order`.orderno=#{orderno}
</select>

运行结果:

DEBUG [main] - ==> Preparing: SELECT
order.*,user.username,user.address,user.cellphone FROM order
,user WHERE order.create_userid=user.id and order.orderno=?
DEBUG [main] - ==> Parameters: M201209012578917(String) DEBUG [main] -
====> Preparing: select * from order_detail where order_id=? DEBUG [main] - ====> Parameters: 1(Integer) DEBUG [main] - <==== Total:
2 DEBUG [main] - <== Total: 1
M201209012578917,高天仪,北冥山庄,18911110000 iphone 8 plus,3,5700.0
华为G7,2,2700.0

可以看到存在N+1问题,只不过这种场景下N永远是1。
原来用一个联合查询可以解决的,现在需要用两次查询。

本文博客地址:http://blog.csdn.net/soonfly/article/details/63688582 (转载请注明出处)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值