MyBatis學習(四):一對多、多對一、多對多

在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可以重名嗎?不可以
如果可以的話根據傳入參數的不同會不會是重載呢?不會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章