![Java EE轻量级框架应用实战:SSM框架(Spring MVC+Spring+MyBatis)](https://wfqqreader-1252317822.image.myqcloud.com/cover/921/32517921/b_32517921.jpg)
4.4 多对多(collection)
在实际项目开发中,多对多的关联关系也是很常见的。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品就属于多对多的关联关系,如图4.6所示。
在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表中的订单id作为外键参照订单表的id,商品id作为外键参照商品表的id。这3个表之间的关系如图4.7所示。
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_69.jpg?sign=1739283747-tvvTX2RtXsDI9Aey97iRyRMUirjVnU4J-0-1f03698ce26482251e0a73e75221a291)
图4.6 订单和商品之间的关联关系
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_70.jpg?sign=1739283747-tQWOZ45B7mXvmulzMSxtGlP0GyqZmgKV-0-5a440cb90d78e8400513a17d0aa1c60a)
图4.7 数据库中订单表、中间表与商品表之间的关联
了解数据库中订单表与商品表之间的多对多关联关系后,下面就通过具体的案例来讲解如何使用MyBatis来处理这种多对多的关系。
4.4.1 应用案例:销售订单关联订购商品信息
下面通过一个示例来演示使用collection实现多对多的具体应用,示例需求:获取销售订单关联订购商品信息的列表,具体实现步骤如下。
(1)MyBatisUtils.java、mybatis-config.xml和log4j.properties等文件请参考相关项目内容,此处不再赘述。
(2)在cn.dsscm.pojo包中,创建持久化类Product,并在类中定义相关属性和方法,见示例23。
【示例23】 Product.java
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_72.jpg?sign=1739283747-ArqR0Zz8y4YA9BnZBgoinhYlLYmd4O25-0-49174e53e5ba82fd191772d5b9478838)
在cn.dsscm.pojo包中,创建持久化类Order,并在类中定义相关属性和方法,见示例24。
【示例24】 Order.java
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_74.jpg?sign=1739283747-kTzVw7Bt3iVn8GQcYltrgoLlAhWyjtuG-0-7cf425472063efb59f838731b592b026)
除了在商品持久化类中需要添加订单的集合属性,还需要在订单持久化类(Orders.java)中增加商品集合的属性及其对应的getter方法和setter方法,同时为了方便查看输出结果,还需要重写toString()方法,Order类中添加的代码如下所示:
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_75.jpg?sign=1739283747-GYwdxqEFVjaNpVxE3eFIeo7Hj6XzC89v-0-23bc64d73e2a2648b70303dff05cdfc2)
(3)在cn.dsscm.mapper包中,创建接口映射文件ProductMapper.java和订单实体映射文件ProductMapper.xml,见示例25和示例26。
【示例25】 ProductMapper.java
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_77.jpg?sign=1739283747-QgSug2HVMz3N74ShAjxvMQHrYOMFK5eC-0-60ecd4993936723d6feb956fe6cb28fb)
【示例26】 ProductMapper.xml
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_79.jpg?sign=1739283747-gwFw0lHFMRs4BKWM8AhfjKBD3J8LiEQS-0-e25c27d94baa0936212d473a35b39016)
在上面代码中,定义了一个id为getProduct的执行语句,该执行语句中的SQL会根据订单id查询与该订单所关联的商品信息。由于订单和商品是多对多的关联关系,所以需要通过中间表来查询商品信息。
(4)在cn.dsscm.mapper包中,创建接口映射文件OrdersMapper.java和订单实体映射文件OrdersMapper.xml,见示例27和示例28。
【示例27】 OrdersMapper.java
【示例28】 OrdersMapper.xml
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_83.jpg?sign=1739283747-IbrRSL2iGlaYx6XQ5wuVdcMKaJynLHHC-0-1d7590905a8741df1ebcbaaa44e788cd)
在上面代码中,使用嵌套查询的方式定义了一个id为getOrder1的select语句来查询订单及其关联的商品信息。在<resultMap>元素中使用了<collection>元素来映射多对多的关联关系,其中,property属性表示订单持久化类中的商品属性,ofType属性表示集合中的数据为Product类型,而column的属性值会作为参数执行ProductMapper中定义的id为getProduct的执行语句来查询订单中的商品信息。
(5)在测试类OrderMapperTest.java中,编写多对多关联查询的测试方法getOrderListTest1(),见示例29。
【示例29】 OrderMapperTest.java
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_85.jpg?sign=1739283747-9fBNaRcgnfEn9NmC7QWX3jl2ErmwfDLP-0-8969034e437b960224943a705d879669)
使用JUnit4执行getOrderListTest()方法后,控制台的输出结果如下所示:
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_86.jpg?sign=1739283747-0BWbnswPZnWR9EXqRow0ndZUo3s2b6E6-0-d86088e82cf3764393d66efa520b07ad)
从控制台的输出结果可以看出,使用MyBatis框架嵌套查询的方式执行了多条SQL语句,先查询订单表信息,再在订单查询的数据基础上对每一笔订单的商品查询其关联的商品信息,这就是MyBatis多对多的关联查询。
如果对多表关联查询的SQL语句比较熟,也可以在OrdersMapper.xml中使用嵌套结果的方式,见示例30和示例31。
【示例30】 OrdersMapper.java
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_88.jpg?sign=1739283747-ynUsjjXXAZw6Zy8J3Q1S51g8eYnrTAD3-0-7cbc32938f51a50d3af631c9439f1d0c)
【示例31】 OrdersMapper.xml
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_90.jpg?sign=1739283747-0ts4ju4XSEXqd1ugh8lt6KtaRXo2rVuE-0-e1f7ff9f232609c0d32c0f33f5438f78)
在测试类OrderMapperTest.java中,编写多对多关联查询的测试方法getOrderListTest2(),其代码同示例29。使用JUnit4执行getOrderListTest2()方法后,控制台的输出结果如下所示:
![img](https://epubservercos.yuewen.com/1B1F03/17545850907267806/epubprivate/OEBPS/Images/txt004_91.jpg?sign=1739283747-gdkkDsMl97AYCjTbhRiQoeov2peNPlGw-0-efca81efecc3a0933d34fc8b4ad8f79c)
从控制台的输出结果可以看出,使用MyBatis框架嵌套查询的方式执行了一条SQL语句,并查询出了订单及其关联的商品信息,这就是MyBatis框架多对多的关联查询。
综合比较上述完成多对多的实现案例,可能发现与一对多的实现区别不大,由于不管怎么进行关联都只能从一个角度看,所以在实际应用中只需要使用“一对一”与“一对多”两种方式即可。在使用嵌套查询与嵌套结果的两种处理方式中,推荐使用嵌套结果对查询结果进行处理,该方式操作更加方便。
4.4.2 技能训练
上机练习3 获取销售订单及其订单商品列表(collection)
需求说明
在上机练习2的基础上,结合嵌套查询与嵌套结果这两种方式,完成销售订单及其订单商品列表的多对多查询。