俗話說,自己寫的代碼,6個月后也是別人的代碼……復習!復習!復習!涉及的知識點總結如下:
-
One to Many 映射關系
- 多對一單向外鍵關聯(XML/Annotation)
- 一對多單向外鍵關聯(XML/Annotation)
- 懶加載和積極加載
- 一對多雙向外鍵關聯(XML/Annotation)
-
Many to Many 映射關系
- 多對多單向外鍵關聯(XML/Annotation)
- 多對多雙向外鍵關聯(XML/Annotation)
- set的inverse元素詳解
- 問題小結
- 關聯關系的優缺點
多對一單向外鍵關聯關系
注意多對一關聯是多方持有一方的引用。看一個例子,去淘寶購物,那么一個淘寶用戶可以對應多個購物訂單,如圖所示:
多的一方是Orders,持有一方的引用,也就是Users,而在Users中無需作任何定義,從訂單到用戶的關系是單向多對一關聯。對應數據庫就是:
還有比如說學生和班級的關系,多個學生可以屬於同一個班級,這就是從學生到班級也是典型的單向多對一關系,看代碼實現:
基於注解的多對一單向外鍵關聯:
單向多對一關聯中,多方需要持有一方的引用,那么多方(學生類)需要額外配置,需要對持有的一方引用使用注解@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; } }
一方——班級類無需做多余的定義,下面是多方——學生實體和配置:

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配置:

<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方。
多對多的關聯關系映射
現在有一個角色類,和一個特權類,前者保存了都有哪些人(角色)擁有哪些特權,后者保存的是一些特權,比如可以做什么,不可以做什么等讓哪些人擁有。如圖類關系:
這就是一個多對多的例子,他們之間在數據庫如何實現的關聯呢?顯然不能互相持有對方主鍵做外鍵,那么就需要用到一個新的表——映射表作為中間表:
再舉一個最熟悉的學生的例子,現實中,學生和教師就構成了多對多的關聯關系。一個教師可以教很多學生,同時一個學生可以師從很多老師,拿這個例子說明。
多對多單向外鍵關聯

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都是作為了中間表的聯合主鍵,他們同時也是外鍵:

// 因為學生持有教師的集合,先設置教師 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屬性設置:
關聯關系的優缺點
問題小結
- 注意在多對一/一對多關系里:多方必須保留一個不帶參數的構造器!
- 如果沒有設置級聯ALL,那么需要在保存的時候先保存班級,在保存學生,否則出錯: object references an unsaved transient instance - save the transient instance before flushing:
- 多對一時候,多方設置EAGER加載,一對多的時候,一方設置LAZY加載
- 多對多關聯,多方需要保留一個無參構造器。
歡迎關注
dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!