上一節咱們主要研究下了Hibernate中的一一映射和多對一映射,這節咱們看下Hibernate中的其餘幾種映射,包括一對多映射,多對多映射,複合主鍵映射及繼承映射。java
第一種是一對多映射,「一對多」,顧名思義,是由「一」的一端加載「多」的一端,關係交由「一」來維護。反映在Java代碼中就是在「一」的一端中持有「多」一端的集合,而hibernate把這種關係反映到數據庫的策略是在「多」一端的表上加上一個外鍵指向「一」一端表。顯現,其實這採用的仍是「多対一」的映射原理。 可是,在「一」一端維護關係是咱們不提倡的,由於它有不可避免的缺點,即級聯插入數據的時候要先插「多」一端,這樣形成了兩方面的不妥:1.若是咱們把「多」一端的外鍵必須添加非空約束,將致使數據不能插入;2.即便外鍵不設置爲非空,在插入「多」一端數據時外鍵將暫時爲空( 由於此時它所引用的「一」記錄尚未插入),而只有等到它所引用的「一」記錄插入後,再發出update語句修改外鍵,這樣的效率必然下降。這裏咱們用class和student舉例。sql
<class name="com.xiaoming.test.hibernate.classTest.Classes" table="classes2"> <id name="id" length="4"> <generator class="native"></generator> </id> <property name="name" length="10"></property> <set name="students" cascade="save-update"> <key column="class_id"></key> <one-to-many class="com.xiaoming.test.hibernate.classTest.Student"/> </set> </class>
key的含義,指在另外一端增長的外鍵指向本主鍵.若是設置上屬性not-null="true",表示該外鍵非空,則在由"一"的一端維護關係時,可能致使插入數據異常PropertyValueException.數據庫
one-to-many含義,指出set集合中的元素類型,以供加載時使用session
set標籤後面還能夠加上inverse=ture屬性,inverse="true"含義,把關聯關係交由對方一端維護,而在操做本方數據時再也不維護關係學習
<class name="com.xiaoming.test.hibernate.classTest.Student" table="student2"> <id name="id" length="4"> <generator class="native"></generator> </id> <property name="name" length="10"></property> <many-to-one name="classes" column="class_id" cascade="save-update"></many-to-one> </class>
測試方法以下:測試
Student student1=new Student(); student1.setName("奇隆"); Student student2=new Student(); student2.setName("有朋"); Set<Student> students=new HashSet<Student>(); students.add(student1); students.add(student2); Classes classes=new Classes(); classes.setName("不一班"); classes.setStudents(students); session.save(classes);
這種狀況下若是沒有配置cascade屬性的話,存儲class不成功,由於class對象引用了student對象student1和student2。
spa
Student student1=new Student(); student1.setName("奇隆"); session.save(student1); Student student2=new Student(); student2.setName("有朋"); session.save(student2); Set<Student> students=new HashSet<Student>(); students.add(student1); students.add(student2); Classes classes=new Classes(); classes.setName("不一班"); classes.setStudents(students); session.save(classes);
執行ok,有兩種狀況,一種是<set>標籤中沒有inverse="true。會先執行3條insert語句,分別插入student1和student2對象及class對象,可是此時student對象的classid爲空,因此在執行完insert語句後還會執行對應的update語句來更新對應的student中對應的classid,對應語句以下:hibernate
Hibernate: insert into calss(name, id) values (?, ?) code
Hibernate: insert into student(name, id) values (?, ?)
Hibernate: insert into student (name, id) values (?, ?)
Hibernate: update student set class_id=? where id=?
Hibernate: update sstudent set class_id=? where id=? orm
若是set標籤中有inverse=true的時候,表示把關係交給對方來維護,本身不負責維護相應的操做。存儲過程是這樣的:存classes時,因爲有cascade="save-update",它會先觸發存儲student; 而在存儲student時,inverse="true"指明瞭由它來維護關聯關係,因此他要先存主表class,再回來存副表student
因此對應的sql以下:
Hibernate: insert into sxt_hibernate_class (name, id) values (?, ?)
Hibernate: insert into sxt_hibernate_student (name, class_id, id) values (?, ?, ?)
Hibernate: insert into sxt_hibernate_student (name, class_id, id) values (?, ?, ?)
相應的讀取代碼以下:
Classes classes = (Classes) session.load(Classes.class, 3); System.out.println(classes); Set<Student> students = classes.getStudents(); for (Iterator<Student> stus = students.iterator(); stus.hasNext();) { System.out.println(stus.next()); }
接下來咱們要研究的是多對多的映射,多對多經常用中間表來解決相互的關係,這樣咱們就要使用中間表或中間類來解決。中間類就是把咱們的中間表抽象生成一個實體類,在映射的時候分別和兩個關聯類構成一對多的關係,即演變成兩個一對多來處理。這裏咱們用role和player的對象來講明一下多對多的使用.
<class name="com.xiaoming.test.hibernate.roleTest.Player" table="t_player"> <id name="id" length="4"> <generator class="native"></generator> </id> <property name="name" length="10"></property> <set name="roles" table="t_player_role" cascade="save-update"> <key column="playerid"></key> <many-to-many class="com.xiaoming.test.hibernate.roleTest.Role" column="roleid"></many-to-many> </set> </class>
table屬性的含義,用來指定中間表 ,set中的key column屬性含義,指定中間表中用來指向本表的外鍵
<class name="com.xiaoming.test.hibernate.roleTest.Role" table="t_role"> <id name="id" length="4"> <generator class="native"></generator> </id> <property name="name" length="10"></property> <set name="players" table="t_player_role" cascade="save-update"> <key column="roleid"></key> <many-to-many class="com.xiaoming.test.hibernate.roleTest.Player" column="playerid"></many-to-many> </set> </class>
測試代碼以下:
Role role1=new Role(); role1.setName("後衛"); Role role2=new Role(); role2.setName("前鋒"); Role role3=new Role(); role3.setName("中鋒"); Player player1=new Player(); player1.setName("姚明"); Set<Role> roles1=new HashSet<Role>(); roles1.add(role3); player1.setRoles(roles1); Player player2=new Player(); player2.setName("詹姆斯"); Set<Role> roles2=new HashSet<Role>(); roles2.add(role1); roles2.add(role2); roles2.add(role3); player2.setRoles(roles2); session.save(player1); session.save(player2);*/
這個代碼能夠正確執行,每次保存play的時候都能級聯保存對應的role和相關的關係表數據。
另外一種使用方法以下:
Player player1=new Player(); player1.setName("姚明"); Player player2=new Player(); player2.setName("詹姆斯"); Player player3=new Player(); player3.setName("科比"); Role role1=new Role(); role1.setName("中鋒"); Set<Player> players1=new HashSet<Player>(); players1.add(player1); players1.add(player2); role1.setPlayers(players1); Role role2=new Role(); role2.setName("後衛"); Set<Player> players2=new HashSet<Player>(); players2.add(player2); players2.add(player3); role2.setPlayers(players2); session.save(role1); session.save(role2);*/
是經過role的維度來插入對應的player。
加載代碼以下:
Player player=(Player)session.load(Player.class, 1); System.out.println(player); for(Iterator<Role> iterator=player.getRoles().iterator();iterator.hasNext();){ System.out.println(iterator.next()); }
接下來是符合主鍵的映射,使用了composite-id標籤,對應到一個符合主鍵類。這裏咱們用department來講明符合主鍵使用,department的area和name爲其複合主鍵,對應的配置以下:
<class name="com.xiaoming.test.hibernate.departmentTest.Department" table="department"> <!-- 聯合主鍵 --> <composite-id name="departmentPK"> <key-property name="area" /> <key-property name="name" /> </composite-id> <property name="empCount" length="4" /> <property name="birthday" type="date" /> </class>
其中departmentPk是複合主鍵類,對應的測試代碼以下:
//插入數據 Department dept = new Department(); /** 生成主鍵對象 */ DepartmentPK deptPK = new DepartmentPK(); deptPK.setArea("area1"); deptPK.setName("dept1"); dept.setDepartmentPK(deptPK); dept.setEmpCount(100); dept.setBirthday(new Date()); session.save(dept); //讀取數據 DepartmentPK deptPK = new DepartmentPK(); deptPK.setArea("area1"); deptPK.setName("dept1"); Department dept=(Department)session.load(Department.class, deptPK); System.out.println(dept.getDepartmentPK().getArea()+","+dept.getEmpCount()); //更新數據 DepartmentPK deptPK = new DepartmentPK(); deptPK.setArea("area1"); deptPK.setName("dept1"); Department emp=(Department)session.load(Department.class, deptPK); System.out.println(emp.getDepartmentPK().getArea()+","+emp.getDepartmentPK().getName()+","+emp.getEmpCount()+","+emp.getBirthday()); emp.setEmpCount(100); session.saveOrUpdate(emp); DepartmentPK deptPK2 = new DepartmentPK(); deptPK2.setArea("area1"); deptPK2.setName("dept1"); Department dept=(Department)session.load(Department.class, deptPK2); System.out.println(dept.getDepartmentPK().getArea()+","+dept.getDepartmentPK().getName()+","+dept.getEmpCount()+","+dept.getBirthday());
在映射關係中咱們常常用到的兩個屬性有invere和cascade。cascade定義的是源頭的對象插入或刪除對象時,級聯的關係對象也會作相關的操做。inverse屬性對於多對多中帶有關係表的時候會講對關係表的操做放到另外一方去操做。簡單的說cascade是一種級聯的程度,而invers表示的是是否維持兩個實體的關係(外鍵)
在Hibernte中還有一種映射是繼承映射,繼承也分3種,
1 單表繼承:每顆類繼承樹使用一個表(table per class hlerarchy)
2 具體表繼承:每一個子類一個表(table per subclass)
3 類表繼承:每一個具體類一個表(table per concrete class)
假設咱們有基本類animal屬性有id,name和sex,其子類bird,有屬性height,子類pig有屬性weight
對於第一種狀況,多個類信息存儲在一張表中,可能由表中的一個字段來標註具體是哪一個子類,相應的配置以下:
<class name="Animal" table="t_animal" lazy="false"> <id name="id"> <generator class="native"/> </id> <discriminator column="type" type="string"/> <property name="name"/> <property name="sex"/> <subclass name="Pig" discriminator-value="P"> <property name="weight"/> </subclass> <subclass name="Bird" discriminator-value="B"> <property name="height"/> </subclass> </class>
其中,父類使用discriminator來指定區分不一樣子類的字段,子類使用discriminator-value來表名本子類的discriminator字段。
對於第二種狀況,這種策略是使用joined-subclass標籤來定義子類的。父類、子類,每一個類都對應一張數據庫表。 在父類對應的數據庫表中,實際上會存儲全部的記錄,包括父類和子類的記錄;在子類對應的數據庫表中, 這個表只定義了子類中所特有的屬性映射的字段。子類與父類,經過相同的主鍵值來關聯。其配置關係以下:
<class name="Animal" table="t_animal"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="sex"/> <joined-subclass name="Pig" table="t_pig"> <key column="pid"/> <property name="weight"/> </joined-subclass> <joined-subclass name="Bird" table="t_bird"> <key column="bid"/> <property name="height"/> </joined-subclass> </class>
其中,join-subclass標籤的name屬性是子類的全路徑名,key標籤用來執行子類和父類是經過哪一個字段關聯的。
對於第3中狀況,這種策略是使用union-subclass標籤來定義子類的。每一個子類對應一張表,並且這個表的信息是完備的, 即包含了全部從父類繼承下來的屬性映射的字段,相關的配置以下:
<class name="Animal" abstract="true"> <id name="id"> <generator class="assigned"/> </id> <property name="name"/> <property name="sex"/> <union-subclass name="Pig" table="t_pig"> <property name="weight"/> </union-subclass> <union-subclass name="Bird" table="t_bird"> <property name="height"/> </union-subclass> </class>
這裏unionclass不須要再包含key屬性了。另外class中的abstracet屬性若是爲true的話則不會生成表結構,若是爲false會生成表結構,可是不會插入數據。
總結一下,這裏主要介紹了多對一,多對多的級聯關係,還有其餘的如複合主鍵映射和繼承映射的關係。下一節將學習的是Hibernate中最強大的Hql部分。