學會MyBatis中的級聯
一、概述
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>