1. 程式人生 > >Hibernate繼承對映與多型查詢

Hibernate繼承對映與多型查詢

一、繼承對映:

關係資料庫的表之間不存在繼承關係,為了把域模型的繼承關係對映到資料庫中,Hibernate提供了以下三種對繼承關係對映的方法:

  • 每個子類一張表
  • 一張表儲存繼承體系中所有類的資訊(該表由繼承體系中所有類的屬性的並集所對映的欄位組成)
  • 公共資訊放在父類表中,獨有資訊放在子類表中,每個子類對應一張表,並且子類表通過外來鍵與父類表關聯

以Person類和Person的兩個子類Student、Teacher為例:

Person類:

  1. publicclass Person {  
  2.     private Long id;  
  3.     private String name;  
  4.     //省略set、get方法
  5. }  

Student類:
  1. publicclass Student extends Person {  
  2.     private String idCard;  
  3.     //省略set、get方法
  4. }  

Teacher類:
  1. publicclass Teacher extends Person {  
  2.     private Integer salary;  
  3.     //省略set、get方法
  4. }  

1.每個子類一張表:

(1)儲存子類物件時,不必為Person寫對映檔案,只需為兩個子類Student和Teacher編寫對映檔案;但是儲存父類物件時需為父類寫對映檔案。

(2)子類繼承於父類的屬性id和name會對映到子類表中,兩個子類表沒有任何參照關係。

具體配置如下:

Student.hbm.xml:

  1. <hibernate-mapping>
  2.     <classname="bean.Student"table="students">
  3.         <!--id和name屬性是Student繼承於父類Person的屬性而不是Student獨有的屬性  -->
  4.         <idname="id"column="id"type="long">
  5.             <generatorclass="increment">
  6.             </generator>
  7.         </
    id>
  8.         <propertyname="name"column="student_name"type="string"></property>
  9.         <propertyname="idCard"column="idCard"type="string"></property>
  10.     </class>
  11. </hibernate-mapping>

Teacher.hbm.xml:
  1. <hibernate-mapping>
  2.     <classname="bean.Teacher"table="teachers">
  3.         <idname="id"column="id"type="long">
  4.             <generatorclass="increment"></generator>
  5.         </id>
  6.         <propertyname="name"column="teacher_name"type="string"></property>
  7.         <!-- salary是Teacher類獨有的屬性 -->
  8.         <propertyname="salary"column="salary"type="integer"></property>
  9.     </class>
  10. </hibernate-mapping>
將兩個對映檔案加入到主配置檔案中:
  1. <mappingresource="Student.hbm.xml"/>
  2. <mappingresource="Teacher.hbm.xml"/>
兩表的結構為:

2.用一張表儲存繼承體系中所有類的資訊

(1)子類的資訊都儲存在這一張表中,表中的欄位由所有父類子類的屬性的集合對映而成。

(2)表中需要一個欄位來標識“這一條記錄究竟是屬於哪一個子類”

(3)對於某子類沒有的屬性欄位,其子類物件對應的記錄的該欄位值被填為NULL。

(4)對於這種方式,不需要為子類配置對映檔案,只需為父類配置對映檔案,因為所有的繼承體系的資訊都放置在這一張表中。

具體配置如下:

Person.hbm.xml:

  1. <classname="bean.Person"table="persons"discriminator-value="Person">
  2.         <idname="id"column="id"type="long">
  3.             <generatorclass="increment">
  4.             </generator>
  5.         </id>
  6.         <!-- 注意元素的順序:discriminator要在property的上面 -->
  7.         <!-- discriminator指定判別類的型別的那一個欄位的欄位名稱及資料型別 -->
  8.         <discriminatorcolumn="Type"type="string"></discriminator>
  9.         <propertyname="name"column="name"type="string"></property>
  10.         <!-- subclass配置Person的子類,其子元素<property>對映子類的屬性,所以不需要為子類單獨配置對映檔案  
  11.              discriminator-value指定用於分辨子類的Type欄位值 -->
  12.         <subclassname="bean.Student"discriminator-value="Student">
  13.             <propertyname="idCard"column="idCard"type="string"></property>
  14.         </subclass>
  15.         <subclassname="bean.Teacher"discriminator-value="Teacher">
  16.             <propertyname="salary"column="salary"type="integer"></property>
  17.         </subclass>
  18.     </class>
  19. </hibernate-mapping>
