JAVAEE框架學(xué)習(xí)——Hibernate——一對(duì)多|多對(duì)多關(guān)聯(lián)關(guān)系映射操作

表關(guān)系的分析

數(shù)據(jù)庫中多表之間存在著三種關(guān)系:


表關(guān)系

表與表的三種關(guān)系:

一對(duì)多|多對(duì)一

建表原則:在多的一方創(chuàng)建外鍵指向一的一方的主鍵

一對(duì)多

表中的表達(dá)

表中表達(dá)

實(shí)體中的表達(dá)

實(shí)體中的表達(dá)

orm元數(shù)據(jù)表達(dá)

  • 多對(duì)一配置
 <!--表示多對(duì)一-->
        <many-to-one name="customer"
                     column="lkm_cust_id"
                     class="Customer">

        </many-to-one>

這里的這個(gè)配置表示的是:當(dāng)前的這個(gè)配置對(duì)象存在多對(duì)一的情況,其中name表示實(shí)體中多對(duì)一的一的名字,column表示外鍵的列名 class表示的是多個(gè)對(duì)一個(gè)的一的實(shí)體類名

  • 一對(duì)多配置
 <!--配置集合 一對(duì)多的關(guān)系-->
        <set name="linkMens">
            <!--指定外鍵列名-->
            <key column="lkm_cust_id"></key>
            <!--指定一對(duì)多還是多對(duì)多
                class指定是與哪個(gè)類型一對(duì)多
            -->
            <one-to-many class="LinkMan"/>
        </set>

這里的這個(gè)配置表示的是:當(dāng)前的這個(gè)配置對(duì)象存在多對(duì)一的情況,其中name表示實(shí)體中多個(gè)的set集合,key column表示外鍵的列名 class表示的是一對(duì)多的多的實(shí)體類名

 /**
     *保存客戶以及客戶下的聯(lián)系人
     */
    @Test
    public void fun1() {
        //獲得session
        Session session = HibernateUtils.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作數(shù)據(jù)
        Customer customer = new Customer();
        customer.setCust_name("xxx");
        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("aaa1");
        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("aaa2");
        //表達(dá)一對(duì)多關(guān)系 客戶下有多個(gè)聯(lián)系人
        customer.getLinkMens().add(lm1);
        customer.getLinkMens().add(lm2);
        //表達(dá)多對(duì)一 聯(lián)系人屬于哪個(gè)客戶
        lm1.setCustomer(customer);
        lm2.setCustomer(customer);

        //瞬時(shí)狀態(tài)對(duì)象---->持久化狀態(tài)
        session.save(customer);
        session.save(lm1);
        session.save(lm2);
        //----------------------------
        //提交事物
        tx.commit();
        //關(guān)閉資源
        session.close();
    }
/**
     * 為客戶添加聯(lián)系人
     */
    @Test
    public void fun2() {
        //獲得session
        Session session = HibernateUtils.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作數(shù)據(jù)
        //獲得要操作的客戶對(duì)象
        Customer customer = session.get(Customer.class, 1l);

        //創(chuàng)建聯(lián)系人
        LinkMan lm1 = new LinkMan();
        //將聯(lián)系人添加到客戶,將客戶設(shè)置到聯(lián)系人中
        customer.getLinkMens().add(lm1);
        lm1.setCustomer(customer);
        //執(zhí)行保存
        session.save(customer);
        session.save(lm1);
        //----------------------------
        //提交事物
        tx.commit();
        //關(guān)閉資源
        session.close();
    }
 /**
     * 從客戶中刪除某個(gè)聯(lián)系人
     */
    @Test
    public void fun3() {
        //獲得session
        Session session = HibernateUtils.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作數(shù)據(jù)
        //獲得客戶
        Customer c = session.get(Customer.class, 1l);
        LinkMan lm = session.get(LinkMan.class, 3l);
        //獲得客戶的聯(lián)系人
        //將要移除的聯(lián)系人從客戶的聯(lián)系人集合中移除
        c.getLinkMens().remove(lm);
        //清空聯(lián)系人的客戶
        lm.setCustomer(null);
        //----------------------------
        //提交事物
        tx.commit();
        //關(guān)閉資源
        session.close();
    }

上面的代碼再刪除某個(gè)聯(lián)系人時(shí)由于數(shù)據(jù)都是查詢出來的,都是出于持久態(tài)的對(duì)象,所以再事務(wù)進(jìn)行提交時(shí)Hibernate會(huì)查看數(shù)據(jù)是否發(fā)生變化,發(fā)生變化后會(huì)將數(shù)據(jù)更新,所以不需要執(zhí)行session.save()方法進(jìn)行保存數(shù)據(jù)

操作進(jìn)階

