一、概述
MyBatis中的级联分为三种
- 鉴别器(discriminator): 根据一些条件决定实现类级联的方案。比如体检表需要根据性别区分。
- 一对一(association): 比如学生与学生证就是一对一的关系。
- 一对多(collection): 比如班级与学生就是一对多的关系。
MyBatis中没有多对多的级联,一般使用两个一对多的级联代替。
二、了解resultMap元素的作用
resultMap定义的主要是一个结果集的映射关系,也就是SQL到Java Bean的映射关系定义,它也支持级联等特性。现版本的MyBatis只支持resusltMap查询,不支持更新或则保存。
resultMap元素的子元素
<resultMap>
<constructor>
<idArg/>
<arg/>
</constructor>
<id />
<result />
<association />
<collection />
<discriminator>
<case/>
</discriminator>
</resultMap>
-
constructor:用于在实例化类时,注入结果到构造方法中
-
idArg:ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
-
arg:将被注入到构造方法的一个普通结果
-
id:一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
-
result:注入到字段或 JavaBean 属性的普通结果
-
association:一个复杂类型的关联。许多结果将包装成这种类型嵌套结果映射,关联可以指定为一个 resultMap 元素,或者引用一个
-
collection:一个复杂类型的集合。嵌套结果映射,集合可以指定为一个 resultMap 元素,或者引用一个
-
discriminator:使用结果值来决定使用哪个 resultMap
-
case:基于某些值的结果映射。嵌套结果映射一个 case 也是一个映射它本身的结果,因此可以包含很多相 同的元素,或者它可以参照一个外部的 resultMap。
三、案例
这里我们提供创建一个案例,创建了两个表customer表和invoice表,其中customer表和invoice表的关系是一对多。创建实体类时我们把invoice表中关于billing的信息提取出来创建一个BillingInfo实体类,其他的字段创建成一个实体类。这两个实体类之间的关系是一对一。注意阅读注释会有详细的解释
数据库
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`customer_id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(8) DEFAULT NULL,
`last_name` varchar(8) DEFAULT NULL,
`company` varchar(32) DEFAULT NULL,
`address` varchar(64) DEFAULT NULL,
`city` varchar(8) DEFAULT NULL,
`state` varchar(8) DEFAULT NULL,
`country` varchar(8) DEFAULT NULL,
`postal_code` char(6) DEFAULT NULL,
`phone` char(11) DEFAULT NULL,
`fax` varchar(16) DEFAULT NULL,
`email` varchar(16) DEFAULT NULL,
`support_repld` varchar(4) DEFAULT NULL,
PRIMARY KEY (`customer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `invoice`;
CREATE TABLE `invoice` (
`invoice_id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) DEFAULT NULL,
`invoice_date` datetime DEFAULT NULL,
`billing_address` varchar(32) DEFAULT NULL,
`billing_city` varchar(16) DEFAULT NULL,
`billing_state` varchar(4) DEFAULT NULL,
`billing_country` varchar(16) DEFAULT NULL,
`billing_postalcode` varchar(8) DEFAULT NULL,
`total` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`invoice_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
实体类
public class CustomerEntity {
int customerId;
String firstName;
String lastName;
String company;
String address;
String city;
String state;
String country;
String postalCode;
String phone;
String fax;
String email;
String supportRepld;
// 客户customer和发票invoice是一对多关系
List<InvoiceEntity> invoiceEntityList= null;
// ..省略get\set方法
}
public class BillingInfoEntity {
int invoiceId;
String billingAddress;
String billingCity;
String billingState;
String billingCountry;
String billingPostalCode;
// ..省略get\set方法
}
public class InvoiceEntity {
int invoiceId;
Date invoiceDate;
float total;
// 发票invoice和账单billing是一对一关系
BillingInfoEntity billingInfoEntity;
// 发票invoice和客户customer是一对一关系
CustomerEntity customerEntity;
// ..省略get\set方法
}
查询
这里我们对于resultMap使用级联采用了两种方式Nested ResultMap和Nested Select。
接口
public interface SelectMapper {
// Nested ResultMap方式定义的方法
InvoiceEntity getInvoiceByInvoiceId(int id);
InvoiceEntity getInvoiceForCustomerByInvoiceId(int id);
CustomerEntity getCustomerById(int id);
// Nested Select方式定义的方法
CustomerEntity getCusById(int id);
List<InvoiceEntity> getIncByCustomerId(int id);
List<CustomerEntity> getAll();
}
映射器
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lzx.select.mappers.SelectMapper">
<!-- Nested ResultSet(多用于多表连接查询,只需查询一次即可,但是语句复杂)-->
<!--
根据实体类InvoiceEntity中描述的关系创建一个新的结果集,association适用于一对一的关系。
在这里,invoice里面的字段mybatis会自动帮我们映射,前提是你的数据库命名和字段的命名要符合规范
-->
<!--
type属性为resultMap的映射的实体类,可以使用完全限定名,也可以使用别名
autoMapping为是否启动自动映射,默认值是false,不启动
-->
<resultMap id="invoiceAndBilling" type="InvoiceEntity" autoMapping="true">
<!--association用于一对一级联,property是pojo中的属性。javaType是java这边的类型。 -->
<association property="billingInfoEntity" javaType="BillingInfoEntity" autoMapping="true"/>
<!--resultMap之间的相互引用-->
<association property="customerEntity" resultMap="customerInfo"/>
</resultMap>
<!--
根据实体类CustomerEntity中描述的关系创建一个新的结果集,collection适用于一对多的关系。
-->
<resultMap id="customerInfo" type="CustomerEntity" autoMapping="true">
<!--这里的ofType等同于association中的javaType-->
<collection property="invoiceEntityList"
resultMap="invoiceAndBilling" ofType="InvoiceEntity" />
</resultMap>
<select id="getInvoiceByInvoiceId" resultMap="invoiceAndBilling">
select * from invoice where invoice_id = #{id}
</select>
<select id="getInvoiceForCustomerByInvoiceId" resultMap="invoiceAndBilling">
select
i.invoice_id,
i.invoice_date,
i.billing_address,
i.billing_city,
i.billing_state,
i.billing_country,
i.billing_postalCode,
i.total,
c.*
from invoice i
left join customer c
on c.customer_id = i.invoice_id
where i.invoice_id = #{id}
</select>
<select id="getCustomerById" resultMap="customerInfo">
select *
from invoice i
left join customer c
on c.customer_id = i.invoice_id
where c.customer_id = #{id}
</select>
<!--Nested Select (也可用于多表连接查询,但是要查询多次,效率低,但是SQL语句简单)-->
<resultMap id="invoiceAndCustomer" type="InvoiceEntity" autoMapping="true">
<association property="billingInfoEntity" javaType="BillingInfoEntity" autoMapping="true"/>
<!--select属性级联到customer的查询方法,column属性代表传入的参数-->
<association property="customerEntity" column="customer_id"
select="com.lzx.select.mappers.SelectMapper.getCusById"/>
</resultMap>
<resultMap id="customerAndInvoice" type="CustomerEntity" autoMapping="true">
<collection property="invoiceEntityList" column="customer_id"
select="com.lzx.select.mappers.SelectMapper.getIncByCustomerId"/>
</resultMap>
<select id="getIncByCustomerId" resultMap="invoiceAndCustomer" >
select * from invoice where customer_id = #{id}
</select>
<select id="getCusById" resultMap="customerAndInvoice">
select * from customer where customer_id = #{id}
</select>
<select id="getAll" resultType="com.lzx.select.entity.CustomerEntity">
select * from customer
</select>
</mapper>