(將Person.hbm.xml加入到主配置檔案中,並將Student.hbm.xml和Teacher.hbm.xml從主配置檔案中移除)

另外要注意元素的配置順序,如:dsicriminator配置在property下面時就會出錯。

persons表的結構如下:

儲存物件:

  1. Teacher teacher=new Teacher();  
  2. teacher.setName("teacher");  
  3. teacher.setSalary(10000);  
  4. Student student=new Student();  
  5. student.setIdCard("10020711");  
  6. student.setName("student");  
  7. Person person=new Person();  
  8. person.setName("person");  
  9. session.save(person);  
  10. session.save(teacher);  
  11. session.save(student);  
輸出的SQL語句為:
  1. Hibernate: insertinto persons (name, Type, id) values (?, 'Person', ?)  
  2. Hibernate: insertinto persons (name, salary, Type, id) values (?, ?, 'Teacher', ?)  
  3. Hibernate: insertinto persons (name, idCard, Type, id) values (?, ?, 'Student', ?)  
(可以看出Type欄位的值已經確定)

persons表的內容為:

(可以看出某類沒有的欄位屬性值就會被填為NULL,Type欄位用於分辨“該記錄對應哪個類?“”)

3.公共資訊放在父類表中,獨有資訊放在子類表中,每個子類對應一張表,並且子類表通過外來鍵與父類表關聯

(1)與第二種方式相同,只需父類的配置檔案Person.hbm.xml

(2)將公共資訊即父類中的屬性儲存在父類表中,將子類的獨有的屬性存放在子類表中,並且子類表中的主鍵參照父類表的主鍵id以便關聯父子表獲取子類物件中父類屬性的值。

(3)與第二種方式配置Person.hbm.xml不同,該方式使用了<joined-subclass元素,如下:

Person.hbm.xml:

  1. <classname="bean.Person"table="persons">
  2.     <idname="id"column="id"type="long">
  3.         <generatorclass="increment"></generator>
  4.     </id>
  5.     <propertyname="name"column="name"type="string"></property>
  6.     <joined-subclassname="bean.Student"table="students">
  7.         <!-- key元素指定了子類表中的外來鍵(參照父類表的主鍵id),同時這也是子類表的主鍵 -->
  8.         <keycolumn="person_id"></key>
  9.         <propertyname="idCard"column="idCard"type="string"></property>
  10.     </joined-subclass>
  11.     <joined-subclassname="bean.Teacher"table="teachers">
  12.         <keycolumn="person_id"></key>
  13.         <propertyname="salary"column="salary"type="integer"></property>
  14.     </joined-subclass>
  15. </class>
生成的這三個表的結構及參照關係為:

儲存物件後的表的內容為:


二、多型查詢:

多型查詢指的是在檢索當前類時,Hibernate會同時檢索當前類的子類,檢索結果是當前類及其子類的所有例項。

有網友提出:”get支援多型查詢;load只有在lazy=false,才支援多型查詢;HQL支援多型查詢

以及提出禁用多型查詢的方式:將<class>的屬性polymorphism改為"explicit"(預設為implicit)

下面我針對上述的三種繼承對映的方式編碼實分別驗證:

儲存物件:

  1. Teacher teacher=new Teacher();  
  2. teacher.setName("teacher");  
  3. teacher.setSalary(10000);  
  4. Student student=new Student();  
  5. student.setIdCard("10020711");  
  6. student.setName("student");  
  7. Person person=new Person();  
  8. person.setName("person");  
  9. session.save(person);  
  10. session.save(teacher);  
  11. session.save(student);  

(1)第一種繼承對映方式,即子類各一張表,父類也單獨存取在一張表中,且各表之間無任何參照關係,所以當使用get或load查詢父類的例項時當然會到父類表中查詢了,由於get、load的引數需要id且它們都只返回一個Object,所以這種情況下就不支援多型查詢了。

