1. 程式人生 > >Hibernate中多表設計的一對多,多對一的對映檔案的配置

Hibernate中多表設計的一對多,多對一的對映檔案的配置

一對多(客戶表與聯絡人表為例):

一對多關係中習慣性的把一方稱為主表,把多方稱為從表,外來鍵指的是從表中有一列,取值參照主表的主鍵,這一列就是外來鍵。

舉例:一方為客戶,多方為聯絡人

在客戶的實體類中需要配置一個set集合,包含多個聯絡人。

private Set<LinkMan>linkmans = new HashSet<LinkMan>();

在聯絡人的實體類中配置一個客戶物件

private Customer customer;//用它的主鍵,對應聯絡人表中的外來鍵

客戶的配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.shuai.domain">
	<class name="Customer" table="cst_customer">
		<id name="custId" column="cust_id">
			<generator class="native"></generator>
		</id>
		<property name="custName" column="cust_name"></property>
		<property name="custLevel" column="cust_level"></property>
		<property name="custSource" column="cust_source"></property>
		<property name="custIndustry" column="cust_industry"></property>
		<property name="custAddress" column="cust_address"></property>
		<property name="custPhone" column="cust_phone"></property>
		<!-- 一對多關係對映 
			涉及的標籤
				set:用於對映set集合屬性
					屬性:
					  name:指定集合屬性的名稱
					  table:在一對多的時候寫不寫都可以。
					  		它指定的是集合元素所對應的表
				one-to-many:用於指定當前對映配置檔案所對應的實體和集合元素所對應的實體是一對多關係。
					屬性:
					   class:指定集合元素所對應的實體類名稱。
				key:用於對映外來鍵欄位的。
					屬性:
					   column:指定從表中的外來鍵欄位名稱
		-->
		<set name="linkmans" table="cst_linkman">
			<key column="lkm_cust_id"></key>
			<one-to-many class="LinkMan"/>
		</set>
	</class>
</hibernate-mapping>

聯絡人的配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.shuai.domain">
	<class name="LinkMan" table="cst_linkman">
		<id name="lkmId" column="lkm_id">
			<generator class="native"></generator>
		</id>
		<property name="lkmName" column="lkm_name"></property>
		<property name="lkmGender" column="lkm_gender"></property>
		<property name="lkmPhone" column="lkm_phone"></property>
		<property name="lkmMobile" column="lkm_mobile"></property>
		<property name="lkmEmail" column="lkm_email"></property>
		<property name="lkmPosition" column="lkm_position"></property>
		<property name="lkmMemo" column="lkm_memo"></property>
		<!-- 多對一關係對映 
			涉及的標籤:
				many-to-one:用於建立多對一的關係對映配置
					屬性:
					  name:指定的實體類中屬性的名稱
					  class:該屬性所對應的實體類名稱。如果在hibernate-mapping上沒有導包,則需要寫全限定類名
					  column:指定從表中的外來鍵欄位名稱
		-->
		<many-to-one name="customer" class="Customer" column="lkm_cust_id" />	</class>
</hibernate-mapping>

多對多(使用者表與角色表為例):

一個使用者可以具備多個角色,一個角色也可以賦予多個使用者,使用者和角色之間的關係是多對多,多對多的實現方式靠的是第三張表,也叫中間表,裡面包含了兩個欄位,分別引用各自的主鍵,同時這兩個欄位還是聯合主鍵,其中使用者表和中間表的關係是一對多,角色表和中間表的關係也是一對多。

 一個使用者可以具有多個角色,所以在使用者實體類中應該包含多個角色的資訊。

使用者的實體類中定義一個set集合用於存放多個聯絡人:

private Set<SysRole>roles = new HashSet<SysRole>(0);

一個角色可以賦予多個使用者,所以在角色的實體類中應該包含多個使用者的資訊。

角色的實體類中定義一個set集合用於存放多個使用者:

private Set<SysUser>users = new HashSet<SysUser>(0);

