複習_Hibernate_一對多關係、多對多關係、級聯和外鍵維護

1.Hibernate中一對多關係映射

1.1 一對多關係:實體類關係建立

1.客戶實體類

package cn.itcast.domain;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
/**
 * 客戶實體類
 * 
 * @author Jackie
 *
 */
public class Customer implements Serializable {
	private Long custId;// 客戶ID
	private String custName;// 客戶名稱
	private String custSource;// 客戶來源
	private String custIndustry;// 客戶所屬行業
	private String custLevel;// 客戶級別
	private String custAddress;// 客戶聯繫地址
	private String custPhone;// 客戶聯繫方式
	
	//一對多關係映射:一個客戶可以對應多個聯繫人
	private Set<LinkMan> linkmans=new HashSet<LinkMan>();
	public Set<LinkMan> getLinkmans() {
		return linkmans;
	}
	public void setLinkmans(Set<LinkMan> linkmans) {
		this.linkmans = linkmans;
	}

	public Long getCustId() {
		return custId;
	}
	public void setCustId(Long custId) {
		this.custId = custId;
	}
	public String getCustName() {
		return custName;
	}
	public void setCustName(String custName) {
		this.custName = custName;
	}
	public String getCustSource() {
		return custSource;
	}
	public void setCustSource(String custSource) {
		this.custSource = custSource;
	}
	public String getCustIndustry() {
		return custIndustry;
	}
	public void setCustIndustry(String custIndustry) {
		this.custIndustry = custIndustry;
	}
	public String getCustLevel() {
		return custLevel;
	}
	public void setCustLevel(String custLevel) {
		this.custLevel = custLevel;
	}
	public String getCustAddress() {
		return custAddress;
	}
	public void setCustAddress(String custAddress) {
		this.custAddress = custAddress;
	}
	public String getCustPhone() {
		return custPhone;
	}
	public void setCustPhone(String custPhone) {
		this.custPhone = custPhone;
	}
	@Override
	public String toString() {
		return "Customer [custId=" + custId + ", custName=" + custName
				+ ", custSource=" + custSource + ", custIndustry="
				+ custIndustry + ", custLevel=" + custLevel + ", custAddress="
				+ custAddress + ", custPhone=" + custPhone + "]";
	}
}

2.聯繫人實體類
由於聯繫人是多的一方,在實體類中要體現出,每個聯繫人只能對應一個客戶,代碼如下:

package cn.itcast.domain;
import java.io.Serializable;
/**
 * 聯繫人實體類
 * 
 * @author Administrator
 *
 */
/**
 * 聯繫人實體類
 * 
 * @author Administrator
 *
 */
public class LinkMan implements Serializable {
	private Long lkmId;//聯繫人編號(主鍵)
	private String lkmName;//聯繫人姓名
	private String lkmGender;//聯繫人性別
	private String lkmPhone;//聯繫人辦公電話
	private String lkmMobile;//聯繫人手機
	private String lkmEmail;//聯繫人郵箱
	private String lkmPosition;//聯繫人職位
	private String lkmMemo;//聯繫人備註
	
	//多對一關係:多個聯繫人對應客戶
	private Customer customer;//對應聯繫人表中的外鍵
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	
    public Long getLkmId() {
		return lkmId;
	}
	public void setLkmId(Long lkmId) {
		this.lkmId = lkmId;
	}
	public String getLkmName() {
		return lkmName;
	}
	public void setLkmName(String lkmName) {
		this.lkmName = lkmName;
	}
	public String getLkmGender() {
		return lkmGender;
	}
	public void setLkmGender(String lkmGender) {
		this.lkmGender = lkmGender;
	}
	public String getLkmPhone() {
		return lkmPhone;
	}
	public void setLkmPhone(String lkmPhone) {
		this.lkmPhone = lkmPhone;
	}
	public String getLkmMobile() {
		return lkmMobile;
	}
	public void setLkmMobile(String lkmMobile) {
		this.lkmMobile = lkmMobile;
	}
	public String getLkmEmail() {
		return lkmEmail;
	}
	public void setLkmEmail(String lkmEmail) {
		this.lkmEmail = lkmEmail;
	}
	public String getLkmPosition() {
		return lkmPosition;
	}
	public void setLkmPosition(String lkmPosition) {
		this.lkmPosition = lkmPosition;
	}
	public String getLkmMemo() {
		return lkmMemo;
	}
	public void setLkmMemo(String lkmMemo) {
		this.lkmMemo = lkmMemo;
	}
	@Override
	public String toString() {
		return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName
				+ ", lkmGender=" + lkmGender + ", lkmPhone=" + lkmPhone
				+ ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail
				+ ", lkmPosition=" + lkmPosition + ", lkmMemo=" + lkmMemo + "]";
	}
}