——HQL:支援多型查旬,將父類對映檔案(即Person.hbm.xml)的<class>的屬性polymorphism改為"explicit"可以“禁用多型查詢”。

測試程式碼:

  1.  Query query=session.createQuery("from Person");  
  2.  List list = query.list();  
  3. for (int i = 0; i < list.size(); i++) {  
  4.     Object object = list.get(i);  
  5.     if (object instanceof Student) {  
  6.         Student s = (Student) object;  
  7.         System.out.println(s.getName() + ":" + s.getIdCard());  
  8.     } elseif (object instanceof Teacher) {  
  9.         Teacher t = (Teacher) object;  
  10.         System.out.println(t.getName() + ":" + t.getSalary());  
  11.     }else System.out.println("Person");  
  12. }  

(2)第二種方式的繼承對映:所有繼承體系的資訊都儲存在一張表中:

——get方法:支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

——load方法:將<class>中的lazy屬性值改為false後支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

——HQL:支援多型查旬,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

測試程式碼:

  1.              Query query=session.createQuery("from Person");//測試HQL
  2.              List list = query.list();  
  3.             for (int i = 0; i < list.size(); i++) {  
  4.                 Object object = list.get(i);  
  5.                 if (object instanceof Student) {  
  6.                     Student s = (Student) object;  
  7.                     System.out.println(s.getName() + ":" + s.getIdCard());  
  8.                 } elseif (object instanceof Teacher) {  
  9.                     Teacher t = (Teacher) object;  
  10.                     System.out.println(t.getName() + ":" + t.getSalary());  
  11.                 }else System.out.println("Person");  
  12.             }  
  13. //          Person p=(Person)session.get(Person.class, 2L);//測試get
  14. //          Person p=(Person)session.load(Person.class, 2L);//測試load
  15. //          System.out.println(p.getName());
  16. //          if(p instanceof Teacher)
  17. //          {
  18. //              Teacher t=(Teacher)p;
  19. //              System.out.println(t.getSalary());
  20. //          }else if(p instanceof Student)
  21. //          {
  22. //              Student s=(Student)p;
  23. //              System.out.println(s.getIdCard());
  24. //          }else
  25. //              System.out.println("person");


(3)第三種方式的繼承對映:公共資訊放在父類表中,子類獨有的屬性放在子類表中,且通過外來鍵與父類表關聯

——get方法:支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

——load方法:將<class>中的lazy屬性值改為false後支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

——HQL:支援多型查旬,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。

測試程式碼:

  1.              Query query=session.createQuery("from Person");//測試HQL
  2.              List list = query.list();  
  3.             for (int i = 0; i < list.size(); i++) {  
  4.                 Object object = list.get(i);  
  5.                 if (object instanceof Student) {  
  6.                     Student s = (Student) object;  
  7.                     System.out.println(s.getName() + ":" + s.getIdCard());  
  8.                 } elseif (object instanceof Teacher) {  
  9.                     Teacher t = (Teacher) object;  
  10.                     System.out.println(t.getName() + ":" + t.getSalary());  
  11.                 }else System.out.println("Person");  
  12.             }  
  13. //          Person p=(Person)session.get(Person.class, 2L);//測試get
  14. //          Person p=(Person)session.load(Person.class, 2L);//測試load
  15. //          System.out.println(p.getName());
  16. //          if(p instanceof Teacher)
  17. //          {
  18. //              Teacher t=(Teacher)p;
  19. //              System.out.println(t.getSalary());
  20. //          }else if(p instanceof Student)
  21. //          {
  22. //              Student s=(Student)p;
  23. //              System.out.println(s.getIdCard());
  24. //          }else
  25. //              System.out.println("person");

(如驗證的不正確,請指正)

通過HQL查詢表中所有的實體物件

    * HQL語句:session.createQuery("from java.lang.Object").list();

    * 因為所有物件都是繼承Object類

轉載請註明出處:http://blog.csdn.net/jialinqiang/article/details