一、继承映射:
关系数据库的表之间不存在继承关系,为了把域模型的继承关系映射到数据库中,Hibernate提供了以下三种对继承关系映射的方法:
-
每个子类一张表
-
一张表存储继承体系中所有类的信息(该表由继承体系中所有类的属性的并集所映射的字段组成)
-
公共信息放在父类表中,独有信息放在子类表中,每个子类对应一张表,并且子类表通过外键与父类表关联
以Person类和Person的两个子类Student、Teacher为例:
Person类:
- public class Person {
- private Long id;
- private String name;
- //省略set、get方法
- }
Student类:
- public class Student extends Person {
- private String idCard;
- //省略set、get方法
- }
Teacher类:
- public class Teacher extends Person {
- private Integer salary;
- //省略set、get方法
- }
1.每个子类一张表:
(1)保存子类对象时,不必为Person写映射文件,只需为两个子类Student和Teacher编写映射文件;但是保存父类对象时需为父类写映射文件。
(2)子类继承于父类的属性id和name会映射到子类表中,两个子类表没有任何参照关系。
具体配置如下:
Student.hbm.xml:
- <hibernate-mapping>
- <class name="bean.Student" table="students" >
- <!--id和name属性是Student继承于父类Person的属性而不是Student独有的属性 -->
- <id name="id" column="id" type="long">
- <generator class="increment">
- </generator>
- </id>
- <property name="name" column="student_name" type="string"></property>
- <property name="idCard" column="idCard" type="string"></property>
- </class>
- </hibernate-mapping>
Teacher.hbm.xml:
- <hibernate-mapping>
- <class name="bean.Teacher" table="teachers" >
- <id name="id" column="id" type="long">
- <generator class="increment"></generator>
- </id>
- <property name="name" column="teacher_name" type="string"></property>
- <!-- salary是Teacher类独有的属性 -->
- <property name="salary" column="salary" type="integer"></property>
- </class>
- </hibernate-mapping>
- <mapping resource="Student.hbm.xml"/>
- <mapping resource="Teacher.hbm.xml"/>
2.用一张表存储继承体系中所有类的信息
(1)子类的信息都存储在这一张表中,表中的字段由所有父类子类的属性的集合映射而成。
(2)表中需要一个字段来标识“这一条记录究竟是属于哪一个子类”
(3)对于某子类没有的属性字段,其子类对象对应的记录的该字段值被填为NULL。
(4)对于这种方式,不需要为子类配置映射文件,只需为父类配置映射文件,因为所有的继承体系的信息都放置在这一张表中。
具体配置如下:
Person.hbm.xml:
- <class name="bean.Person" table="persons" discriminator-value="Person">
- <id name="id" column="id" type="long">
- <generator class="increment">
- </generator>
- </id>
- <!-- 注意元素的顺序:discriminator要在property的上面 -->
- <!-- discriminator指定判别类的类型的那一个字段的字段名称及数据类型 -->
- <discriminator column="Type" type="string"></discriminator>
- <property name="name" column="name" type="string"></property>
- <!-- subclass配置Person的子类,其子元素<property>映射子类的属性,所以不需要为子类单独配置映射文件
- discriminator-value指定用于分辨子类的Type字段值 -->
- <subclass name="bean.Student" discriminator-value="Student">
- <property name="idCard" column="idCard" type="string"></property>
- </subclass>
- <subclass name="bean.Teacher" discriminator-value="Teacher">
- <property name="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: insert into persons (name, Type, id) values (?, 'Person', ?)
- Hibernate: insert into persons (name, salary, Type, id) values (?, ?, 'Teacher', ?)
- Hibernate: insert into 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:
- <class name="bean.Person" table="persons">
- <id name="id" column="id" type="long">
- <generator class="increment"></generator>
- </id>
- <property name="name" column="name" type="string"></property>
- <joined-subclass name="bean.Student" table="students">
- <!-- key元素指定了子类表中的外键(参照父类表的主键id),同时这也是子类表的主键 -->
- <key column="person_id"></key>
- <property name="idCard" column="idCard" type="string"></property>
- </joined-subclass>
- <joined-subclass name="bean.Teacher" table="teachers">
- <key column="person_id"></key>
- <property name="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());
- } else if (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());
- } else if (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());
- } else if (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类