hibernate二


layout: post
title: hibernate--多表
subtitle: 多表操作
date: 2018-05-23
author: ZL
header-img: img/20180523.jpg
catalog: true
tags:
- hibernate
- 多表


一對多多對一

數據庫中的一對多,多對一

指的是兩個數據庫表之間的關系,比如一個數據下面的例子中,一個表是存放客戶的表,另一個表存放聯系人。然后一個客戶會有多個聯系人,即一個客戶和多個聯系人綁定,那么對于客戶的表就是一對多,對于聯系人表就是多對一。

如何將兩個表的關系表達出來,在數據庫中有一個叫外鍵的東西。在一對多中處在“多”位置的那個表中要表明外鍵是“一”的表的某個字段。

hibernate中處理一對多多對一的操作

  1. 首先看一下兩個表的內容。

    --客戶表的內容
    CREATE TABLE `cst_customer` (
          `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客戶編號(主鍵)',
          `cust_name` VARCHAR(32) NOT NULL COMMENT '客戶名稱(公司名稱)',
          `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客戶信息來源',
          `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客戶所屬行業',
          `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客戶級別',
          `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '聯系人',
          `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定電話',
          `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移動電話',
          PRIMARY KEY (`cust_id`)
        ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    -- 聯系人表的內容
    -- 在這里表明了外鍵叫“lkm_cust_id”,它指向了客戶表的“cust_id”字段。
    CREATE TABLE `cst_linkman` (
          `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯系人編號(主鍵)',
          `lkm_name` varchar(16) DEFAULT NULL COMMENT '聯系人姓名',
          `lkm_cust_id` bigint(32) NOT NULL COMMENT '客戶id',
          `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_qq` varchar(16) DEFAULT NULL COMMENT '聯系人qq',
          `lkm_position` varchar(16) DEFAULT NULL COMMENT '聯系人職位',
          `lkm_memo` varchar(512) DEFAULT NULL COMMENT '聯系人備注',
          PRIMARY KEY (`lkm_id`),
          KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
          CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
        ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    
  1. 創建關于兩個表的Bean類
    客戶的bean類:

    package cn.itheima.domain;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Customer {
    
        private Long cust_id;
        
        private String cust_name;
        private String cust_source;
        private String cust_industry;
        private String cust_level;
        private String cust_linkman;
        private String cust_phone;
        private String cust_mobile;
        
        private Set<LinkMan> linkMens = new HashSet<LinkMan>();
        
        public Long getCust_id() {
            return cust_id;
        }
        public void setCust_id(Long cust_id) {
            this.cust_id = cust_id;
        }
        public String getCust_name() {
            return cust_name;
        }
        public void setCust_name(String cust_name) {
            this.cust_name = cust_name;
        }
        public String getCust_source() {
            return cust_source;
        }
        public void setCust_source(String cust_source) {
            this.cust_source = cust_source;
        }
        public String getCust_industry() {
            return cust_industry;
        }
        public void setCust_industry(String cust_industry) {
            this.cust_industry = cust_industry;
        }
        public String getCust_level() {
            return cust_level;
        }
        public void setCust_level(String cust_level) {
            this.cust_level = cust_level;
        }
        public String getCust_linkman() {
            return cust_linkman;
        }
        public void setCust_linkman(String cust_linkman) {
            this.cust_linkman = cust_linkman;
        }
        public String getCust_phone() {
            return cust_phone;
        }
        
        public Set<LinkMan> getLinkMens() {
            return linkMens;
        }
        public void setLinkMens(Set<LinkMan> linkMens) {
            this.linkMens = linkMens;
        }
        public void setCust_phone(String cust_phone) {
            this.cust_phone = cust_phone;
        }
        public String getCust_mobile() {
            return cust_mobile;
        }
        public void setCust_mobile(String cust_mobile) {
            this.cust_mobile = cust_mobile;
        }
        @Override
        public String toString() {
            return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
        }
    }
    

    linkMens字段是客戶表中沒有的,這里寫出來linkMens并給他設置set,get是因為一個客戶對應多個聯系人,在后面的代碼中有類似將聯系人和客戶綁定的操作,需要用到linkMens字段。

聯系人的bean類:

package cn.itheima.domain;
//聯系人實體
public class LinkMan {

  private Long lkm_id;
  private Character lkm_gender;
  private String lkm_name;
  private String lkm_phone;
  private String lkm_email;
  private String lkm_qq;
  private String lkm_mobile;
  private String lkm_memo;
  private String lkm_position;
  
  //表達多對一關系
  private Customer customer ;

  public Customer getCustomer() {
      return customer;
  }
  public void setCustomer(Customer customer) {
      this.customer = customer;
  }
  public Long getLkm_id() {
      return lkm_id;
  }
  public void setLkm_id(Long lkm_id) {
      this.lkm_id = lkm_id;
  }
  public Character getLkm_gender() {
      return lkm_gender;
  }
  public void setLkm_gender(Character lkm_gender) {
      this.lkm_gender = lkm_gender;
  }
  public String getLkm_name() {
      return lkm_name;
  }
  public void setLkm_name(String lkm_name) {
      this.lkm_name = lkm_name;
  }
  public String getLkm_phone() {
      return lkm_phone;
  }
  public void setLkm_phone(String lkm_phone) {
      this.lkm_phone = lkm_phone;
  }
  public String getLkm_email() {
      return lkm_email;
  }
  public void setLkm_email(String lkm_email) {
      this.lkm_email = lkm_email;
  }
  public String getLkm_qq() {
      return lkm_qq;
  }
  public void setLkm_qq(String lkm_qq) {
      this.lkm_qq = lkm_qq;
  }
  public String getLkm_mobile() {
      return lkm_mobile;
  }
  public void setLkm_mobile(String lkm_mobile) {
      this.lkm_mobile = lkm_mobile;
  }
  public String getLkm_memo() {
      return lkm_memo;
  }
  public void setLkm_memo(String lkm_memo) {
      this.lkm_memo = lkm_memo;
  }
  public String getLkm_position() {
      return lkm_position;
  }
  public void setLkm_position(String lkm_position) {
      this.lkm_position = lkm_position;
  }
}

這里也多了一個屬性叫做customer,是為了后面的綁定操作。和上面的linkMens作用一樣。

  1. 創建hbm.xml

客戶表的Bean類的hbm.xml:Customer.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.itheima.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屬性:集合屬性名
            column屬性: 外鍵列名
            class屬性: 與我關聯的對象完整類名
         -->
        <set name="linkMens">
            <key column="lkm_cust_id" ></key>
            <one-to-many class="LinkMan" />
        </set>
    </class>
</hibernate-mapping>

*這里指出set以及其內部的key和one-to-many

聯系人表的Bean類的hbm.xml:LinkMan.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.itheima.domain" >
    <class name="LinkMan" table="cst_linkman" >
        <id name="lkm_id"  >
            <generator class="native"></generator>
        </id>
        <property name="lkm_gender"  ></property>
        <property name="lkm_name"  ></property>
        <property name="lkm_phone"  ></property>
        <property name="lkm_email"  ></property>
        <property name="lkm_qq"  ></property>
        <property name="lkm_mobile"  ></property>
        <property name="lkm_memo"  ></property>
        <property name="lkm_position"  ></property>
        
        <!-- 多對一 -->
        <!-- 
            name屬性:引用屬性名
            column屬性: 外鍵列名
            class屬性: 與我關聯的對象完整類名
         -->
        <many-to-one name="customer" column="lkm_cust_id" class="Customer"  >
        </many-to-one>
    </class>
</hibernate-mapping>

在這里指出many-to-one就可以了

這里的聯系人表property只有name沒有指明column,不知道為什么,不知道是一定不能指明還是指明不指明都可以

  1. 在hibernate.cfg.xml中把兩個hbm.xml的位置指出來。
<mapping resource="cn/itheima/domain/Customer.hbm.xml" />
<mapping resource="cn/itheima/domain/LinkMan.hbm.xml"/>
  1. 代碼操作
@Test
//保存客戶 以及客戶 下的聯系人
public void fun1(){
  Session session = HibernateUtils.openSession();
  Transaction transaction = session.beginTransaction();
  
  //1.客戶創建對象(一個)
  Customer c= new Customer();
  c.setCust_name("xxx");
  //2.創建聯系人對象(多個)
  LinkMan linkMan1 = new LinkMan();
  linkMan1.setLkm_name("111");
  LinkMan linkMan2 = new LinkMan();
  linkMan2.setLkm_name("222");
  LinkMan linkMan3 = new LinkMan();
  linkMan3.setLkm_name("333");
  //相互綁定
  c.getLinkMens().add(linkMan1);
  c.getLinkMens().add(linkMan2);
  c.getLinkMens().add(linkMan3);
  //相互綁定
  linkMan1.setCustomer(c);
  linkMan2.setCustomer(c);
  linkMan3.setCustomer(c);
  
  
  session.save(c);
  session.save(linkMan1);
  session.save(linkMan2);
  session.save(linkMan3);
  
  transaction.commit();
  session.close();
}
@Test
//為客戶增加聯系人
public void fun2(){
  //1 獲得session
  Session session = HibernateUtils.openSession();
  //2 開啟事務
  Transaction tx = session.beginTransaction();
  //-------------------------------------------------
  //3操作
  //1> 獲得要操作的客戶對象
  Customer c = session.get(Customer.class,1l);
  //2> 創建聯系人
  LinkMan lm1 = new LinkMan();
  lm1.setLkm_name("郝強勇");
  //3> 將聯系人添加到客戶,將客戶設置到聯系人中
  c.getLinkMens().add(lm1);
  lm1.setCustomer(c);
  //4> 執行保存
  session.save(lm1);
  //-------------------------------------------------
  //4提交事務
  tx.commit();
  //5關閉資源
  session.close();
}
@Test
//為客戶刪除聯系人
public void fun3(){
  //1 獲得session
  Session session = HibernateUtils.openSession();
  //2 開啟事務
  Transaction tx = session.beginTransaction();
  //-------------------------------------------------
  //3操作
  //1> 獲得要操作的客戶對象
  Customer c = session.get(Customer.class,1l);
  //2> 獲得要移除的聯系人
  LinkMan lm = session.get(LinkMan.class, 3l);
  //3> 將聯系人從客戶集合中移除
  c.getLinkMens().remove(lm);
  lm.setCustomer(null);
  //-------------------------------------------------
  //4提交事務
  tx.commit();
  //5關閉資源
  session.close();
}
  1. 小總結一波
    這種一對多和單個表的不同地方在于:
  • bean類中需要加入對方類型的屬性,不然后面沒法綁定。
  • hbm.xml中要指出一些一對多,多對一的信息。
  • hibernate.cfg.xml要把所有的hbm.xml指明出來
  • 代碼中也要相互綁定。

級聯操作

這可以簡化操作。
比如原來保存是這樣的。客戶和聯系人需要單獨保存。

session.save(c);  
session.save(linkMan1);  
session.save(linkMan2);  
session.save(linkMan3);  

有了級聯操作以后,就可以只保存客戶,和客戶掛鉤的三個聯系人會自動保存。

session.save(c);//一句就可以

在hbm.xml中配置cascade就可以了。
比如:

<set name="linkMens" cascade="delete"  >
  <key column="lkm_cust_id" ></key>
  <one-to-many class="LinkMan" />
</set>

cascade有四個屬性值:

  • delete:級聯刪除,刪除一個就刪除另一個
  • save-update: 級聯保存更新,保存一個也保存另一個
  • all:save-update+delete
  • none:默認值,不級聯

兩個hbm.xml都可以配置,也可以只配置一個。比如配置了客戶表的save-update,那么保存客戶時,客戶的聯系人也保存了,配置聯系人的hbm.xml時,保存聯系人時,客戶也保存了,兩個都配置時,保存任意一個另一個也保存了。

級聯操作的delete有一個邏輯問題,最好不要用比如一個客戶有三個聯系人,我想刪除聯系人a,但是因為delete的配置效果,會把a對應的客戶也刪除了,這是不符合預期的,因為聯系人b和c還在呢。如果客戶也配置了delete的話,客戶刪除的時候還會把b和c也刪除了,這就太不符合預期了。

外鍵關系維護

在默認情況下,一對多中,兩個表都會維護這個外鍵關系。而這個關系其實只要一方維護就可以。在“一”的那一方可以放棄維護,“多”的那一方是不能放棄的。

“一”的那一方放棄的方法就是在hbm.xml中配置inverse="true"。true 表示放棄維護,默認是false。這個配置的位置在:

<set name="linkMens" inverse="true">
  <key column="lkm_cust_id" ></key>
  <one-to-many class="LinkMan" />
</set>

減少一方維護關系可以優化性能

要么兩個都維護關系,要么只“多”的一方維護關系(因為外鍵字段就在“多”的一方)。

多對多

多對多大同小異,兩個表都有外鍵,多對多會引入一個中間表。

注意點:

  • 在兩個表的bean類中都要加入set集合,以及set集合的get,set方法。

    比如在類A里面有:
    private Set<B> users = new HashSet<B>();
    在類B里面有:
    private Set<A> users = new HashSet<A>();

  • 兩個表的hbm.xml配置:
    比如User:

<set name="roles" table="sys_user_role" cascade="save-update" >
  <key column="user_id" ></key>
  <many-to-many class="Role" column="role_id" ></many-to-many>
</set>

name:bean類中的set集合屬性的名稱
table:中間表的名字,自己取名,兩個xml一致即可。
column:我的外鍵名
class:和我多對多對應的那個類
column:和我多對多的那個外鍵

代碼使用:

//多對多關系操作
public class Demo {
    @Test
    //保存員工以及角色
    public void fun1(){
        //1 獲得session
        Session session = HibernateUtils.openSession();
        //2 開啟事務
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 創建兩個 User
        User u1 = new User();
        u1.setUser_name("郝強勇");
        
        User u2 = new User();
        u2.setUser_name("金家德");
        
        //2> 創建兩個 Role
        Role r1 = new Role();
        r1.setRole_name("保潔");
        
        Role r2 = new Role();
        r2.setRole_name("保安");
        //3> 用戶表達關系
        u1.getRoles().add(r1);
        u1.getRoles().add(r2);
        
        u2.getRoles().add(r1);
        u2.getRoles().add(r2);
        
        //4> 角色表達關系
        r1.getUsers().add(u1);
        r1.getUsers().add(u2);
        
        r2.getUsers().add(u1);
        r2.getUsers().add(u2);
        
        //5> 調用Save方法一次保存
        session.save(u1);
        session.save(u2);
        session.save(r1);
        session.save(r2);
        //-------------------------------------------------
        //4提交事務
        tx.commit();
        //5關閉資源
        session.close();
    }
    

注意:
這里如果兩方都表達關系,則需要在配置hbm.xml中有一方放棄維護,或者是兩方都維護關系,但是上述代碼只有一方表達關系,也就是上面的代碼第三步和第四步只留其一。

結論:
以后直接放棄一方維護關系,具體放棄哪一方要根據邏輯自己判斷。

@Test
    //為郝強勇新增一個角色
    public void fun3(){
        //1 獲得session
        Session session = HibernateUtils.openSession();
        //2 開啟事務
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 獲得郝強勇用戶
        User user = session.get(User.class, 1l);
        //2> 創建公關角色
        Role r = new Role();
        r.setRole_name("男公關");
        //3> 將角色添加到用戶中
        user.getRoles().add(r);
        //4> 將角色轉換為持久化
        session.save(r);//user類如果使用了級聯保存這句代碼可以省略。
        //-------------------------------------------------
        //4提交事務
        tx.commit();
        //5關閉資源
        session.close();
    }
    

@Test
//為郝強勇解除一個角色
public void fun4(){
  //1 獲得session
  Session session = HibernateUtils.openSession();
  //2 開啟事務
  Transaction tx = session.beginTransaction();
  //-------------------------------------------------
  //3操作
  //1> 獲得郝強勇用戶
  User user = session.get(User.class, 1l);
  //2> 獲得要操作的角色對象(保潔,保安)
  Role r1 = session.get(Role.class, 1l);
  Role r2 = session.get(Role.class, 2l);
  //3> 將角色從用戶的角色集合中移除
  user.getRoles().remove(r1);
  user.getRoles().remove(r2);
  
  //-------------------------------------------------
  //4提交事務
  tx.commit();
  //5關閉資源
  session.close();
}
}

多對多也有cascade級聯操作,這里更不能用delete。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容

  • 本文包括: 1、CRM 項目的整體介紹 2、Hibernate 框架概述 3、Hibernate 快速入門 4、H...
    廖少少閱讀 3,484評論 9 66
  • 本文包括:1、一對多結構的準備2、雙向關聯與單向關聯3、級聯保存4、級聯刪除5、cascade 屬性——級聯6、i...
    廖少少閱讀 1,235評論 1 6
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,738評論 18 399
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,831評論 0 11
  • CocoaPods報錯:The dependency `xxxxx` is not used in any con...
    江游兒閱讀 387評論 0 1