在上面的操作中,我們?cè)诓僮鞅4婵蛻舻穆?lián)系人的時(shí)候,我們需要執(zhí)行多條保存的代碼,這樣顯然很麻煩,我們希望再保存客戶的時(shí)候,將客戶的聯(lián)系人也一起保存。所以我們需要進(jìn)行一些別的配置

  • 級(jí)聯(lián)操作
 <!--級(jí)聯(lián)操作:cascade
                save-update:級(jí)聯(lián)保存更新
                delete:級(jí)聯(lián)刪除
                all: save-update delete 級(jí)聯(lián)保存更新和級(jí)聯(lián)刪除
            級(jí)聯(lián)操作:簡化操作

        -->
        <set name="linkMens" cascade="all">
            <!--配置集合 一對(duì)多的關(guān)系-->
            <!--指定外鍵列名-->
            <key column="lkm_cust_id"></key>
            <!--指定一對(duì)多還是多對(duì)多
                class指定是與哪個(gè)類型一對(duì)多
            -->
            <one-to-many class="LinkMan"/>

        </set>

級(jí)聯(lián)操作應(yīng)用在一對(duì)多的多的時(shí)候,也可以實(shí)現(xiàn)保存聯(lián)系人的時(shí)候?qū)⒖蛻舯4?/p>

  • linkman.hbm.xml 配置級(jí)聯(lián)操作
 <!--表示多對(duì)一-->
        <many-to-one name="customer"
                     column="lkm_cust_id"
                     class="Customer"
                     cascade="all"
        >

操作代碼

/**
     * 級(jí)聯(lián)操作 保存聯(lián)系人 級(jí)聯(lián)保存客戶
     */
    @Test
    public void fun5() {
        //獲得session
        Session session = HibernateUtils.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作數(shù)據(jù)
        //創(chuàng)建客戶
        Customer customer = new Customer();
        customer.setCust_name("google company");

        //創(chuàng)建聯(lián)系人
        LinkMan lm = new LinkMan();
        lm.setLkm_name("劈柴哥");
        //為客戶添加聯(lián)系人
        customer.getLinkMens().add(lm);
        //為聯(lián)系人指定客戶
        lm.setCustomer(customer);
        //保存客戶----> 只保存聯(lián)系人 通過級(jí)聯(lián)操作保存客戶
        session.saveOrUpdate(lm);

        //----------------------------
        //提交事物
        tx.commit();
        //關(guān)閉資源
        session.close();
    }

在平時(shí)開發(fā)中,建議使用save-update 不建議使用delete 級(jí)聯(lián)刪除會(huì)刪除相關(guān)的所有數(shù)據(jù)

  • 關(guān)系維護(hù)
    我們查看操作時(shí)Hibernate產(chǎn)生的SQL語句時(shí)發(fā)現(xiàn)了如下的語句
    Hibernate執(zhí)行的語句.png

    我們發(fā)現(xiàn)。在保存時(shí),客戶和聯(lián)系人兩房都會(huì)維護(hù)外鍵的關(guān)系,關(guān)系會(huì)維護(hù)兩次,這樣冗余了。多余的維護(hù)關(guān)系語句。顯然是客戶這一端在維護(hù)關(guān)系。所以我們需要優(yōu)化這種情況 這就引入了Hibernate在實(shí)體類配置的時(shí)候提供的配置 inverse
    inverse是為了提高效率
 <!--
            inverse屬性:配置關(guān)系是否維護(hù)
            配置當(dāng)前的Customer是否維護(hù)與LinkMan的關(guān)系
            inverse true 的意思就是將關(guān)系維護(hù)的工作完全交給對(duì)方 作為配置方不進(jìn)行維護(hù)關(guān)系
                    * 不維護(hù)關(guān)系無法在刪除的時(shí)候?qū)⑼怄I置為空
                    false(默認(rèn)值) 意思是將關(guān)系維護(hù)的工作由配置方來維護(hù)
                    原則:無論關(guān)系方如何放棄關(guān)系,總有一方必須要維護(hù)關(guān)系
                    一對(duì)多關(guān)系當(dāng)中,也只能是一的一方放棄維護(hù),多的一方不能放棄
                
        -->
        <set name="linkMens" inverse="true">
            <key column="lkm_cust_id"></key>
                class指定是與哪個(gè)類型一對(duì)多   
            <one-to-many class="LinkMan"/>
        </set>

tips:在開發(fā)中的刪除數(shù)據(jù),結(jié)合inverse 和cascade 實(shí)現(xiàn)不同的數(shù)據(jù)刪除策略。以這個(gè)demo為例,一個(gè)客戶可以由多個(gè)聯(lián)系人。在這種情況下 相當(dāng)于客戶和聯(lián)系人維持著某種關(guān)系。我們?cè)谟行枨髣h除客戶的時(shí)候?qū)β?lián)系人的刪除有兩種。
第一種:刪除Customer的時(shí)候,將LinkMan的外鍵置為空,LinkMan數(shù)據(jù)保留
這種情況下,如果Customer不維護(hù)數(shù)據(jù)的話是無法操作LinkMan的外鍵的,所以在這種情況下我們需要讓Customer inverse配置為false 讓customer維護(hù)關(guān)系,在這種情況下刪除Customer的時(shí)候會(huì)刪除LinkMan的關(guān)系。
第二種:刪除Customer的時(shí)候,級(jí)聯(lián)操作將LinkMan的數(shù)據(jù)刪除。
在這種情況下,將Customer inverse設(shè)置為true 并且將cascade指定為delete 這樣在刪除Customer的同時(shí)就會(huì)刪除LinkMan子表中的數(shù)據(jù)。

