4.3 一对多(collection)
与一对一的关联关系相比,开发人员接触更多的关联关系是一对多(或多对一)。如一个用户可以有多个订单,同时多个订单归一个用户所有。用户和订单的关联关系如图4.5所示。那么如何使用MyBatis框架处理这种一对多关联关系呢?在讲解过的<resultMap>元素中,包含一个<collection>子元素,MyBatis框架就是通过该子元素来处理一对多关联关系的。<collection>元素的大部分属性与<association>子元素相同,但其还包含一个特殊属性—ofType。ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。
图4.5 用户和订单的关联关系
<collection>元素的使用非常简单,可以参考如下两种示例进行配置,具体代码如下:
collection的作用和association的作用都是映射到JavaBem的某个“复杂类型”属性,只不过这个属性是一个集合列表,即JavaBean内部嵌套一个复杂数据类型(集合)属性。
4.3.1 应用案例:用户角色关联用户信息
下面通过一个示例来演示collection的具体应用,示例需求:获取指定用户角色类型的相关用户信息列表。从不同角度出发映射关系的使用不同,可使用用户角色表关联用户表来实现一对多关联,具体实现步骤如下。
(1)MyBatisUtils.java、mybatis-config.xml和log4j.properties等文件请参考相关项目内容,此处不再赘述。
(2)创建POJO:Role.java,可根据数据库表(tb_role)设计相应的属性,并增加getter方法和setter方法,见示例15。
【示例15】 Role.java
通过以上改造,在JavaBean: Role对象内部嵌套了一个复杂数据类型的属性(users)。
(3)接下来在RoleMapper接口中增加根据用户id获取用户信息,以及地址列表的方法,见示例16。
【示例16】 RoleMapper.java
(4)修改对应RoleMapper.xml,以增加getRole。该select查询语句返回类型为resultMap,并且引用外部的resultMap类型为role。由于role对象内嵌集合对象(users),因此需要使用collection来实现结果映射,见示例17。
【示例17】 RoleMapper.xml
其中,使用collection元素嵌套结果的方式处理同使用association元素一样,因此,使用嵌套结果,或者从连接中嵌套查询,这两种使用方式类似,由于在实战项目中以使用嵌套结果的方式为主,嵌套查询的使用此处不再赘述。
(5)最后修改测试类UserMapperTest.java,以增加测试方法getRoleListTest(),见示例18。
【示例18】 UserMapperTest.java
在测试方法中调用getRoleListTest()方法获取userList,并进行结果输出,关键是映射用户角色的相关信息,结果输出如下:
从运行结果可以看出,使用MyBatis框架嵌套结果的方式查询出了用户权限及其关联的用户集合信息。这就是MyBatis框架一对多的关联查询。
需要注意的是,上述案例如果从用户权限的角度出发,用户权限与用户之间是一对多的关联关系,但如果从单个用户的角度出发,一个用户只能属于一个用户权限,即一对一的关联关系。可根据已学内容实现单个用户与用户权限之间的一对一关联关系。
4.3.2 应用案例:商品类型关联商品信息
下面再通过一个示例来演示collection的具体应用,示例需求:获取指定商品类型的相关商品信息列表,具体实现步骤如下。
(1)MyBatisUtils.java、mybatis-config.xml和log4j.properties等文件请参考前面项目内容,此处不再赘述。
(2)创建POJO:ProductCategory.java,可根据数据库表(tb_product_category)设计相应的属性,并增加getter方法和setter方法,见示例19。
【示例19】 ProductCategory.java
通过以上改造,在JavaBean:ProductCategory对象内部嵌套了一个复杂数据类型的属性(products)。
(3)接下来在接口ProductMapper.java中增加根据用户id获取用户信息以及地址列表的方法,见示例20。
【示例20】 ProductMapper.java
(4)修改对应的ProductMapper.xml,以增加getProduct。该select查询语句返回类型为resultMap,并且引用外部的resultMap类型为ProductCategory。由于ProductCategory对象内嵌集合对象(Product),因此需要使用collection来实现结果映射,见示例21。
【示例21】 ProductMapper.xml
根据上述代码,简单分析collection的属性。
①ofType:指完整Java类名或者别名,即集合所包含的类型。此处为Product。
②property:指映射数据库列的实体对象属性。此处为在ProductCategory里定义的属性(products)。
对于collection的这段代码:
可以理解为一个名为products,且元素类型为ProductCategory的ArrayList集合。
collection与association的功能基本一致,此处不再赘述。
(5)最后修改测试类ProductMapperTest.java,以增加测试方法getProductListTest(),见示例22。
【示例22】 ProductMapperTest.java
在测试方法中调用getProductListTest()方法获取pList,并进行结果输出,关键是映射商品的相关信息,需要进一步循环productCategory.getProducts()进行输出。
通过上述代码不难发现,同association元素的resultMap属性用法基本是一样的,在此不再赘述。
4.3.3 技能训练
上机练习2 获取供应商及其采购订单列表(collection)
需求说明
(1)在上机练习1的基础上,根据指定的供应商(id)查询出其相关信息,以及其中所有的订单列表。
(2)查询结果列显示:供应商id、供应商编码、供应商名称、供应商联系人、供应商联系电话、订单列表信息(订单编码、商品名称、订单金额、是否付款)。
(3)在resultMap中使用collection子元素完成内部嵌套。
(1)修改Provider类,以增加集合类型属性(List<Bill>billList)。
(2)编写SQL查询语句(连表查询)。
(3)创建resultMap自定义映射结果,并在select中引用。