Hibernate繼承對映與多型查詢
一、繼承對映:
關係資料庫的表之間不存在繼承關係,為了把域模型的繼承關係對映到資料庫中,Hibernate提供了以下三種對繼承關係對映的方法:
-
每個子類一張表
-
一張表儲存繼承體系中所有類的資訊(該表由繼承體系中所有類的屬性的並集所對映的欄位組成)
-
公共資訊放在父類表中,獨有資訊放在子類表中,每個子類對應一張表,並且子類表通過外來鍵與父類表關聯
以Person類和Person的兩個子類Student、Teacher為例:
Person類:
- publicclass Person {
- private Long id;
- private String name;
- //省略set、get方法
- }
Student類:
- publicclass Student extends Person {
- private String idCard;
- //省略set、get方法
- }
Teacher類:
- publicclass Teacher extends Person {
- private Integer salary;
- //省略set、get方法
- }
1.每個子類一張表:
(1)儲存子類物件時,不必為Person寫對映檔案,只需為兩個子類Student和Teacher編寫對映檔案;但是儲存父類物件時需為父類寫對映檔案。
(2)子類繼承於父類的屬性id和name會對映到子類表中,兩個子類表沒有任何參照關係。
具體配置如下:
Student.hbm.xml:
- <hibernate-mapping>
- <classname="bean.Student"table="students">
- <!--id和name屬性是Student繼承於父類Person的屬性而不是Student獨有的屬性 -->
- <idname="id"column="id"type="long">
- <generatorclass="increment">
- </generator>
- </
- <propertyname="name"column="student_name"type="string"></property>
- <propertyname="idCard"column="idCard"type="string"></property>
- </class>
- </hibernate-mapping>
Teacher.hbm.xml:
- <hibernate-mapping>
- <classname="bean.Teacher"table="teachers">
- <idname="id"column="id"type="long">
- <generatorclass="increment"></generator>
- </id>
- <propertyname="name"column="teacher_name"type="string"></property>
- <!-- salary是Teacher類獨有的屬性 -->
- <propertyname="salary"column="salary"type="integer"></property>
- </class>
- </hibernate-mapping>
- <mappingresource="Student.hbm.xml"/>
- <mappingresource="Teacher.hbm.xml"/>
2.用一張表儲存繼承體系中所有類的資訊
(1)子類的資訊都儲存在這一張表中,表中的欄位由所有父類子類的屬性的集合對映而成。
(2)表中需要一個欄位來標識“這一條記錄究竟是屬於哪一個子類”
(3)對於某子類沒有的屬性欄位,其子類物件對應的記錄的該欄位值被填為NULL。
(4)對於這種方式,不需要為子類配置對映檔案,只需為父類配置對映檔案,因為所有的繼承體系的資訊都放置在這一張表中。
具體配置如下:
Person.hbm.xml:
- <classname="bean.Person"table="persons"discriminator-value="Person">
- <idname="id"column="id"type="long">
- <generatorclass="increment">
- </generator>
- </id>
- <!-- 注意元素的順序:discriminator要在property的上面 -->
- <!-- discriminator指定判別類的型別的那一個欄位的欄位名稱及資料型別 -->
- <discriminatorcolumn="Type"type="string"></discriminator>
- <propertyname="name"column="name"type="string"></property>
- <!-- subclass配置Person的子類,其子元素<property>對映子類的屬性,所以不需要為子類單獨配置對映檔案
- discriminator-value指定用於分辨子類的Type欄位值 -->
- <subclassname="bean.Student"discriminator-value="Student">
- <propertyname="idCard"column="idCard"type="string"></property>
- </subclass>
- <subclassname="bean.Teacher"discriminator-value="Teacher">
- <propertyname="salary"column="salary"type="integer"></property>
- </subclass>
- </class>
- </hibernate-mapping>
另外要注意元素的配置順序,如:dsicriminator配置在property下面時就會出錯。
persons表的結構如下:
儲存物件:
- Teacher teacher=new Teacher();
- teacher.setName("teacher");
- teacher.setSalary(10000);
- Student student=new Student();
- student.setIdCard("10020711");
- student.setName("student");
- Person person=new Person();
- person.setName("person");
- session.save(person);
- session.save(teacher);
- session.save(student);
- Hibernate: insertinto persons (name, Type, id) values (?, 'Person', ?)
- Hibernate: insertinto persons (name, salary, Type, id) values (?, ?, 'Teacher', ?)
- Hibernate: insertinto persons (name, idCard, Type, id) values (?, ?, 'Student', ?)
persons表的內容為:
(可以看出某類沒有的欄位屬性值就會被填為NULL,Type欄位用於分辨“該記錄對應哪個類?“”)
3.公共資訊放在父類表中,獨有資訊放在子類表中,每個子類對應一張表,並且子類表通過外來鍵與父類表關聯
(1)與第二種方式相同,只需父類的配置檔案Person.hbm.xml
(2)將公共資訊即父類中的屬性儲存在父類表中,將子類的獨有的屬性存放在子類表中,並且子類表中的主鍵參照父類表的主鍵id以便關聯父子表獲取子類物件中父類屬性的值。
(3)與第二種方式配置Person.hbm.xml不同,該方式使用了<joined-subclass元素,如下:
Person.hbm.xml:
- <classname="bean.Person"table="persons">
- <idname="id"column="id"type="long">
- <generatorclass="increment"></generator>
- </id>
- <propertyname="name"column="name"type="string"></property>
- <joined-subclassname="bean.Student"table="students">
- <!-- key元素指定了子類表中的外來鍵(參照父類表的主鍵id),同時這也是子類表的主鍵 -->
- <keycolumn="person_id"></key>
- <propertyname="idCard"column="idCard"type="string"></property>
- </joined-subclass>
- <joined-subclassname="bean.Teacher"table="teachers">
- <keycolumn="person_id"></key>
- <propertyname="salary"column="salary"type="integer"></property>
- </joined-subclass>
- </class>
儲存物件後的表的內容為:
二、多型查詢:
多型查詢指的是在檢索當前類時,Hibernate會同時檢索當前類的子類,檢索結果是當前類及其子類的所有例項。
有網友提出:”get支援多型查詢;load只有在lazy=false,才支援多型查詢;HQL支援多型查詢“
以及提出禁用多型查詢的方式:將<class>的屬性polymorphism改為"explicit"(預設為implicit)
下面我針對上述的三種繼承對映的方式編碼實分別驗證:
儲存物件:
- Teacher teacher=new Teacher();
- teacher.setName("teacher");
- teacher.setSalary(10000);
- Student student=new Student();
- student.setIdCard("10020711");
- student.setName("student");
- Person person=new Person();
- person.setName("person");
- session.save(person);
- session.save(teacher);
- session.save(student);
(1)第一種繼承對映方式,即子類各一張表,父類也單獨存取在一張表中,且各表之間無任何參照關係,所以當使用get或load查詢父類的例項時當然會到父類表中查詢了,由於get、load的引數需要id且它們都只返回一個Object,所以這種情況下就不支援多型查詢了。
——HQL:支援多型查旬,將父類對映檔案(即Person.hbm.xml)的<class>的屬性polymorphism改為"explicit"可以“禁用多型查詢”。
測試程式碼:
- Query query=session.createQuery("from Person");
- List list = query.list();
- for (int i = 0; i < list.size(); i++) {
- Object object = list.get(i);
- if (object instanceof Student) {
- Student s = (Student) object;
- System.out.println(s.getName() + ":" + s.getIdCard());
- } elseif (object instanceof Teacher) {
- Teacher t = (Teacher) object;
- System.out.println(t.getName() + ":" + t.getSalary());
- }else System.out.println("Person");
- }
(2)第二種方式的繼承對映:所有繼承體系的資訊都儲存在一張表中:
——get方法:支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
——load方法:將<class>中的lazy屬性值改為false後支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
——HQL:支援多型查旬,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
測試程式碼:
- Query query=session.createQuery("from Person");//測試HQL
- List list = query.list();
- for (int i = 0; i < list.size(); i++) {
- Object object = list.get(i);
- if (object instanceof Student) {
- Student s = (Student) object;
- System.out.println(s.getName() + ":" + s.getIdCard());
- } elseif (object instanceof Teacher) {
- Teacher t = (Teacher) object;
- System.out.println(t.getName() + ":" + t.getSalary());
- }else System.out.println("Person");
- }
- // Person p=(Person)session.get(Person.class, 2L);//測試get
- // Person p=(Person)session.load(Person.class, 2L);//測試load
- // System.out.println(p.getName());
- // if(p instanceof Teacher)
- // {
- // Teacher t=(Teacher)p;
- // System.out.println(t.getSalary());
- // }else if(p instanceof Student)
- // {
- // Student s=(Student)p;
- // System.out.println(s.getIdCard());
- // }else
- // System.out.println("person");
(3)第三種方式的繼承對映:公共資訊放在父類表中,子類獨有的屬性放在子類表中,且通過外來鍵與父類表關聯
——get方法:支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
——load方法:將<class>中的lazy屬性值改為false後支援多型查詢,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
——HQL:支援多型查旬,但是將<class>的屬性polymorphism改為"explicit"不能“禁用多型查詢”。
測試程式碼:
- Query query=session.createQuery("from Person");//測試HQL
- List list = query.list();
- for (int i = 0; i < list.size(); i++) {
- Object object = list.get(i);
- if (object instanceof Student) {
- Student s = (Student) object;
- System.out.println(s.getName() + ":" + s.getIdCard());
- } elseif (object instanceof Teacher) {
- Teacher t = (Teacher) object;
- System.out.println(t.getName() + ":" + t.getSalary());
- }else System.out.println("Person");
- }
- // Person p=(Person)session.get(Person.class, 2L);//測試get
- // Person p=(Person)session.load(Person.class, 2L);//測試load
- // System.out.println(p.getName());
- // if(p instanceof Teacher)
- // {
- // Teacher t=(Teacher)p;
- // System.out.println(t.getSalary());
- // }else if(p instanceof Student)
- // {
- // Student s=(Student)p;
- // System.out.println(s.getIdCard());
- // }else
- // System.out.println("person");
(如驗證的不正確,請指正)
通過HQL查詢表中所有的實體物件
* HQL語句:session.createQuery("from java.lang.Object").list();
* 因為所有物件都是繼承Object類
轉載請註明出處:http://blog.csdn.net/jialinqiang/article/details