接下來的問題就是:
如何通過配置的方式把客戶實體的Set集合和聯繫人實體的中Customer對象與數據庫建立起來關係,這就是我們今天學習的重點內容。


1.2 一對多關係:XML映射文件

1.客戶配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入DTD約束
位置:在Hibernate的核心jar包(hibernate-core-5.0.7.Final.jar)中名稱爲hibernate-mapping-3.0.dtd
明確該文件中的內容:
	實體類和表的對應關係
	實體類中屬性和表的字段的對應關係
-->
<!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"><!-- package屬性用於設定包的名稱,接下來該配置文件中凡是用到此包中的對象時都可以省略包名 -->
    <!-- class標籤
            作用:建立實體類和表的對應關係
            屬性:
                name:指定實體類的名稱
                table:指定數據庫表的名稱
     -->
    <class name="Customer" table="cst_customer">
        <!-- id標籤
             作用:用於映射主鍵
             屬性:
                 name:指定的是屬性名稱。也就是get/set方法後面的部分,並且首字母要轉小寫。
                 column:指定的是數據庫表的字段名稱
        -->
        <id name="custId" column="cust_id">
            <!-- generator標籤:
            作用:配置主鍵的生成策略。
            屬性:
              class:指定生成方式的取值。
              取值之一:native   使用本地數據庫的自動增長能力。
              mysql數據庫的自動增長能力是讓某一列自動+1。但是不是所有數據庫都支持這種方式
           -->
            <generator class="native"></generator>
        </id>
        <!-- property標籤:
                作用:映射其他字段
                屬性:
                    name:指定屬性的名稱。和id標籤的name屬性含義一致
                    column:指定數據庫表的字段名稱
        -->
        <property name="custName" column="cust_name"></property>
        <property name="custSource" column="cust_source"></property>
        <property name="custIndustry" column="cust_industry"></property>
        <property name="custLevel" column="cust_level"></property>
        <property name="custAddress" column="cust_address"></property>
        <property name="custPhone" column="cust_phone"></property>

        <!--
            set標籤:用於映射set集合屬性
            屬性:
			    name:指定集合屬性的名稱
		        table:指定的是集合元素所對應的表(在一對多的時候寫不寫都可以)

		    key標籤:用於映射外鍵字段的
            屬性:
                column:指定從表中的外鍵字段名稱

            one-to-many標籤:用於指定當前映射配置文件所對應的實體和集合元素所對應的實體是一對多關係。
            屬性:
                 class:指定集合元素所對應的實體類名稱
        -->
        <set name="linkmans" table="cst_linkman">
            <key column="lkm_cust_id"></key>
            <one-to-many class="cn.itcast.domain.LinkMan"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

擴展:在一對多關係中,關於set標籤的一些知識
1.我們僅配置了客戶配置文件中的一對多關係,而沒有在聯繫人配置文件中配置多對一關係,也是可以進行級聯保存和刪除的
測試:
(1).客戶配置文件中的一對多關係,並開啓級聯
(2).聯繫人配置文件不進行配置
(3).cst_linkman沒有設置外鍵約束