多對(duì)多關(guān)聯(lián)關(guān)系映射操作

建表原則:創(chuàng)建一個(gè)中間表,中間表中至少兩個(gè)字段作為外鍵分別指向多對(duì)多雙方的主鍵

多對(duì)多

表中關(guān)系表達(dá)

表中表達(dá)

實(shí)體中的表達(dá)

實(shí)體中的表達(dá)

orm元數(shù)據(jù)表達(dá)

  • User表配置
  <set name="roles" table="sys_user_role">
            <!--
                key標(biāo)簽
                    column:當(dāng)前對(duì)象在中間表中的外鍵名稱
            -->
            <key column="user_id"></key>
            <!--
                many-to-many標(biāo)簽:
                class:關(guān)聯(lián)另一方的類的全類名
                column 關(guān)聯(lián)的另一方在中間表的外鍵名稱
            -->
            <many-to-many class="Role" column="role_id"></many-to-many>
        </set>
  • role表配置
  <!--Role多對(duì)多配置-->
        <set name="users" table="sys_user_role">
            <key column="role_id"/>
            <many-to-many class="User"
                          column="user_id"/>
        </set>

多對(duì)多操作

我們使用案例:保存員工以及角色

在多對(duì)多關(guān)系中一定要有一方放棄關(guān)系的維護(hù),否則會(huì)出現(xiàn)主鍵重復(fù)錯(cuò)誤
放棄關(guān)系維護(hù)在配置文件中使用 inverse=“true” 表示放棄操作

  • 配置文件
  <!-- inverse 屬性 使得一方放棄維護(hù)外鍵關(guān)系-->
        <set name="users" table="sys_user_role" inverse="true">
            <key column="role_id"/>
            <many-to-many class="User"
                          column="user_id"/>
        </set>
  • 實(shí)例代碼
 //從工廠獲得session
        Session session = HibernateUtil.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //操作數(shù)據(jù)
        //-------------------------------
        //創(chuàng)建兩個(gè)User
        User user1 = new User();
        User user2 = new User();
        user1.setUser_name("blackman");
        user2.setUser_name("whiteman");

        //創(chuàng)建兩個(gè)role
        Role role1 = new Role();
        Role role2 = new Role();
        Role role3 = new Role();
        role1.setRole_name("vp");
        role2.setRole_name("cto");
        role3.setRole_name("security");


        //用戶表達(dá)關(guān)系
        user1.getRoles().add(role1);
        user1.getRoles().add(role2);
        user2.getRoles().add(role1);
        user2.getRoles().add(role3);
        //角色表達(dá)關(guān)系
        role1.getUsers().add(user1);
        role1.getUsers().add(user2);
        role2.getUsers().add(user1);
        role3.getUsers().add(user2);
        //保存到數(shù)據(jù)庫
        session.save(user1);
        session.save(user2);
        session.save(role1);
        session.save(role2);
        session.save(role3);
        //-------------------------------
        //提交事務(wù)
        tx.commit();
        //關(guān)閉資源
        session.close();

 @Test
    /**
     * 添加角色到某個(gè)用戶中
     */
    public void addRoleToUser() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        //操作
        //------------
        //獲得要添加的用戶
        User user = session.get(User.class, 1l);
        //創(chuàng)建公關(guān)角色
        Role role = new Role();
        role.setRole_name("公關(guān)");
        //將角色添加到用戶
        user.getRoles().add(role);
        //將角色轉(zhuǎn)換為持久化
        session.save(role);
        //------------
        tx.commit();
        session.close();
    }

/**
* 從指定角色刪除
*/
  @Test
    public void delRoleFromUser(){
        //獲取session
        Session session = HibernateUtil.openSession();
        //開啟事物
        Transaction tx = session.beginTransaction();
        //操作數(shù)據(jù)
        //-------------
        //獲得要?jiǎng)h除的數(shù)據(jù)
        User user = session.get(User.class, 3l);
        //獲得要?jiǎng)h除的角色數(shù)據(jù)
        Role role = session.get(Role.class, 7l);
        user.getRoles().remove(role);
        //-------------
        //提交事務(wù)
        tx.commit();
        //關(guān)閉資源
        session.close();
    }


進(jìn)階操作

  • 級(jí)聯(lián)操作
    cascade: 級(jí)聯(lián)操作
    在User.hbm.xml中配置cascade后
        <set name="roles" table="sys_user_role" cascade="save-update">

可以進(jìn)行級(jí)聯(lián)保存 上面的操作代碼不再需要

session.save(role)

一對(duì)一

  • 建表原則:
    • 1 唯一外鍵對(duì)應(yīng):假設(shè)一對(duì)一種的任意一方為多,在多的一方創(chuàng)建外鍵指向一的一方的主鍵,然后將外鍵設(shè)置為唯一。
    • 2 主鍵對(duì)應(yīng):一方的主鍵作為另一方的主鍵


      一對(duì)一
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容