Hibernate—— 一對多 和 多對多關聯關係映射(xml和註解)總結(轉載)

  • One to Many 映射關係
    • 多對一單向外鍵關聯(XML/Annotation)
    • 一對多單向外鍵關聯(XML/Annotation)
    • 懶加載和積極加載
    • 一對多雙向外鍵關聯(XML/Annotation)
  • Many to Many 映射關係
    • 多對多單向外鍵關聯(XML/Annotation)
    • 多對多雙向外鍵關聯(XML/Annotation)
    • set的inverse元素詳解
  • 問題小結
  • 關聯關係的優缺點

    多對一單向外鍵關聯關係html

      注意多對一關聯是多方持有一方的引用。看一個例子,去淘寶購物,那麼一個淘寶用戶能夠對應多個購物訂單,如圖所示:java

      多的一方是Orders,持有一方的引用,也就是Users,而在Users中無需做任何定義,從訂單到用戶的關係是單向多對一關聯。對應數據庫就是:數據庫

      還有好比說學生和班級的關係,多個學生能夠屬於同一個班級,這就是從學生到班級也是典型的單向多對一關係,看代碼實現:緩存

      

      基於註解的多對一單向外鍵關聯:session

      單向多對一關聯中,多方須要持有一方的引用,那麼多方(學生類)須要額外配置,須要對持有的一方引用使用註解@ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER),設置爲級聯操做和飢渴的抓取策略,@JoinColumn(name="cid"),而一方(教室類)無需作任何多方的定義。架構

      注意;多方必須保留一個不帶參數的構造器!併發

    複製代碼
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    //班級類,在多對一關係中屬於一的方,不持有其餘多餘的配置,反而是被多方持有
    @Entity
    public class ClassRoom {
        private int cid;//班級編號
    
        private String cname;//班級名稱
    
    //    自動增加的主鍵
        @Id
        @GeneratedValue
        public int getCid() {
            return cid;
        }
    
        public void setCid(int cid) {
            this.cid = cid;
        }
    
        public String getCname() {
            return cname;
        }
    
        public void setCname(String cname) {
            this.cname = cname;
        }
    }

     

    一方——班級類無需作多餘的定義,下面是多方——學生實體和配置:app

    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    
    //學生實體類,屬於多對一的多方,持有班級(一方)的引用
    @Entity
    public class Students {
        private int sid;  //編號
    
        private String sname; //姓名
    
        private ClassRoom classroom;//學生班級
        
        //注意:多方必定要顯式的定義不帶參數的構造方法
        public Students() {
        }
    
        public Students(String sname)
        {
            this.sname = sname;
        }
    //    多方使用註解:@ManyToOne
    //  fetch=FetchType.EAGER,急加載,加載一個實體時,定義急加載的屬性會當即從數據庫中加載。
    //    所有級聯操做,referencedColumnName顯式設置數據庫字段名cid,不寫默認就是和name同樣的。
        @ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
        @JoinColumn(name="cid",referencedColumnName="cid")
        public ClassRoom getClassroom() {
            return classroom;
        }
    
        public void setClassroom(ClassRoom classroom) {
            this.classroom = classroom;
        }
    
        //    自動增加主鍵
        @Id
        @GeneratedValue
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    }

     

    下面測試:先生成數據庫腳本,再進行學生對象的插入框架

    public class TestStudentsByAnno {
        private static SessionFactory sessionFactory;
        
        @Before
        public void setUp() throws Exception {
            System.out.println("setUp()...");
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        }
    
        @After
        public void tearDown() throws Exception {
            System.out.println("tearDown()...");
            sessionFactory.close();
        }
    
        @Test
        public void testSave() {
            Session session = sessionFactory.getCurrentSession();
            Transaction tx = session.beginTransaction();
    
            try    {
                ClassRoom c = new ClassRoom();
                c.setCname("computer001");
    
                Students s = new Students("zhangsan");
                s.setClassroom(c);
    
                session.save(s);    
                tx.commit();
            } catch(Exception ex) {
                ex.printStackTrace();
                tx.rollback();
            }
        }
    
        @Test
        @Ignore
        public void testSchemaExport() {
            SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
            se.create(true, true);
        }
    }

     

    反向建立表的數據庫腳本以下:性能

    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))

    插入一個學生對象,會自動生成以下語句:

     ClassRoom c = new ClassRoom();
                c.setCname("computer001");
    
                Students s = new Students("zhangsan");
                s.setClassroom(c);
    
                session.save(s);    
                tx.commit();

     

    Hibernate: insert into ClassRoom (cname) values (?)

    Hibernate: insert into Students (cid, sname) values (?, ?)

    插入成功:

     

      基於xml配置實現多對一單向外鍵關聯

    <hibernate-mapping>
      <class name="net.nw.vo.fk.mto.ClassRoom" table="classroom">
        <id name="cid" column="cid" type="int">
          <generator class="native"/>
        </id>
        
        <property name="cname" column="cname" type="string"/>
      </class>
    </hibernate-mapping>

     

      一方(教室類)無需作任何多方的定義。只須要維護好本身的屬性配置便可。而多方只須要加上<many-to-one name="" column=「"/>就ok。

    <hibernate-mapping>
      <class name="net.nw.vo.fk.mto.Students" table="students">
        <id name="sid" column="sid" type="int">
          <generator class="native"/>
        </id>
        
        <property name="sname" column="sname" type="string"/>
        
        <many-to-one name="classroom" column="cid"/>
      </class>
    </hibernate-mapping>

     

      hibernate.cfg.xml里加上

      <mapping resource="net/nw/vo/fk/mto/ClassRoom.hbm.xml" />
            <mapping resource="net/nw/vo/fk/mto/Students.hbm.xml" />

     

    注意:若是沒有設置級聯ALL,那麼須要在保存的時候先保存班級,在保存學生,不然出錯: object references an unsaved transient instance - save the transient instance before flushing: 

                ClassRoom classRoom = new ClassRoom();
                classRoom.setCname("CS");
    
                Students students = new Students("111");
                students.setClassroom(classRoom);
    
                session.save(classRoom);
                session.save(students);
                tx.commit();
    

     

      小結:使用<many-to-one>元素進行多對一關聯關係配置,name屬性  指定類的屬性名,column屬性 指定庫表字段名,class屬性  指定類屬性類型(加上姓,即包名),not-null屬性 指定屬性是否容許爲空,cascade屬性 指定是否級聯保存和更新:save-update、delete、all、none。

      

      一對多單向外鍵關聯

      當類與類創建了關聯,程序能很方便的從一個對象導航到另外一個或一組與之關聯的對象,有了student對象,就能夠經過student對象獲得這個學生所屬的班級的信息——students.getClassroom();,對於班級對象,若是想要獲得某個學生的信息,怎麼辦呢?這時候能夠反過來控制,一方控制多方,下面進行一對多單向外鍵關聯。

      簡單說就是和以前多對一相反,以前是多方持有一方的引用,而一對多關聯關係是一方持有多方的集合的引用,注意區別:這裏是持有多方的集合。

     

      基於註解的配置:

       @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY),@JoinColumn(name=""),除了級聯以外,還要設置一方爲懶加載模式。且外鍵仍是加在了多方學生表裏,只不過控制權變了,以前多對一關聯是多方學生持有班級外鍵,控制班級,如今一對多關聯,表裏仍是多方學生持有班級一方的外鍵,只不過控制權交給了班級,讓班級控制學生。不要混淆。

    import javax.persistence.*;
    import java.util.Set;
    
    //班級類是一方,一方持有多方的引用
    @Entity
    public class ClassRoom {
        private int cid;//班級編號
    
        private String cname;//班級名稱
    
        private Set<Students> stus ;//班級的學生集合是多方
    
    //    如今是一方維護多方了,主控權交給了一方,設置級聯,一方要設置懶加載,推薦!
        @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
        @JoinColumn(name="cid") // 設置一方的外鍵,這裏是cid,由於實際上這個外鍵仍是加在多方,只不過控制權變了。
        public Set<Students> getStus() {
            return stus;
        }
    
        public void setStus(Set<Students> stus) {
            this.stus = stus;
        }
    
        @Id
        @GeneratedValue
        public int getCid() {
            return cid;
        }
    
        public void setCid(int cid) {
            this.cid = cid;
        }
    
        public String getCname() {
            return cname;
        }
    
        public void setCname(String cname) {
            this.cname = cname;
        }
    }

     

      注意,不論多對一仍是一對多,多方都要顯式保留無參構造器。

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    //學生實體類
    @Entity
    public class Students {
        private int sid;  //編號
    
        private String sname; //姓名
        
        //注意:必定要保留這個默認不帶參數的構造方法
        public Students(){
            
        }
    
        public Students(String sname)
        {
            this.sname = sname;
        }
    
        @Id
        @GeneratedValue
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    }

     

      執行數據庫腳本,發現一方(主控方)仍是和以前多對一的表結構同樣,多方也是如此。

    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))

      執行測試,保存學生,由於如今關係是一方維護,控制多方。確定保存主控方——班級(和以前相反,以前多對一保存的是多方學生對象),可是本質上仍是先保存的學生班級,再自動保存學生,這點和多對一本質同樣。

                Set<Students> stus = new HashSet<>();
                stus.add(new Students("zhangsan"));
                stus.add(new Students("lisi"));
                stus.add(new Students("wangwu"));
                stus.add(new Students("zhaoliu"));
                stus.add(new Students("sunqi"));
    
                ClassRoom c = new ClassRoom();
                c.setCname("cs001");
                c.setStus(stus);
    
                session.save(c);
                tx.commit();

     

      生成的腳本以下:先插入外鍵的班級對象,在執行五個學生的插入操做,最後執行五個更新,爲sid=1。。。5的學生,更新cid爲2

    Hibernate: insert into ClassRoom (cname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?

     

      

      總結:多對一時候,多方設置EAGER,一方設置LAZY,也就是說,若是是多對一,多方控制一方,那麼多方設置積極加載,一方無需多餘配置,反過來,若是是一對多關係,一方控制多方,那麼一方設置懶加載,多方無需多餘配置,可是不論哪一種,多方都顯式加上一個不帶參數的構造器。

      

      一對多裏的懶加載

      記得以前總結,get和load的查詢方式源碼的時候,就總結了一下懶加載load裏的應用,以前說Hibernate中,當訪問的數據量過大時,用緩存也不太合適, 由於內存容量有限 ,爲了減小併發量,減小系統資源的消耗,Hibernate用懶加載機制來彌補這種缺陷,可是這只是彌補而不是用了懶加載整體性能就提升了。懶加載也被稱爲延遲加載,它在查詢的時候不會馬上訪問數據庫,而是返回代理對象,好比以前總結的load方式查詢,當真正去使用對象的時候纔會訪問數據庫。除了load查詢默認使用懶加載,如今咱們的一對多關聯映射也使用了lazy加載,下面進行實際驗證測試:

      public void testQuery()    {
            Session session = sessionFactory.getCurrentSession();
            Transaction tx = session.beginTransaction();
    
            try    {
                // 首先查詢班級,cid=1的班級
                ClassRoom c =(ClassRoom) session.get(ClassRoom.class, 1);
    
                // 經過班級導航到學生,遍歷學生獲得名字
                for(Students s : c.getStus()) {
                    System.out.println("姓名 :" + s.getSname());
                }
                
                tx.commit();
            } catch(Exception ex) {
                ex.printStackTrace();
                tx.rollback();
            }
        }

     

      執行以後,debug發現:在沒有使用班級對象的時候,只有這樣一條SQL語句:從classroom表查詢,cid=1的班級,使用使用AS賦給列一個別名。

    Hibernate: select classroom0_.cid as cid0_0_, classroom0_.cname as cname0_0_ from ClassRoom classroom0_ where classroom0_.cid=?

     

      等執行到for了,纔打印這一語句:

    Hibernate: select stus0_.cid as cid0_1_, stus0_.sid as sid1_, stus0_.sid as sid1_0_, stus0_.sname as sname1_0_ from Students stus0_ where stus0_.cid=?

     

      充分說明這是執行的懶加載模式。一方控制多方,一方設置懶加載,若是什麼都不設置,會是什麼狀況?通過驗證,發現和顯式設置懶加載效果同樣,也就是說,one-to-many(元素)的懶加載是默認的,這是必須的,是經常使用的策略。一對多的時候,查詢主對象時默認是懶加載。即:查詢主對象的時候不會把從對象查詢出來,使用從對象的時候才加載從對象。

      若是人爲設置爲積極加載,則直接所有查詢,@OneToMany(cascade={CascadeType.ALL},fetch=FetchType.EAGER),SQL語句爲以下,進行了外鏈接的查詢。一次性查了主對象和從對象出來。

    Hibernate: select classroom0_.cid as cid0_1_, classroom0_.cname as cname0_1_, stus1_.cid as cid0_3_, stus1_.sid as sid3_, stus1_.sid as sid1_0_, stus1_.sname as sname1_0_ from ClassRoom classroom0_ left outer join Students stus1_ on classroom0_.cid=stus1_.cid where classroom0_.cid=?

     

     

      基於xml文件配置

      一方做爲主控方:

    <set name="" >
    
       <key column=""/>
    
       <one-to-many class= "" />
    
    </set>

     

      一方是班級,持有多方的集合,以下配置:

    <hibernate-mapping>
      <class name="net.nw.vo.fk.otm.ClassRoom" table="classroom">
        <id name="cid" column="cid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="cname" column="cname" type="string"/>
    
        <set name="stus" >
          <!-- 外鍵仍是班級cid -->
          <key column="cid"/>
          <one-to-many class="net.nw.vo.fk.otm.Students" />
        </set>
      </class>
    </hibernate-mapping>

     

      多方是學生,做爲從對象,別忘了,保留無參構造器,以下配置:

    <hibernate-mapping>
      <class name="net.nw.vo.fk.otm.Students" table="students">
        <id name="sid" column="sid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="sname" column="sname" type="string"/>
      </class>
    </hibernate-mapping>

     

      

      一對多雙向外鍵關聯

      其實相似以前的一對一雙向外鍵關聯,也是互相持有對方的引用,故也叫雙向一對多自身關聯。多方持有一方的引用,@ManyToOne(cascade={CascadeType.ALL}),@JoinColumn(name="")。反過來,一方也持有多方的集合,@OneToMany(cascade={CascadeType.ALL}),@JoinColumn(name="")。代碼以下:

      基於註解的配置:

    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    
    //學生實體類,屬於多方,持有一方的引用
    @Entity
    public class Students {
        private int sid;  //編號
    
        private String sname; //姓名
    
        private ClassRoom classroom;//學生班級屬於一方
    
        //注意:必定要在多方保留這個默認不帶參數的構造方法
        public Students() {
    
        }
    
        public Students(String sname) {
            this.sname = sname;
        }
    
    //    多方是設置積極加載,所有級聯
        @ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
        @JoinColumn(name="cid",referencedColumnName="cid") // 外鍵設置爲班級id,cid
        public ClassRoom getClassroom() {
            return classroom;
        }
    
        public void setClassroom(ClassRoom classroom) {
            this.classroom = classroom;
        }
    
        @Id
        @GeneratedValue
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    }

     

      關鍵是一方,也必須持有多方的集合,造成你中有我,我中有你的局面,互相控制。可是仍是注意,本質上,數據庫表裏外鍵cid仍是加在了學生表——多方的表裏。

    import javax.persistence.*;
    import java.util.Set;
    
    //班級類
    @Entity
    public class ClassRoom {
        private int cid;//班級編號
    
        private String cname;//班級名稱
    
        private Set<Students> stus; // 一方也持有了多方:學生的集合引用
    
    //    一方也要控制多方,一方設置懶加載,外鍵仍是cid,也就是外鍵仍是加在多方——學生表。
        @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
        @JoinColumn(name="cid")
        public Set<Students> getStus() {
            return stus;
        }
    
        public void setStus(Set<Students> stus) {
            this.stus = stus;
        }
    
        @Id
        @GeneratedValue
        public int getCid() {
            return cid;
        }
    
        public void setCid(int cid) {
            this.cid = cid;
        }
    
        public String getCname() {
            return cname;
        }
    
        public void setCname(String cname) {
            this.cname = cname;
        }
    }

     

      測試腳本生成。和以前表同樣,只不過控制權雙方都有了:

    alter table Students drop foreign key FK73AC29B8559B6D03
    drop table if exists ClassRoom
    drop table if exists Students
    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))
    alter table Students add index FK73AC29B8559B6D03 (cid), add constraint FK73AC29B8559B6D03 foreign key (cid) references ClassRoom (cid)

     

       此時先保存誰均可以!控制權是雙方都有。

     

      基於xml配置

      多方:<many-to-one name="group" column="gid"></many-to-one>,一方:記住,一方是持有集合
    <set name="" >
    
       <key column=""></key>
    
       <one-to-many class=""/>
    
    </set>

     

      本例代碼以下:

    <hibernate-mapping>
      <class name="net.nw.vo.bfk.mto.Students" table="students">
        <id name="sid" column="sid" type="int">
          <generator class="native"/>
        </id>
        
        <property name="sname" column="sname" type="string"/>
        
        <many-to-one name="classroom" column="cid"/>
      </class>
    </hibernate-mapping>
    
    ----------------------------------------------------------------------------------
    
    <hibernate-mapping>
      <class name="net.nw.vo.bfk.mto.ClassRoom" table="classroom">
        <id name="cid" column="cid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="cname" column="cname" type="string"/>
    
        <set name="stus" >
          <key column="cid"/>
          <one-to-many class="net.nw.vo.bfk.mto.Students" />
        </set>
      </class>
    </hibernate-mapping>

     

      小結:在關係模型中,只存在外鍵參照關係,並且是many方參照one方。

     

      多對多的關聯關係映射

      如今有一個角色類,和一個特權類,前者保存了都有哪些人(角色)擁有哪些特權,後者保存的是一些特權,好比能夠作什麼,不能夠作什麼等讓哪些人擁有。如圖類關係:

      這就是一個多對多的例子,他們之間在數據庫如何實現的關聯呢?顯然不能互相持有對方主鍵作外鍵,那麼就須要用到一個新的表——映射表做爲中間表:

      再舉一個最熟悉的學生的例子,現實中,學生和教師就構成了多對多的關聯關係。一個教師能夠教不少學生,同時一個學生能夠師從不少老師,拿這個例子說明。

     

      多對多單向外鍵關聯

      其中一個多方持有另外一個多方的集合對象,並且前面也分析了,還須要建立一箇中間表,先看兩個實體對象配置
      
       基於註解的多對多單向外鍵關係配置:
      一個多方持有另外一個多方的集合對象,我讓學生持有老師的集合對象引用,一樣的另外一個多方——老師不作多餘配置,且多方要顯式設置一個無參構造器,那麼學生須要使用註解@ManyToMany和@JoinTable,設置級聯所有cascade=CascadeType.ALL,代碼以下:
    import javax.persistence.*;
    import java.util.Set;
    
    //學生實體類
    @Entity
    public class Students {
        private int sid;  //編號
    
        private String sname; //姓名
    
        private Set<Teachers> teachers ;  // 我設置學生這個多方去持有老師這個多方的集合,去控制老師
    
        //注意:必定要保留這個默認不帶參數的構造方法
        public Students() {
            
        }
    
        public Students(String sname)
        {
            this.sname = sname;
        }
    
    //    先設置多對多的關聯,以後必須生成一箇中間表,使用JoinTable註解
        @ManyToMany(cascade=CascadeType.ALL)
        @JoinTable(
    //      設置中間表名
          name="teachers_students",
    //      指定當前對象的外鍵,本表在中間表的外鍵名稱
          joinColumns={@JoinColumn(name="sid")},
    //      指定關聯對象的外鍵,另外一個表在中間表的外鍵名稱。
          inverseJoinColumns={@JoinColumn(name="tid")}
         )
        public Set<Teachers> getTeachers() {
            return teachers;
        }
    
        public void setTeachers(Set<Teachers> teachers) {
            this.teachers = teachers;
        }
        
        @Id
        @GeneratedValue
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    }

     

      另外一個多方,老師不作多餘配置:

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    @Entity
    public class Teachers {
        private int tid;//教師的編號
    
        private String tname;//教師姓名
        
        public Teachers() {
            
        }
    
        public Teachers(String tname)
        {
          this.tname = tname; 
        }
        
        @Id
        @GeneratedValue
        public int getTid() {
            return tid;
        }
    
        public void setTid(int tid) {
            this.tid = tid;
        }
    
        public String getTname() {
            return tname;
        }
    
        public void setTname(String tname) {
            this.tname = tname;
        }
    }

     

      生成的數據庫腳本以下:

    create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid))
    create table Teachers (tid integer not null auto_increment, tname varchar(255), primary key (tid))
    create table teachers_students (sid integer not null, tid integer not null, primary key (sid, tid))

      主要關注中間表,sid和tid都是做爲了中間表的聯合主鍵,他們同時也是外鍵:

      下面進行插入數據的測試,由於學生持有老師集合引用,且設置了級聯,故直接保存學生就ok:
                // 由於學生持有教師的集合,先設置教師
                Set<Teachers> teachers = new HashSet<>();
                
                teachers.add(new Teachers("Wang"));
                teachers.add(new Teachers("Li"));
                teachers.add(new Teachers("Song"));
                teachers.add(new Teachers("Zhang"));
    
                Students s = new Students();
                s.setSname("zhangsan");
                s.setTeachers(teachers);
    
                session.save(s);
                tx.commit();

     

     

      基於xml的多對多單向外鍵關係配置:

      學生這個多方持有老師的集合,那麼持有對方集合的學生映射文件配置以下: 

    <hibernate-mapping>
      <class name="net.nw.vo.fk.mtm.Students" table="students">
        <id name="sid" column="sid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="sname" column="sname" type="string"/>
    
        <!-- 學生表持有老師的集合,以下進行配置 -->
        <set name="teachers" table="students_teachers" cascade="all">
          <!-- table設置中間表,級聯是all -->
          <!-- key設置本對象在中間表的外鍵sid -->
          <key column="sid"/>
          <!-- many-to-many 標籤設置對方的表(老師)在中間表的外鍵tid -->
          <many-to-many class= "net.nw.vo.fk.mtm.Teachers" column="tid"/>
        </set>
      </class>
    </hibernate-mapping>

     

      老師表配置就簡單了:

    <hibernate-mapping>
      <class name="net.nw.vo.fk.mtm.Teachers" table="teachers">
        <id name="tid" column="tid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="tname" column="tname" type="string"/>
      </class>
    </hibernate-mapping>

     

      進行測試(刪除以前的表,先刪除中間表,在刪除老師表,最後刪除學生表):

                Set<Teachers> teachers = new HashSet<>();
                
                teachers.add(new Teachers("Teacher Wang"));
                teachers.add(new Teachers("Teacher Li"));
                teachers.add(new Teachers("Teacher Song"));
                teachers.add(new Teachers("Teacher Zhang"));
    
                Students s = new Students();
                s.setSname("zhangsan");
                s.setTeachers(teachers);
    
                session.save(s);
                tx.commit();

     

     

      多對多雙向外鍵關聯

      和以前的相似,是互相持有對方的集合,雙方持有對方的集合對象,其中一方設置@ManyToMany(mappedBy=""),另外一方:

    @ManyToMany
    
      @JoinTable(
    
      name="",
    
      joinColumns={@JoinColumn(name="")},
    
      inverseJoinColumns={@JoinColumn(name="")}
    
    )

     

      基於註解的配置,看具體代碼:

    import javax.persistence.*;
    import java.util.Set;
    
    //學生實體類
    @Entity
    public class Students {
        private int sid;  //編號
    
        private String sname; //姓名
    
        private Set<Teachers> teachers ;
    
        //注意:必定要保留這個默認不帶參數的構造方法
        public Students() {
        }
    
        public Students(String sname)
        {
            this.sname = sname;
        }
         
        @ManyToMany(cascade=CascadeType.ALL)
        @JoinTable(
          name="teachers_students",
          joinColumns={@JoinColumn(name="sid")},
          inverseJoinColumns={@JoinColumn(name="tid")}
         )
        public Set<Teachers> getTeachers() {
            return teachers;
        }
    
        public void setTeachers(Set<Teachers> teachers) {
            this.teachers = teachers;
        }
        
        @Id
        @GeneratedValue
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    }
    

     

      關鍵是另外一方的配置,前面總結了,雙向關聯不會真的是互相維持,只能交給一方去維護:

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.ManyToMany;
    import java.util.Set;
    
    @Entity
    public class Teachers {
        private int tid;//教師的編號
    
        private String tname;//教師姓名
    
        private Set<Students> stus ;
    
        public Teachers() {
        }
    
        public Teachers(String tname)
        {
          this.tname = tname; 
        }
    
    //    把控制權交給student類——teachers集合引用
        @ManyToMany(mappedBy="teachers") 
        public Set<Students> getStus() {
            return stus;
        }
    
        public void setStus(Set<Students> stus) {
            this.stus = stus;
        }
        
        @Id
        @GeneratedValue
        public int getTid() {
            return tid;
        }
    
        public void setTid(int tid) {
            this.tid = tid;
        }
    
        public String getTname() {
            return tname;
        }
    
        public void setTname(String tname) {
            this.tname = tname;
        }
    }

     

      生成數據庫腳本:(和以前的單向多對多同樣的表結構,關鍵看實體中控制權的變化)

    create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid))
    create table Teachers (tid integer not null auto_increment, tname varchar(255), primary key (tid))
    create table teachers_students (sid integer not null, tid integer not null, primary key (sid, tid))

      基於xml的配置:

      其中一方:
    <set name="teachers" table="students_teachers">
    
      <key column="sid"></key>
    
      <many-to-many class="net.nw.vo.Teachers" column="tid"/>
    
    </set>

     

      另外一方:

    <set name="students" table="students_teachers">
    
      <key column="tid"></key>
    
      <many-to-many class="net.nw.vo.Students" column="sid"/>
    
    </set>

     

      具體代碼:

    <hibernate-mapping>
      <class name="net.nw.vo.bfk.mtm.Students" table="students">
        <id name="sid" column="sid" type="int">
          <generator class="native"/>
        </id>
        
        <property name="sname" column="sname" type="string"/>
        
        <set name="teachers" table="students_teachers" cascade="all">
          <key column="sid"/>
          <many-to-many class= "net.nw.vo.bfk.mtm.Teachers" column="tid"/>
        </set>
      </class>
    </hibernate-mapping>
    
    ---------------------------------------------------------------------
    
    <hibernate-mapping>
      <class name="net.nw.vo.bfk.mtm.Teachers" table="teachers">
        <id name="tid" column="tid" type="int">
          <generator class="native"/>
        </id>
    
        <property name="tname" column="tname" type="string"/>
    
        <set name="stus" table="students_teachers" cascade="all">
          <key column="tid"/>
          <many-to-many class= "net.nw.vo.bfk.mtm.Students" column="sid"/>
        </set>
      </class>
    </hibernate-mapping>

     

      

      注意:set元素配置;

      屬性name  指定類的屬性名,table指定多對多關聯關係中間表,cascade 級聯操做屬性:save-update、delete、all、none,通常all就ok,lazy屬性能夠指定是不是懶加載。set的子元素key元素——設定本表在中間表的外鍵名稱。

        inverse屬性設置:

      inverse是Hibernate中雙向關聯關係中的基本概念,在xml配置裏,用來設置關係由哪一方來維護,inverse=true 表示被控方,false表示主控方,在多對一,一對一關聯關係中,Hibernate默認設置多方的inverse=true,即多方爲被控方,一方的inverse=false,即一方爲主控方。在多對多關係中須要咱們本身設置哪一方爲被控方即設置inverse=true。上述例子沒有設置,其實須要手動設置的,對應註解裏的mappedBy屬性。

     

      關聯關係的優缺點

      使用關聯關係,就能夠直接操做內存中的對象,不用每次都查詢數據庫,會提升效率;並且域模型真實反映了客觀世界的關係,可是缺點就是創建複雜的關聯關係會給程序開發帶來麻煩,當修改一個對象時,會牽連其它的對象,這也是爲何不少人說什麼hibernate很差用……其實就是沒學好,通常人掌握的不紮實,總出錯,後來MyBatis橫空出現,你們就去用它了,比起hibernate來,上手很是簡單,使用原生SQL……本質不是一個真正的ORM架構模式的實現框架。固然Mybatis也有它的優勢和缺點,之後再總結它。
       一句話: 具體要創建對象之間的什麼關聯關係要根據具體的需求。具體使用什麼框架,要聽領導的,哈哈。、
     

      問題小結

      • 注意在多對一/一對多關係裏:多方必須保留一個不帶參數的構造器!
      • 若是沒有設置級聯ALL,那麼須要在保存的時候先保存班級,在保存學生,不然出錯: object references an unsaved transient instance - save the transient instance before flushing: 
      • 多對一時候,多方設置EAGER加載,一對多的時候,一方設置LAZY加載
      • 多對多關聯,多方須要保留一個無參構造器。
      • 轉載地址:http://www.cnblogs.com/kubixuesheng/p/5300437.html
      • 博主真是大神級啊100個贊!
      • 菜鳥不會改大神的樣式,一點一點的粘的代碼。。。。。
相關文章
相關標籤/搜索