使用者的配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.shuai.domain">
	<class name="SysUser" table="sys_user">
		<id name="userId" column="user_id">
			<generator class="native"></generator>
		</id>
		<property name="userCode" column="user_code"></property>
		<property name="userName" column="user_name"></property>
		<property name="userPassword" column="user_password"></property>
		<property name="userState" column="user_state"></property>
		<!-- 多對多關係對映 
			涉及的標籤:
				set:用於對映集合屬性
					屬性:
					  name:指定集合屬性的名稱
					  table:指定的是中間表的名稱,在多對多的配置時,必須寫。
				 key:指定外來鍵欄位
				 	屬性:
				 	   column:指定的是當前對映檔案所對應的實體在中間表的外來鍵欄位名稱
				 many-to-many:指定當前對映檔案所對應的實體和集合元素所對應的實體是多對多的關係
				 	屬性:
				 		class:指定集合元素所對應的實體類
				 		column:指定的是集合元素所對應的實體在中間表的外來鍵欄位名稱
				 	
				 	
		-->
		<set name="roles" table="user_role_rel">
			<key column="user_id"></key>
			<many-to-many class="SysRole" column="role_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

角色配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.shuai.domain">
	<class name="SysRole" table="sys_role">
		<id name="roleId" column="role_id">
			<generator class="native"></generator>
		</id>
		<property name="roleName" column="role_name"></property>
		<property name="roleMemo" column="role_memo"></property>
		<!-- 多對多關係對映 -->
		<set name="users" table="user_role_rel">
			<key column="role_id"></key>
			<many-to-many class="SysUser" column="user_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

儲存時遇到的問題:

因為雙向維護了關係,而且持久態物件可以自動更新資料庫,更新客戶的時候會修改一次外來鍵,更新聯絡人的時候同樣也會修改一次外來鍵,這樣就會產生多餘的sql,解決的方法很簡單,只需要將一方放棄外來鍵的維護權即可,也就是關係不是雙方維護的,只需要交給某一方維護就可以了,通常我們都是交給多的一方去維護的。舉個例子,一個老師對應多個學生,一個學生對應一個老師,這是典型的一對多。那麼一個老師如果要記住所有學生的名字很難的,但如果讓每個學生記住老師的名字應該不難。其實就是這個道理。

所以在一對多中,一的一方都會放棄外來鍵的維護權(關係的維護)。這個時候如果想讓一的一方放棄外來鍵的維護權,只需要進行如下的配置即可。

解決多一條update語句的問題:
<set name="linkmans" table="cst_linkman" inverse="true">
		<key column="lkm_cust_id"></key>
		<one-to-many class="LinkMan"/>
</set>

inverse的預設值是false,代表不放棄外來鍵維護權,配置值為true,代表放棄外來鍵的維護權。

修改中遇到的問題:

什麼是級聯操作:

級聯操作是指當主控方執行儲存、更新或者刪除操作時,其關聯物件(被控方)也執行相同的操作。在對映檔案中通過對cascade屬性的設定來控制是否對關聯物件採用級聯操作,級聯操作對各種關聯關係都是有效的。

級聯操作的方向性:

級聯是有方向性的,所謂的方向性指的是,在儲存一的一方級聯多的一方和在儲存多的一方級聯一的一方

【儲存客戶級聯聯絡人】

首先要確定我們要儲存的主控方是那一方,我們要儲存客戶,所以客戶是主控方,那麼需要在客戶的對映檔案中進行如下的配置。

程式碼如下:

解決報錯的問題,配置級聯儲存更新:
<set name="linkmans" table="cst_linkman" cascade="save-update" inverse="true">
	<key column="lkm_cust_id"></key>
	<one-to-many class="LinkMan"/>
</set>

刪除操作:

刪除從表資料,可以隨意的刪除

刪除主表的資料:

(1)預設情況下會把外來鍵欄位先置為null,然後刪除從表資料,如果在資料庫的表結構上,外來鍵欄位有非空約束,預設情況就會報錯了。

(2)如果配置了放棄維護關聯關係的權利,則不能刪除,因為在刪除的時候它根本不會去更新從表的外來鍵欄位。

(3)如果還想刪除,使用級聯刪除。在實際開發中,級聯刪除請慎用。

級聯刪除的配置:
<set name="linkmans" table="cst_linkman" cascade="delete" inverse="true">
	<key column="lkm_cust_id"></key>
	<one-to-many class="LinkMan"/>
</set>

如果想有之前的級聯儲存或更新,同時還想有級聯刪除,如下:

級聯刪除的配置:
<set name="linkmans" table="cst_linkman" cascade="delete,save-update" inverse="true">
	<key column="lkm_cust_id"></key>
	<one-to-many class="LinkMan"/>
</set>