Hibernate學習筆記(四)—— 表與表的關係
一、一對多|多對一
1.1 關係表達
1.1.1 表中的表達
建表原則:在多的一方建立外來鍵指向一的一方的主鍵。
1.1.2 實體中的表達
【客戶實體】
public class Customer { private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; privateString cust_linkman; private String cust_phone; private String cust_mobile; // 一個客戶有多個聯絡人:客戶中應該放有聯絡人的集合 private Set<LinkMan> linkMans = new HashSet<>(); get/set... }
【聯絡人實體】
public class LinkMan { private Long lkm_id; private String lkm_name;private String lkm_gender; private String lkm_phone; private String lkm_mobile; private String lkm_email; private String lkm_qq; private String lkm_position; private String lkm_memo; // Hibernate是一個ORM框架:在關係型資料庫中描述表與表之間的關係,使用的是外來鍵。開發語言使用的是Java,面向物件的 privateCustomer customer;
get/set...
}
1.1.3 配置檔案中的表達
【客戶的對映】
<?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="cn.itcast.domain" > <class name="Customer" table="cst_customer" > <id name="cust_id" > <generator class="native"></generator> </id> <property name="cust_name" column="cust_name" ></property> <property name="cust_source" column="cust_source" ></property> <property name="cust_industry" column="cust_industry" ></property> <property name="cust_level" column="cust_level" ></property> <property name="cust_linkman" column="cust_linkman" ></property> <property name="cust_phone" column="cust_phone" ></property> <property name="cust_mobile" column="cust_mobile" ></property> <!-- 配置關聯物件 --> <!-- name屬性:多的一方集合的屬性名稱 --> <set name="linkMans"> <!-- column屬性:多的一方外來鍵的名稱 --> <key column="lkm_cust_id"></key> <!-- 多的一方類的全路徑,如果前面的package中設定了包名,這裡填類名即可 --> <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="cn.itcast.domain" > <class name="LinkMan" table="cst_linkman" > <id name="lkm_id" > <generator class="native"></generator> </id> <property name="lkm_name" column="lkm_name" ></property> <property name="lkm_gender" column="lkm_gender" ></property> <property name="lkm_phone" column="lkm_phone" ></property> <property name="lkm_mobile" column="lkm_mobile" ></property> <property name="lkm_email" column="lkm_email" ></property> <property name="lkm_qq" column="lkm_qq" ></property> <property name="lkm_position" column="lkm_position" ></property> <property name="lkm_memo" column="lkm_memo" ></property> <!-- 配置關聯物件 --> <!-- name:一的一方的物件的名稱 class:一的一方類的全路徑 column:表中外來鍵的名稱 --> <many-to-one name="customer" class="Customer" column="lkm_cust_id"/> </class> </hibernate-mapping>
1.2 測試程式碼
- 儲存
// 儲存一個客戶和兩個聯絡人 @Test public void testSave() throws Exception { // 獲得session Session session = HibernateUtils.openSession(); // 開啟事務 Transaction tx = session.beginTransaction(); // 建立一個客戶 Customer customer = new Customer(); customer.setCust_name("李總"); // 建立兩個聯絡人 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkm_name("張祕書"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("王助理"); // 表達一對多,客戶下有多個聯絡人 customer.getLinkMans().add(linkMan1); customer.getLinkMans().add(linkMan2); // 表達多對一,聯絡人屬於哪個客戶 linkMan1.setCustomer(customer); linkMan2.setCustomer(customer); session.save(customer); session.save(linkMan1); session.save(linkMan2); // 提交事務 tx.commit(); // 關閉資源 session.close(); }
- 增加
// 為客戶增加聯絡人 @Test public void testAdd() throws Exception { // 獲得session Session session = HibernateUtils.openSession(); // 開啟事務 Transaction tx = session.beginTransaction(); // 獲得要操作的客戶物件 Customer customer = session.get(Customer.class, 1l); // 建立聯絡人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("趙祕書"); // 將聯絡人新增到客戶中 customer.getLinkMans().add(linkMan); // 將客戶設定到聯絡人中 linkMan.setCustomer(customer); // 執行儲存 session.save(linkMan); tx.commit(); session.close(); }
- 刪除
// 為客戶刪除聯絡人 @Test public void testDelete() throws Exception { // 獲得session Session session = HibernateUtils.openSession(); // 開啟事務 Transaction tx = session.beginTransaction(); // 獲得要操作的客戶物件 Customer customer = session.get(Customer.class, 1l); // 獲得要移除的聯絡人 LinkMan linkMan = session.get(LinkMan.class, 2l); // 將聯絡人從客戶集合中移除 customer.getLinkMans().remove(linkMan); linkMan.setCustomer(null); tx.commit(); session.close(); }
1.3 進階操作
級聯操作是指當主控方執行儲存、更新或者刪除操作時,其關聯的物件(被控方)也執行相同的操作。在對映檔案中通過對cascade屬性的設定來控制是否對關聯物件採用級聯操作,級聯操作對各種關聯關係都是有效的。
1.3.1 級聯儲存或更新
級聯是有方向性的,所謂的方向性指的是,在儲存一的一方級聯多的一方,和在儲存多的一方級聯一的一方。
【儲存客戶級聯聯絡人】
首先要確定我們要儲存的主控方是哪一方,我們要儲存客戶,所以客戶是主控方,那麼需要在客戶的對映檔案中(Customer.hbm.xml)進行如下配置:
<!-- 配置關聯物件 --> <!-- name屬性:多的一方集合的屬性名稱 --> <set name="linkMans" cascade="save-update"> <!-- column屬性:多的一方外來鍵的名稱 --> <key column="lkm_cust_id"></key> <!-- 多的一方類的全路徑,如果前面的package中設定了包名,這裡填類名即可 --> <one-to-many class="LinkMan"/> </set>
編寫測試程式碼:
// 級聯儲存:只儲存一邊的問題 // 級聯是有方向性的,儲存客戶同時級聯客戶的聯絡人 // 在Customer.hbm.xml的<set>標籤上配置cascade="save-update" @Test public void testCascade() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 建立一個客戶 Customer customer = new Customer(); customer.setCust_name("劉總"); // 建立聯絡人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("王祕書"); // 建立關係 customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); session.save(customer); tx.commit(); session.close(); }
【儲存聯絡人級聯客戶】
同樣我們需要確定主控方,現在我們的主控方是聯絡人。所以需要在聯絡人的對映檔案中(LinkMan.hbm.xml)進行配置:
<!-- 配置關聯物件 --> <!-- name:一的一方的物件的名稱 class:一的一方類的全路徑 column:表中外來鍵的名稱 --> <many-to-one name="customer" cascade="save-update" class="Customer" column="lkm_cust_id"/>
測試程式碼:
// 級聯儲存:只儲存一邊的問題 // 級聯是有方向性的,儲存聯絡人同時級聯客戶 // 在LinkMan.hbm.xml的<many-to-one>標籤上配置cascade="save-update" @Test public void testCascade2() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 建立客戶 Customer customer = new Customer(); customer.setCust_name("張總"); // 建立聯絡人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("柳助理"); // 建立關係 customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); session.save(linkMan); tx.commit(); session.close(); }
到這我們已經可以看到級聯儲存或更新的效果了。那麼我們維護的時候都是雙向的關係維護。這種關係到底表述的是什麼含義呢?我們可以通過下面的測試來更進一步瞭解關係的維護(物件導航)的含義。
1.3.2 測試物件導航的問題
我們所說的物件導航其實就是在維護雙方的關係。
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
這種關係有什麼用途呢?我們可以通過下面的測試來更進一步學習Hibernate。
我們在客戶和聯絡人端都配置了cascade="save-update",然後進行如下的關係設定。會產生什麼樣的效果呢?
1 // 測試物件導航和級聯操作 2 @Test 3 public void testCascade3() throws Exception { 4 Session session = HibernateUtils.openSession(); 5 Transaction tx = session.beginTransaction(); 6 7 // 建立一個客戶 8 Customer customer = new Customer(); 9 customer.setCust_name("趙總"); 10 11 // 建立三個聯絡人 12 LinkMan linkMan1 = new LinkMan(); 13 linkMan1.setLkm_name("李祕書"); 14 LinkMan linkMan2 = new LinkMan(); 15 linkMan2.setLkm_name("王祕書"); 16 LinkMan linkMan3 = new LinkMan(); 17 linkMan3.setLkm_name("張助理"); 18 19 // 建立關係 20 linkMan1.setCustomer(customer); 21 customer.getLinkMans().add(linkMan2); 22 customer.getLinkMans().add(linkMan3); 23 24 // 條件是雙方都設定了cascade="save-update" 25 session.save(linkMan1); // 資料庫中有幾條記錄?傳送了幾條insert語句? 4條 26 // session.save(customer); // 傳送了幾條insert語句? 3條 27 // session.save(linkMan2); // 傳送了幾條insert語句? 1條 28 29 tx.commit(); 30 session.close(); 31 }
我們在執行第25行程式碼時,問會執行幾條insert語句?其實發現會有4條insert語句,因為聯絡人1關聯了客戶,客戶又關聯了聯絡人2和聯絡人3,所以當儲存聯絡人1的時候,聯絡人1是可以進入到資料庫的,它關聯的客戶也是會進入到資料庫的(因為聯絡人一端配置了級聯),那麼客戶進入到資料庫以後,聯絡人2和聯絡人3也同樣會進入到資料庫(因為客戶一端配置了級聯),所以會有4條insert語句。
我們在執行26行的時候,問會有幾條insert語句?這時我們儲存的客戶物件關聯了聯絡人2和聯絡人3,那麼客戶在進入資料庫的時候,聯絡人2和聯絡人3也會進入到資料庫,所以是3條insert語句。
同理我們執行27行程式碼的時候,只會執行1條insert語句。因為聯絡人2會儲存到資料庫,但是聯絡人2沒有對客戶建立關係,所以客戶不會儲存到資料庫。
1.3.3 Hibernate的級聯刪除
我們之前學習過級聯儲存或更新,那麼再來看級聯刪除也就不難理解了,級聯刪除也是有方向性的,刪除客戶同時級聯刪除聯絡人,也可以刪除聯絡人同時級聯刪除客戶(這種需求很少)。
原來JDBC中刪除客戶和聯絡人的時候,如果有外來鍵的關係是不可以刪除的,但是現在我們使用了Hibernate,其實Hibernate可以實現這樣的功能,但是不會刪除客戶的同時刪除聯絡人,預設情況下Hibernate會怎麼做呢?我們來看下面的測試:
// 刪除有級聯關係的物件:(預設情況:先將關聯物件的外來鍵置為null,再刪除) @Test public void demo5() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 1l); session.delete(customer); tx.commit(); session.close(); }
預設情況下如果客戶下面還有聯絡人,Hibernate會將聯絡人的外來鍵置為null,然後區刪除客戶。那麼其實有的時候我們需要刪除客戶的時候,同時將客戶關聯的聯絡人一起刪除。這個時候我們就需要使用Hibernate的級聯刪除操作了。
【刪除客戶的同時刪除客戶的聯絡人】
確定刪除的主控方是客戶,所以需要在客戶端(Customer.hbm.xml)配置:
<!-- 配置關聯物件 --> <!-- name屬性:多的一方集合的屬性名稱 --> <set name="linkMans" cascade="delete"> <!-- column屬性:多的一方外來鍵的名稱 --> <key column="lkm_cust_id"></key> <!-- 多的一方類的全路徑,如果前面的package中設定了包名,這裡填類名即可 --> <one-to-many class="LinkMan"/> </set>
如果還想有之前的級聯儲存或更新,同時還想有級聯刪除,那麼我們可以進行如下的配置:
<!-- 配置關聯物件 --> <!-- name屬性:多的一方集合的屬性名稱 --> <set name="linkMans" cascade="delete,save-update"> <!-- column屬性:多的一方外來鍵的名稱 --> <key column="lkm_cust_id"></key> <!-- 多的一方類的全路徑,如果前面的package中設定了包名,這裡填類名即可 --> <one-to-many class="LinkMan"/> </set>
測試程式碼:
// 級聯刪除:級聯刪除有方向性 // 刪除客戶的同時刪除聯絡人 // 在Customer.hbm.xml的<set>標籤上配置cascade="delete" @Test public void demo6() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 級聯刪除:必須是先查詢再刪除 // 因為查詢到客戶,這個時候客戶的聯絡人集合就會有資料 Customer customer = session.get(Customer.class, 7l); session.delete(customer); tx.commit(); session.close(); }
【刪除聯絡人的同時刪除客戶】
這時候我們刪除的是聯絡人,那麼聯絡人就是主控方,需要在聯絡人端(LinkMan.hbm.xml)配置:
<!-- 配置關聯物件 --> <!-- name:一的一方的物件的名稱 class:一的一方類的全路徑 column:表中外來鍵的名稱 --> <many-to-one name="customer" cascade="delete" class="Customer" column="lkm_cust_id"/>
如果既要儲存或更新有級聯操作,又要有級聯刪除的功能,也可以如下配置:
<!-- 配置關聯物件 --> <!-- name:一的一方的物件的名稱 class:一的一方類的全路徑 column:表中外來鍵的名稱 --> <many-to-one name="customer" cascade="delete,save-update" class="Customer" column="lkm_cust_id"/>
測試程式碼:
// 級聯刪除:級聯刪除有方向性 // 刪除聯絡人的同時刪除客戶 // 在LinkMan.hbm.xml中的<many-to-one>標籤上配置cascade="delete" @Test public void demo7() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 級聯刪除:必須是先查詢再刪除 LinkMan linkMan = session.get(LinkMan.class, 4l); session.delete(linkMan); tx.commit(); session.close(); }
1.3.4 雙向關聯產生多餘的SQL語句
【客戶表】
【聯絡人表】
有時候我們需要進行如下操作:將2號聯絡人關聯給2號客戶,也就是將2號李祕書這個聯絡人關聯給2號劉總這個客戶。
編寫修改2號客戶關聯的聯絡人的程式碼:
// 將2號聯絡人關聯的客戶改為2號客戶 @Test public void demo8() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 2l); LinkMan linkMan = session.get(LinkMan.class, 2l); linkMan.setCustomer(customer); customer.getLinkMans().add(linkMan); tx.commit(); session.close(); }
執行上述程式碼,控制檯輸出如下內容:
我們發現執行了兩次update語句,其實這兩個update都是修改了外來鍵的操作,那麼為什麼傳送兩次呢?我們來分析一下產生這個問題的原因:
我們已經分析過了,因為雙向維護了關係,而且持久態物件可以自動更新資料庫,更新客戶的時候會修改一次外來鍵,更新聯絡人的時候同樣會修改一次外來鍵。這樣就會產生多餘的SQL。問題產生了,我們該如何解決呢?
其實解決的辦法很簡單,只需要一方放棄外來鍵維護權即可。也就是說關係不是雙方維護的,只需要交給某一方去維護就可以了。通常我們都是交給多的一方去維護。為什麼呢?因為多的一方才是維護關係最好的地方。舉個例子,一個老師對應多個學生,一個學生對應一個老師,這時典型的一對多。一個老師如果要記住所有學生的名字很難,但如果讓每個學生記住老師的名字應該不難。其實就是這個道理。所有在一對多中,一的一方都會放棄外來鍵的維護權(關係的維護);
這個時候如果想讓一的一方放棄外來鍵的維護權,只需要進行如下配置即可:
<!-- 配置關聯物件 --> <!-- name屬性:多的一方集合的屬性名稱 --> <set name="linkMans" cascade="save-update" inverse="true"> <!-- column屬性:多的一方外來鍵的名稱 --> <key column="lkm_cust_id"></key> <!-- 多的一方類的全路徑,如果前面的package中設定了包名,這裡填類名即可 --> <one-to-many class="LinkMan"/> </set>
inverse的預設值是false,代表不放棄外來鍵的維護權,配置值為true,代表放棄了外來鍵的維護權。這個時候再來執行之前的操作:
這時候就不會出現上述問題了,不會產生多餘的SQL了(因為一的一方已經放棄了外來鍵的維護權)。
1.3.5 區分cascade和inverse
// cascade和inverse // cascade強調的是操作一個物件的時候,是否操作其關聯的物件 // inverse強調的是外來鍵的維護權 @Test public void demo9() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("王總"); LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("趙祕書"); // 在Customer.hbm.xml的<set>標籤上配置cascade="save-update" inverse="true" customer.getLinkMans().add(linkMan); session.save(customer); tx.commit(); session.close(); }
這時候我們會發現,如果在<set>標籤上配置cascade="save-update" inverse="true",那麼執行儲存客戶的操作時,會發現客戶和聯絡人都進入到資料庫了,但是沒有外來鍵。是因為配置了cascade="save-update"所以客戶關聯的聯絡人會進入到資料庫中,但是客戶一端放棄了外來鍵的維護權(inverse="true"),所以聯絡人插入到資料庫以後是沒有外來鍵的。
二、多對多
2.1 關係表達
2.1.1 表中的表達
2.1.2 物件中的表達
【使用者實體】
public class User { private Long user_id; private String user_code; private String user_name; private String user_password; private String user_state; // 使用者所屬的角色集合 private Set<Role> roles = new HashSet<>();
get/set... }
【角色實體】
public class Role { private Long role_id; private String role_name; private String role_memo; // 一個角色包含多個使用者 private Set<User> users = new HashSet<>();
get/set... }
2.1.3 配置檔案中的表達
【使用者的對映】User.hbm.xml
<?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="cn.itcast.domain" > <class name="User" table="sys_user" > <id name="user_id" > <generator class="native"></generator> </id> <property name="user_code"/> <property name="user_name"/> <property name="user_password"/> <property name="user_state"/> <!-- 多對多關係表達 --> <!-- name:集合屬性名 table:配置中間表名 --> <set name="roles" table="sys_user_role"> <!-- column:外來鍵,別人引用"我"的外來鍵列名 --> <key column="user_id"></key> <!-- class:我與哪個類是多對多關係 column:外來鍵.我引用別人的外來鍵列名 --> <many-to-many class="Role" column="role_id"></many-to-many> </set> </class> </hibernate-mapping>
【角色的對映】Role.hbm.xml
<?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="cn.itcast.domain" > <class name="Role" table="sys_role" > <id name="role_id" > <generator class="native"></generator> </id> <property name="role_name"/> <property name="role_memo"/> <!-- 多對多關係表達 --> <!-- name:集合屬性名 table:配置中間表名 --> <set name="users" table="sys_user_role"> <!-- column:外來鍵,別人引用"我"的外來鍵列名 --> <key column="role_id"></key> <!-- class:我與哪個類是多對多關係 column:外來鍵.我引用別人的外來鍵列名 --> <many-to-many class="User" column="user_id"></many-to-many> </set> </class> </hibernate-mapping>
在hibernate.cfg.xml中加入對映檔案
<mapping resource="cn/itcast/domain/User.hbm.xml" /> <mapping resource="cn/itcast/domain/Role.hbm.xml" />
2.2 測試方法
- 儲存
// 儲存使用者及角色 @Test public void fun1() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 建立兩個User User user1 = new User(); user1.setUser_name("張三"); User user2 = new User(); user2.setUser_name("李四"); // 建立兩個Role Role role1 = new Role(); role1.setRole_name("保潔"); Role role2 = new Role(); role2.setRole_name("保安"); // 使用者表達關係 user1.getRoles().add(role1); user1.getRoles().add(role2); user2.getRoles().add(role2); // 角色表達關係 role1.getUsers().add(user1); role2.getUsers().add(user1); role2.getUsers().add(user2); // 呼叫save方法一次儲存 session.save(user1); session.save(user2); session.save(role1); session.save(role2); tx.commit(); session.close(); }
執行上述程式碼時,我們發現報錯了:
【錯誤原因分析】
對映檔案中有一個inverse屬性,不配置時預設為false,即要維護關係。這裡兩方關係都設定了{user1.getRoles().add(role1);role1.getUsers().add(user1);},從配置上講,這兩方都沒放棄維護。多對多維護關係的手段是向中間表(sys_user_role)插入記錄,如果要表達1號使用者的角色是1號,那麼中間表就會插入1-1記錄。現在的情況是兩方都預設維護關係,role在維護關係的時候,表達1-1,會在中間表插入1-1記錄;user在維護關係的時候,也要往中間表插入資料,這時候user又要插入1-1記錄。中間表往往是這兩個id共同作為主鍵的,這兩個1-1就會出現主鍵重複。
之前的一對多兩方都維護關係,卻沒發生異常,是因為它們維護關係,就是修改了外來鍵欄位,不存在向中間表插入記錄,所以修改兩次是沒有問題的,至少不會報錯,雖然效率會低點。而多對多維護關係是向中間表插入記錄,兩方都維護的時候,就會插入兩條一樣的記錄,就會產生主鍵重複。
【解決方案】
方法一:對映檔案不改,從程式碼上來講,只要一方不表達關係即可,這樣在儲存的時候,因為程式碼上並沒有表達關係的邏輯,就不會給你操作了。這裡我們可以讓role的一方不表達關係,在程式碼上把相關程式碼註釋,此時方法就可執行成功
方法二:程式碼不改,在一方的對映檔案中修改inverse為true,讓它放棄維護關係。這裡修改Role.hbm.xml
【結論】
在多對多的儲存操作中,如果進行了雙向維護關係,就必須有一方放棄外來鍵維護權。一般由被動方放棄,使用者主動選擇角色,角色是被選擇的,所以一般角色要放棄外來鍵維護權。但如果只進行單向維護關係,那麼就不需要放棄外來鍵維護權了。
2.3 進階操作
2.3.1 級聯儲存或更新
【儲存使用者級聯角色】
儲存的主控方是使用者,需要在使用者一端(User.hbm,xml)配置
測試程式碼:
// 儲存使用者級聯角色 @Test public void fun2() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 建立兩個User User user1 = new User(); user1.setUser_name("劉備"); User user2 = new User(); user2.setUser_name("關羽"); // 建立三個角色 Role role1 = new Role(); role1.setRole_name("人事管理"); Role role2 = new Role(); role2.setRole_name("行政管理"); Role role3 = new Role(); role3.setRole_name("財務管理"); // 建立關係:如果建立了雙向的關係,一定要有一方放棄外來鍵維護權 user1.getRoles().add(role1); user1.getRoles().add(role2); user2.getRoles().add(role3); user2.getRoles().add(role2); role1.getUsers().add(user1); role2.getUsers().add(user1); role2.getUsers().add(user2); role3.getUsers().add(user2); session.save(user1); session.save(user2); /*session.save(role1); session.save(role2); session.save(role3);*/ tx.commit(); }
【儲存角色級聯使用者】
儲存的主控方是角色,需要在角色一端配置:
測試程式碼:
// 儲存角色級聯使用者 @Test public void fun2() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 建立兩個User User user1 = new User(); user1.setUser_name("劉備"); User user2 = new User(); user2.setUser_name("關羽"); // 建立三個角色 Role role1 = new Role(); role1.setRole_name("人事管理"); Role role2 = new Role(); role2.setRole_name("行政管理"); Role role3 = new Role(); role3.setRole_name("財務管理"); // 建立關係:如果建立了雙向的關係,一定要有一方放棄外來鍵維護權 user1.getRoles().add(role1); user1.getRoles().add(role2); user2.getRoles().add(role3); user2.getRoles().add(role2); role1.getUsers().add(user1); role2.getUsers().add(user1); role2.getUsers().add(user2); role3.getUsers().add(user2); /*session.save(user1); session.save(user2);*/ session.save(role1); session.save(role2); session.save(role3); tx.commit(); }
2.3.2 級聯刪除(瞭解)
在多對多中級聯刪除是不會使用的,因為我們不會有這類的需求,比如刪除使用者,將使用者關聯的角色一起刪除,或者刪除角色的時候將使用者刪除掉,這時不合理的。但是級聯刪除的功能Hibernate已經提供了此功能,所以我們只需要瞭解即可。
【刪除使用者級聯角色】
主控方是使用者,所以需要在使用者端配置
測試程式碼:
// 級聯刪除:刪除使用者,級聯刪除角色 @Test public void fun3() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); User user = session.get(User.class, 1l); session.delete(user); tx.commit(); session.close(); }
2.3.3 多對多的其他操作
- 刪除某個使用者的角色
// 將1號使用者的1號角色去掉 @Test public void fun4() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 查詢1號使用者 User user = session.get(User.class, 1l); // 查詢1號角色 Role role = session.get(Role.class, 1l); // 操作集合 相當於操作資料庫 user.getRoles().remove(role); tx.commit(); }
- 將某個使用者的角色改選
// 將1號使用者的2號角色去掉,改為3號角色 @Test public void fun5() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 查詢1號使用者 User user = session.get(User.class, 1l); // 查詢2號角色 Role role1 = session.get(Role.class, 2l); // 查詢3號角色 Role role2 = session.get(Role.class, 3l); user.getRoles().remove(role1); user.getRoles().add(role2); tx.commit(); }
- 給某個使用者新增新角色
// 給1號使用者新增2號角色 @Test public void fun6() throws Exception { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 查詢1號使用者 User user = session.get(User.class, 1l); // 查詢2號角色 Role role = session.get(Role.class, 2l); user.getRoles().add(role); tx.commit(); }