CREATE TABLE `cst_linkman` (
  `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯繫人編號(主鍵)',
  `lkm_name` varchar(16) DEFAULT NULL COMMENT '聯繫人姓名',
  `lkm_gender` char(1) DEFAULT NULL COMMENT '聯繫人性別',
  `lkm_phone` varchar(16) DEFAULT NULL COMMENT '聯繫人辦公電話',
  `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '聯繫人手機',
  `lkm_email` varchar(64) DEFAULT NULL COMMENT '聯繫人郵箱',
  `lkm_position` varchar(16) DEFAULT NULL COMMENT '聯繫人職位',
  `lkm_memo` varchar(512) DEFAULT NULL COMMENT '聯繫人備註',
  `lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '外鍵,引用客戶表中的主鍵',
  PRIMARY KEY (`lkm_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

代碼執行,我們發現
在這裏插入圖片描述
執行邏輯如下:

  • 1.插入客戶表數據,插入後有了cust_id(自動生成)
  • 2.設置了級聯(cascade=“save-update,delete”),就會去插入客戶對象中設置的聯繫人對象(集合),根據set標籤中設置的類型路徑,找到對應的類,同時這個類必須已經在覈心配置中引入對應的配置文件(例如LinkMan.hbm.xml),然後就會向聯繫人表中插入數據(,如果客戶對象的集合中有10個聯繫人,這裏就要insert
    10個聯繫人)
  • 3.根據set標籤中的key屬性,對插入的聯繫人進行update操作

2.聯繫人配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入DTD約束
位置:在Hibernate的核心jar包(hibernate-core-5.0.7.Final.jar)中名稱爲hibernate-mapping-3.0.dtd
明確該文件中的內容:
	實體類和表的對應關係
	實體類中屬性和表的字段的對應關係
-->
<!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"><!-- package屬性用於設定包的名稱,接下來該配置文件中凡是用到此包中的對象時都可以省略包名 -->

    <!-- 建立實體類和表的對應關係 -->
    <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="lkmPosition" column="lkm_position"></property>
        <property name="lkmEmail" column="lkm_email" ></property>
        <property name="lkmMemo" column="lkm_memo"></property>
        <!-- 多對一關係映射
        many-to-one標籤:用於建立多對一的關係映射配置
        屬性:
           name:指定的實體類中屬性的名稱
           class:該屬性所對應的實體類名稱。如果在hibernate-mapping上沒有導包,則需要寫全限定類名
           column:指定從表中的外鍵字段名稱
        -->
        <many-to-one name="customer" class="cn.itcast.domain.Customer" column="lkm_cust_id"></many-to-one>
    </class>

</hibernate-mapping>

注意:在設置過Customer.hbm.xml文件和LinkMan.hbm.xml文件後,需要在hibernate.cfg.xml中引入兩個映射文件位置
在這裏插入圖片描述

1.3 一對多關係:級聯保存

添加了一個客戶,爲這個客戶添加多個聯繫人,這個過程就叫做級聯保存(要同時操作兩個表來實現)

複雜操作:

/**
 * 一對多關係演示
 * 需求:
 * 	 保存一個客戶和一個聯繫人
 * 要求:
 *   創建一個客戶對象和一個聯繫人對象
 *   建立客戶和聯繫人之間關聯關係(雙向一對多的關聯關係)
 *   先保存客戶,再保存聯繫人
 */
@Test
public void testOneToMany(){
	//創建客戶對象
	Customer c = new Customer();//瞬時態
	c.setCustName("TBD雲集中心");
	c.setCustLevel("VIP客戶");
	c.setCustSource("網絡");
	c.setCustIndustry("商業辦公");
	c.setCustAddress("金沙江路1340弄");
	c.setCustPhone("021-84389340");
	//創建聯繫人對象
	LinkMan lkm =new LinkMan();//瞬時態
	lkm.setLkmName("TBD聯繫人");
	lkm.setLkmGender("男");
	lkm.setLkmMobile("13811111111");
	lkm.setLkmPhone("021-34785348");
	lkm.setLkmEmail("[email protected]");
	lkm.setLkmPosition("業務經理");
	lkm.setLkmMemo("做事比較保守");
	
	//建立他們的雙向一對多關聯關係
	lkm.setCustomer(c);//沒有下面這行代碼時,執行程序時僅有2個insert語句
	c.getLinkmans().add(lkm);//本行代碼和上行代碼同時執行後,會有2個insert語句,1個update
	//從當前線程中獲取新的Session對象
	Session  session= HibernateUtil.getCurrentSession();
	//開啓事務
	Transaction tx =session.beginTransaction();
	//按照要求:先保存客戶,再保存聯繫人(此時符合保存原則:先保存主表,再保存從表)
	session.save(c);//如果在把客戶對象轉成持久態時,不考慮聯繫人的信息。就不會有聯繫人的快照產生
	session.save(lkm);
	
	tx.commit();//默認此時會執行快照機制,當發現一級緩存和快照不一致了,使用一級緩存更新數據庫。
}

執行結果如下:
在這裏插入圖片描述
那麼這種保存的方式存在一個問題,如果一個客戶上有100個聯繫人,那就需要執行100次的 session.save(lkm);

簡單操作:
1.在客戶映射文件中進行配置

<set name="linkmans" cascade="save-update">

2.在代碼中,只有將聯繫人放到客戶的集合中就可以的,省去了把客戶放到聯繫人的屬性中這一個操作

package cn.itcast.dao;

import cn.itcast.domain.Customer;
import cn.itcast.domain.LinkMan;
import cn.itcast.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.jupiter.api.Test;

public class HibernateDemo2 {
    @Test
    public void testOneToMany(){
        //創建客戶對象
        Customer c = new Customer();//瞬時態
        c.setCustName("TBD雲集中心");
        c.setCustLevel("VIP客戶");
        c.setCustSource("網絡");
        c.setCustIndustry("商業辦公");
        c.setCustAddress("金沙江路1340弄");
        c.setCustPhone("021-84389340");
        //創建聯繫人對象
        LinkMan lkm =new LinkMan();//瞬時態
        lkm.setLkmName("TBD聯繫人");
        lkm.setLkmGender("男");
        lkm.setLkmMobile("13811111111");
        lkm.setLkmPhone("021-34785348");
        lkm.setLkmEmail("[email protected]");
        lkm.setLkmPosition("業務經理");
        lkm.setLkmMemo("做事比較保守");

        //建立他們的雙向一對多關聯關係
        c.getLinkmans().add(lkm);//本行代碼和上行代碼同時執行後,會有2個insert語句,1個update
        //從當前線程中獲取新的Session對象
        Session session= HibernateUtil.getCurrentSession();
        //開啓事務
        Transaction tx =session.beginTransaction();
        //按照要求:先保存客戶,再保存聯繫人(此時符合保存原則:先保存主表,再保存從表)
        session.save(c);//如果在把客戶對象轉成持久態時,不考慮聯繫人的信息。就不會有聯繫人的快照產生

        tx.commit();//默認此時會執行快照機制,當發現一級緩存和快照不一致了,使用一級緩存更新數據庫。
    }
}

執行結果如下:
在這裏插入圖片描述


1.4 一對多關係:級聯刪除

刪除了某一個客戶,這個客戶下的所有聯繫人也一起刪除

第一步 在客戶映射文件set標籤下,進行配置,添加delete值

<set name="linkmans" cascade="save-update,delete">

第二步,在代碼中直接刪除客戶

@Test
public void delete(){
    Session session= HibernateUtil.getCurrentSession();
    //開啓事務
    Transaction tx =session.beginTransaction();
    Customer customer = session.get(Customer.class, 2L);
    session.delete(customer);
    tx.commit();//默認此時會執行快照機制,當發現一級緩存和快照不一致了,使用一級緩存更新數據庫。
}

執行的sql如下
在這裏插入圖片描述
在這裏插入圖片描述

1.5 一對多關係:修改操作

1.讓聯繫人A所屬的客戶,從客戶A更換到客戶B

@Test
public void update(){
    Session session= HibernateUtil.getCurrentSession();
    //開啓事務
    Transaction tx =session.beginTransaction();

    Customer customer = session.get(Customer.class, 7L);
    LinkMan linkMan = session.get(LinkMan.class, 9L);

    // 把聯繫人放到客戶裏面
    customer.getLinkmans().add(linkMan);
    // 把客戶放到聯繫人裏面
    linkMan.setCustomer(customer);

    tx.commit();//默認此時會執行快照機制,當發現一級緩存和快照不一致了,使用一級緩存更新數據庫。
}

因爲customer和linkMan是持久態,所以持久態的變更,在commit時,會與快照進行比較,如果不符合就會根據當前的對象,更改數據庫中數據

在這裏插入圖片描述

我們發現執行了兩個update語句,但這兩個update語句的作用是一模一樣的,就是更改聯繫人的外鍵
造成這個情況的原因是,我們在客戶以及聯繫人的配置文件中都對外鍵進行了維護(雙向維護)

解決方式:讓其中一方放棄外鍵維護
讓1的一方放棄

<set name="linkmans" cascade="save-update,delete" inverse="true">

碰到的問題:
如果客戶配置文件中,沒有設置級聯,會碰到以下問題

@Test
public void testOneToManyUpdate(){
	//創建聯繫人對象
	LinkMan lkm =new LinkMan();//瞬時態
	lkm.setLkmName("TBD聯繫人2");
	lkm.setLkmGender("女");
	lkm.setLkmMobile("138222222");
	lkm.setLkmPhone("021-34785347");
	lkm.setLkmEmail("[email protected]");
	lkm.setLkmPosition("業務員");
	lkm.setLkmMemo("還行吧");
	//從當前線程中獲取新的Session對象
	Session  session= HibernateUtil.getCurrentSession();
	Transaction tx =session.beginTransaction();//開啓事務
	//查詢id爲94的客戶對象
	Customer c = session.get(Customer.class, 94L);
	//建立雙向關聯關係
	c.getLinkmans().add(lkm);
	lkm.setCustomer(c);
	//更新客戶信息
	session.update(c);
	tx.commit();
}

執行以上程序後,發現數實體表中並沒有新增這條數據
造成程序執行沒有結果的原因是:
在這裏插入圖片描述
在Customer.hbm.xml文件Set標籤中設置了inverse=”true”,表示”放棄維護關聯關係的權利”。換句話講,對Customer對象進行更新時,僅是操作Customer對象,而Customer對象關聯的其它對象(LinkMan對象)不會有任何操作

去除Customer.hbm.xml文件Set標籤中的inverse屬性,再執行程序時,報錯:

在這裏插入圖片描述
總結:當更新一個持久態對象時,它關聯了一個瞬時態的對象。執行更新時會引發程序報錯

解決方法1:先把瞬時態對象保存,再保存持久態對象
解決辦法2:配置級聯操作(級聯保存和更新)

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

級聯操作的方向性: 級聯是有方向性的,所謂的方向性指的是,在保存一的一方級聯多的一方,或者是在保存多的一方級聯一的一方。

配置級聯操作,首先要確定我們要保存的主控方是那一方,在我們的案例中是要保存客戶,所以客戶是主控方,那麼需要在客戶的映射文件中進行如下的配置。(要操作哪個對象,就找哪個對象的配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入DTD約束 -->
<!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="custId" column="cust_id">
	      <!-- 配置主鍵的生成策略 -->
	      <generator class="native"></generator>
	    </id>
	    <!-- 映射其他字段 -->
	    <property name="custName" column="cust_name"></property>
	    <property name="custSource" column="cust_source"></property>
	    <property name="custIndustry" column="cust_industry"></property>
	    <property name="custLevel" column="cust_level"></property>
	    <property name="custAddress" column="cust_address"></property>
	    <property name="custPhone" column="cust_phone" ></property>
	    <!-- 一對多關係映射 -->
	    <set name="linkmans" table="cst_linkman" cascade="save-update" inverse="true">
	        <key column="lkm_cust_id"/>
	        <one-to-many class="cn.itcast.domain.LinkMan"/>
	    </set>
	 </class>
</hibernate-mapping>

再次執行程序後,數據表結果:

@Test
public void testOneToManyUpdate(){
	//創建聯繫人對象
	LinkMan lkm =new LinkMan();//瞬時態
	lkm.setLkmName("TBD聯繫人2");
	lkm.setLkmGender("女");
	lkm.setLkmMobile("138222222");
	lkm.setLkmPhone("021-34785347");
	lkm.setLkmEmail("[email protected]");
	lkm.setLkmPosition("業務員");
	lkm.setLkmMemo("還行吧");
	//從當前線程中獲取新的Session對象
	Session  session= HibernateUtil.getCurrentSession();
	Transaction tx =session.beginTransaction();//開啓事務
	//查詢id爲1的客戶對象
	Customer c = session.get(Customer.class, 94L);
	//建立雙向關聯關係
	c.getLinkmans().add(lkm);
	//lkm.setCustomer(c);//此行代碼不執行
	//更新客戶信息
	session.update(c);
	tx.commit();
}

在這裏插入圖片描述
這是因爲雖然級聯了,創建了TBD聯繫人2時,外鍵並沒有值
這是因爲沒有設置lkm.setCustomer(customer);

因爲我們級聯保存LinkMan對象時,是根據這個對象的屬性去保存的,一般屬性就根據屬性值,外鍵的話,就根據設置的<many-to-one>標籤去屬性中找customer對象的id來設置lkm_cust_id,這裏無法找到,所以設置爲null


總結:
級聯的優點:

  • 在插入時,只要插入客戶對象,就可以插入客戶表下所有的聯繫人
  • 在修改時,知道修改客戶對象,就可以修改客戶對象下所有的聯繫人
  • 在刪除時,只要刪除客戶對象,就可以刪除客戶表下所有的聯繫人

2.Hibernate中多對多關係映射

用戶實體類

package cn.itcast.domain;
import java.util.HashSet;
import java.util.Set;
/**
 * 用戶數據模型
 * 
 * @author Administrator
 */
public class SysUser {
	private Long userId;
	private String userCode;
	private String userName;
	private String userPassword;
	private String userState;

	// 多對多關係映射
	private Set<SysRole> roles=new HashSet<SysRole>(0);
	public Set<SysRole> getRoles() {
		return roles;
	}
	public void setRoles(Set<SysRole> roles) {
		this.roles = roles;
	}

	public Long getUserId() {
		return userId;
	}
	public void setUserId(Long userId) {
		this.userId = userId;
	}
	public String getUserCode() {
		return userCode;
	}
	public void setUserCode(String userCode) {
		this.userCode = userCode;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getUserPassword() {
		return userPassword;
	}
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}
	public String getUserState() {
		return userState;
	}
	public void setUserState(String userState) {
		this.userState = userState;
	}
	@Override
	public String toString() {
		return "SysUser [userId=" + userId + ", userCode=" + userCode
				+ ", userName=" + userName + ", userPassword=" + userPassword
				+ ", userState=" + userState + "]";
	}
}

角色實體類

package cn.itcast.domain;
import java.util.HashSet;
import java.util.Set;
/**
 * 角色數據模型
 * 
 * @author Administrator
 */
public class SysRole {
	private Long roleId;
	private String roleName;
	private String roleMemo;

    //多對多關係映射
	private Set<SysUser> users=new HashSet<SysUser>(0);
	public Set<SysUser> getUsers() {
		return users;
	}
	public void setUsers(Set<SysUser> users) {
		this.users = users;
	}

	public Long getRoleId() {
		return roleId;
	}
	public void setRoleId(Long roleId) {
		this.roleId = roleId;
	}
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
	public String getRoleMemo() {
		return roleMemo;
	}
	public void setRoleMemo(String roleMemo) {
		this.roleMemo = roleMemo;
	}
	@Override
	public String toString() {
		return "SysRole [roleId=" + roleId + ", roleName=" + roleName
				+ ", roleMemo=" + roleMemo + "]";
	}
}

2.1 多對多關係:XML映射文件

用戶配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入DTD約束 -->
<!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="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="sys_user_role" >
		  <key column="userid"/>
		  <many-to-many class="cn.itcast.domain.SysRole" column="roleid"/>
	 </set>
  </class>
</hibernate-mapping>

角色配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入DTD約束 -->
<!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="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:指定集合屬性的名稱
			  table:指定的是中間表的名稱,在多對多的配置時,必須寫。
	key標籤:指定外鍵字段
		  屬性:
			  column:指定的是當前映射文件所對應的實體在中間表的外鍵字段名稱
	many-to-many標籤:指定當前映射文件所對應的實體和集合元素所對應的實體是多對多的關係
		  屬性:
			  class:指定集合元素所對應的實體類
			  column:指定的是集合元素所對應的實體在中間表的外鍵字段名稱
	 -->
	 <set name="users" table="sys_user_role">
		  <key column="roleid"/>
		  <many-to-many class="cn.itcast.domain.SysUser" column="userid"/>
	 </set>
  </class>
</hibernate-mapping>

2.2 多對多關係:保存操作

package cn.itcast.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.itcast.domain.SysRole;
import cn.itcast.domain.SysUser;
import cn.itcast.utils.HibernateUtil;
public class HibernateDemo2 {
  /**
   * 多對多關係保存操作
   * 
   * 需求:保存兩個用戶和三個角色
   *    要求:
   *    1、讓1號用戶具有 1號角色和2號角色
   *    2、讓2號用戶具有 2號角色和3號角色
   *    3、保存用戶和角色
   */
 @Test
 public void testManyToManySave(){
	//創建兩個用戶對象
	SysUser u1 = new SysUser();//1號用戶
	u1.setUserName("用戶1");
	u1.setUserCode("[email protected]");
	u1.setUserPassword("123123");
	u1.setUserState("1");
	SysUser u2 = new SysUser();//2號用戶
	u2.setUserName("用戶2");
	u2.setUserCode("[email protected]");
	u2.setUserPassword("123456");
	u2.setUserState("1");
	
	//創建三個角色對象
	SysRole r1 = new SysRole();//1號角色
	r1.setRoleName("角色1");
	r1.setRoleMemo("1級管理員");
	SysRole r2 = new SysRole();//2號角色
	r2.setRoleName("角色2");
	r2.setRoleMemo("超級管理員");
	SysRole r3 = new SysRole();//3號角色
	r3.setRoleName("角色3");
	r3.setRoleMemo("2級管理員");
			
	//建立用戶和角色的雙向關聯關係
	u1.getRoles().add(r1);
	u1.getRoles().add(r2);
	r1.getUsers().add(u1);
	r2.getUsers().add(u1);
			
	u2.getRoles().add(r2);
	u2.getRoles().add(r3);
	r2.getUsers().add(u2);
	r3.getUsers().add(u2);
	
	//從當前線程中獲取新的Session對象
	Session  session= HibernateUtil.getCurrentSession();
	Transaction tx =session.beginTransaction();//開啓事務
	session.save(u1);
	session.save(u2);
	session.save(r1);
	session.save(r2);
	session.save(r3);
	tx.commit();
 }
}

程序執行的結果:
在這裏插入圖片描述
分析原因:

解決方案:放棄維護關聯表的權利。(SysUser或SysRole中任意一個放棄即可)

在這裏插入圖片描述
解決方案:放棄維護關聯表的權利。(SysUser或SysRole中任意一個放棄即可)

2.3 多對多關係:刪除操作

注意:在多對多時,禁用雙向級聯刪除!!!
在這裏插入圖片描述


3.Hibernate中的多表查詢

3.1 對象導航查詢

1.概述
對象圖導航檢索方式是根據已經加載的對象,導航到他的關聯對象。它利用類與類之間的關係來檢索對象。

例如: 我們通過OID查詢方式查出一個客戶,可以調用Customer類中的getLinkMans()方法來獲取該客戶的所有聯繫人。
對象導航查詢的使用要求是:兩個對象之間必須存在關聯關係。

2.對象導航查詢示例
(1)查詢一個客戶,獲取該客戶下的所有聯繫人

@Test
public void test1(){
    Session s = HibernateUtil.getCurrentSession();
    Transaction tx = s.beginTransaction();
    SysUser c = s.get(SysUser.class, 9L);
    Set<SysRole> roles = c.getRoles();//此處就是對象導航查詢
    for(Object o : roles){
        System.out.println(o);
    }
    tx.commit();
}

3.對象導航查詢的問題分析
問題1:我們查詢客戶時,要不要把聯繫人查詢出來?
如果我們不查的話,在用的時候還要自己寫代碼,調用方法去查詢。
如果我們查出來的,不使用時又會白白的浪費了服務器內存。

解決:
採用延遲加載的思想。通過配置的方式來設定當我們在需要使用時,發起真正的查詢。

配置的方式:
在Customer.hbm.xml配置文件中的set標籤上使用lazy屬性。取值爲true|fasle

<set name="linkmans" table="cst_linkman" inverse="true" lazy="true">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>

問題2:我們查詢聯繫人時,要不要把客戶查詢出來?
分析:
如果我們不查的話,在用的時候還要自己寫代碼,調用方法去查詢。
如果我們查出來的話,一個對象不會消耗太多的內存。而且多數情況下我們都是要使用的。
例如:查詢聯繫人詳情時,肯定會看看該聯繫人的所屬客戶。

解決:
採用立即加載的思想。通過配置的方式來設定,只要查詢從表實體,就把主表實體對象同時查出來。

在LinkMan.hbm.xml配置文件中的many-to-one標籤上使用lazy屬性。取值爲proxy|fasle
false:立即加載
proxy:看客戶的映射文件class標籤的lazy屬性取值,如果客戶的class標籤lazy屬性是true
那麼proxy表示延遲加載,如果是false就表示立即加載。
<many-to-one name="customer" class="Customer" column="lkm_cust_id" lazy="false"/>

總結:

級聯

作用: 在Hibernate中,通過關聯映射,在對一方進行增刪改時,會連帶增刪改關聯的另外一方的數據,這種關聯操作稱之爲級聯操作

場景:
客戶關聯多個聯繫人,我們只需要對客戶進行保存,就可以通過級聯對關聯的聯繫人一併保存

1.客戶是瞬時態,聯繫人也是瞬時態;客戶配置表開啓了級聯

@Test
    public void testOneToMany(){
        Customer c = new Customer();//瞬時態
        c.setCustName("TBD雲集中心");
        ......
        LinkMan lkm =new LinkMan();//瞬時態
        lkm.setLkmName("TBD聯繫人");
        ......

        Session session= HibernateUtil.getCurrentSession();
        Transaction tx =session.beginTransaction();
        
        c.getLinkmans().add(lkm);
        // lkm.setCustomer(c);

        session.save(c);
        tx.commit();
    }

這裏就會很有意思,如果客戶配置中設置了inverse=“true”,得到的聯繫人的外鍵爲null,如果不設置(默認爲false),聯繫人的外鍵則爲客戶的主鍵
因爲如果設置了主表不維護主鍵,且聯繫人中沒有lkm.setCustomer©;在保存的聯繫人的時候,lkm_cust_id就是null值

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