在JAVA實體類中,類與類的一對多、多對一、多對多關係通過組合的形式實現;
在數據庫中,表之間的一對多、多對一、多對多的關係通過使用外鍵來實現;
一對多、多對一、多對多關係的查詢都通過多表查詢完成。
其中多對多的關係都需要通過一箇中間類/表來實現,比如產品和訂單之間,一個訂單中可以有多個產品,一個產品也可以存在與多個訂單之中,這種情況就稱爲多對多的關係,要建立兩者之間的連接,需要建立一箇中間類/表(訂單項),一個訂單項實例包含自身屬性和產品屬性、訂單屬性。
MyBatis一對多、多對一關係的實現
一對多關係和多對一關係實質上是兩個實體類(數據庫表之間的關係),比如一個分類中包含多個產品,那麼分類和產品之間就是一對多的關係;反過來想,就會有多個產品對應一個分類(假如產品只屬於一個分類),這就是一對多的關係和多對一的關係。
一對多
那麼在JAVA類層面和數據庫層面怎麼實現這種關係呢?對於JAVA類層面,分類中含有多種產品,那麼在代表分類的JAVA類中添加一個產品類的List對象;對於數據庫層面,在一對多中對應的“多”的表中(產品表)增加對應的“一”的主鍵列作爲外鍵,如此便建立了一對多的關係。
如果只是查詢一對多的關係,只通過一查多,配置已經完成,我們可以去簡歷XML映射文件添加查詢語句,但是此處要注意,因爲是數據庫表之間是通過外鍵建立聯繫,只是通過一張表無法查詢到對應關係的全部信息,因此只能通過多表查詢來完成。
除此之外,我們還要注意到,MyBatis的工作方式是將查詢到的字段和返回值類型中的屬性進行匹配,要求實體類屬性和表列名必須一致(可以定義這種一致性,在總配置文件中可以選擇不同的屬性和列的匹配方式),現在,多表查詢,如果兩個表中出現了重複的列呢?兩個表中的所有列,如何才能和一個實體類進行匹配呢(包含另一個實體類,但是存儲的是類對象,SQL查詢結果都爲屬性列)?這裏就需要我們自己定義resultMap對象,用於匹配查詢結果和類中的屬性。以上述分類和產品爲例,定義JAVA類如下:
public class Category {
private int id;
private String name;
List<Product> products;
//省略get set方法
}
定義產品類如下:
public class Product {
private int id;
private String name;
private float price;
}
數據庫表結構如下:(分別爲分類表和產品表)
與分類的實體類相映射的XML文件中查詢分類所定義的產品:
注意:所有的屬性都需要配置!!!即使不重名的,否則查詢結果不會導入屬性中
只有在SQL語句中不查詢的列可以不配置(不需要的列信息)
<!-- 一對多查詢 -->
<!-- 配置resultMap
type指示此返回結果的類型
id爲本個resultMap的標識 用於在查詢標籤的resultMap屬性中使用
id爲主鍵元素 result爲非主鍵元素
column行指代的爲查詢結果表中生成的列名稱(在SQL語句中使用的列別名)
property爲java實體類中的字段值 -->
<resultMap type="Category" id="categoryBean">
<id column="cid" property="id" />
<result column='cname' property="name"/>
<!-- 一對多的關係 collection配置分類 的實體類中的產品屬性-->
<!-- property: 指的是集合屬性的值, ofType:指的是集合中元素的類型 -->
<collection property="products" ofType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</collection>
</resultMap>
<!--查詢語句如下-->
<select id="listCategory" resultMap="categoryBean">
select c.id as cid,c.name as cname,p.id as pid,p.name as pname,p.price from category as c left join product as p on c.id = p.id
</select>
多對一
在JAVA類中如何配置多對一的關係呢?多對一就是多個產品擁有共同的分類,但這並不影響他們自己擁有分類,因此只需要在產品類中添加分類對象即可。至於在數據庫的層面,我們知道一對多的關係中已經通過外鍵將兩張表聯繫起來,因此並不需要添加其他表列。
現在,產品類中也多出了類屬性,在查詢時MybBatis不能將連接查詢的表列自動匹配給實體類屬性的屬性,同樣需要配置resultMap
修改後Product類如下:
public class Product {
private int id;
private String name;
private float price;
private Category category;
}
配置與產品類相對應的XML映射文件如下:
<!-- 多對一關係展示 -->
<resultMap type="Product" id="productBean">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<!-- 即使列名和屬性名相同 也要配置 否則resultMap裏回缺少此項的值 而是默認值 -->
<result column="price" property="price"/>
<!-- 多對一的關係 使用association-->
<!-- property: 指的是屬性名稱, javaType:指的是屬性的類型 -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<!-- select 查詢語句 -->
<select id="listProduct" resultMap="productBean">
select p.id as pid,p.name as pname,p.price,c.id as cid,c.name as cname from product as p,category as c where p.cid=c.id
</select>
關於多對多關係爲什麼要創建中間項?
從類的角度理解爲,一個訂單類包括多個產品類,而一個產品類又包括多個訂單類,enmmm好像沒有什麼不好,有沒有大佬解答下。
從數據庫表的角度理解,可以減少信息的冗餘,產品裏面只存儲所有的產品,訂單類只存儲所有的訂單,在訂單中沒有產品ID,在產品表中沒有訂單ID, 產品和訂單之間的聯繫通過訂單項建立(在一對多和多對一的關係中,我們通過在對應的“多”表中添加了對應的“一”表列)。
這也是數據庫設計上要求的!!!
多對多的關係分解爲兩個一對多、多對一的關係,一個訂單對應多個訂單項,一個訂單項對應一個訂單、一個產品,一個產品對應多個訂單項。
建立如下java類:
public class Order {
private int id;
private String code;
private List<OrderItem> orderItems;
}
public class OrderItem {
private int id;
private int number;
private Order order;
private Product product;
}
數據庫表如下(product、orderItem、order):
通過訂單查詢訂單中的產品(實質爲查詢訂單類屬性信息,因此需要配置與訂單類重名的XML映射文件):
<?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.cn.bean">
<!-- 多對多關係演示 -->
<!-- type返回的對象類型 id命名標識 -->
<resultMap type="Order" id="orderBean">
<!-- Order類的非類屬性 -->
<!-- Order表主鍵 column:查詢結果中取的列別名 property:對應Order類中的信息-->
<id column="oid" property="id"/>
<!-- 沒有取別名的屬性也需要進行配置 -->
<result column="code" property="code"/>
<!-- 對應多個訂單項 使用collection-->
<!-- Order類中屬性 屬性所對應的類型 -->
<collection property="orderItems" ofType="OrderItem">
<!-- 可以不配置不需要的列 -->
<!-- OrderItem中沒有什麼有用的信息 比如id信息對於我們來說意義不大 不配置此列 -->
<id column="oiid" property="id"/>
<!--!!!!不配置此列會出現查詢結果不全的情況 原因未知!!!!!! -->
<result column="number" property="number"/><!-- 產品數量 -->
<!-- OrderItem和Product的一對多關係 -->
<association property="product" javaType="Product">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>
<!-- 查詢所有的訂單 -->
<select id="listOrder" resultMap="orderBean">
select o.id as oid,o.code,oi.number,p.id as pid,p.name,p.price from `order` as o left join orderItem as oi on o.id=oi.oid inner join product as p on oi.pid=p.id
</select>
</mapper>
前面說不查詢的列可以不配置,但是多對多的關係中,中間表的主鍵項如果不配置的話,就會出現查詢結果部分丟失的情況,原因未知。
並不是主鍵必須配置,如果刪除掉resultMap中產品表中關於id的配置,也能夠成功查詢,可能是因爲中間表是個特例?等一個大佬。。。
最後,一個可有可無的提醒,千萬記得將映射文件加入mybatis的總配置文件中0.0
思考一個問題,映射文件必須與實體類重名嗎?不一定
映射文件中各種標籤的id可以重名嗎?不可以
如果可以的話根據傳入參數的不同會不會是重載呢?不會