Hibernate映射继承关系的三种方案


  • 背景:

在一些项目中,会采用集成的关系来定义数据库实体类,比如:人(Person)与学生(Student),学生来源与人,所以人的基本属性学生也拥有;但学生有的一些属性,人就不具有。人与学生之间很显然就拥有了继承关系 ------学生继承于人,人是父类,学生是子类。

那么,这种继承关系在hibernate是如何映射呢?

对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。hibernate的集成映射可以理解为持久化类之间的继承关系。在上边的例子中,学生集成了人,可以认为学生是一个特殊的人,如果对人进行查询,学生的实例也将被得到。

在hibernate中支持三种继承映射策略:

1)使用subclass进行映射:将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系数据模型中考虑域模型中的继承关系和多态。

2)使用jioned-subclass进行映射:对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段。

3)使用union- subclass进行映射:域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。

  • 使用subclass进行映射

新建工程Hibernate08,导入hibernate开发包及mysql驱动包。

在src下新建hibernate.cfg.xml配置文件:

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-configuration PUBLIC
 3         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 4         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 5 <hibernate-configuration>
 6     <session-factory>
 7         <property name="hibernate.connection.username">root</property>
 8         <property name="hibernate.connection.password">123456</property>
 9         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
10         <property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_01</property>
11 
12         <!-- <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 
13             <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> -->
14         <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
15 
16         <property name="hibernate.show_sql">true</property>
17 
18         <property name="hibernate.format_sql">true</property>
19 
20         <property name="hibernate.hbm2ddl.auto">update</property>
21 
22         <property name="hibernate.current_session_context_class">thread</property>
23 
24         <property name="hibernate.c3p0.max_size">500</property>
25         <property name="hibernate.c3p0.min_size">20</property>
26         <property name="hibernate.c3p0.max_statements">10</property>
27         <property name="hibernate.c3p0.timeout">2000</property>
28         <property name="hibernate.c3p0.idle_test_period">2000</property>
29         <property name="hibernate.c3p0.acquire_increment">10</property>
30 
31         <mapping resource="com/dx/hibernate06/extend/Person.hbm.xml" />
32             
33     </session-factory>
34 </hibernate-configuration>

在src下新建com.dx.hibernate06.extend包,在该包下新建Person.java:

1 package com.dx.hibernate06.extend;
 2 
 3 public class Person {
 4     private Integer id;
 5     private String name;
 6     private Integer age;
 7 
 8     public Person() {
 9 
10     }
11 
12     public Integer getId() {
13         return id;
14     }
15 
16     public void setId(Integer id) {
17         this.id = id;
18     }
19 
20     public String getName() {
21         return name;
22     }
23 
24     public void setName(String name) {
25         this.name = name;
26     }
27 
28     public Integer getAge() {
29         return age;
30     }
31 
32     public void setAge(Integer age) {
33         this.age = age;
34     }
35 }

新建Student.java(继承自Person):

1 package com.dx.hibernate06.extend;
 2 
 3 public class Student extends Person {
 4     private String className;
 5     private String schoolName;
 6 
 7     public Student() {
 8 
 9     }
10 
11     public String getClassName() {
12         return className;
13     }
14 
15     public void setClassName(String className) {
16         this.className = className;
17     }
18 
19     public String getSchoolName() {
20         return schoolName;
21     }
22 
23     public void setSchoolName(String schoolName) {
24         this.schoolName = schoolName;
25     }
26 
27 }

添加Person.hbm.xml配置文件:

1 <?xml version="1.0"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4 <!-- Generated 2017-6-8 15:16:00 by Hibernate Tools 3.5.0.Final -->
 5 <hibernate-mapping package="com.dx.hibernate06.extend">
 6     <class name="Person" table="PERSONS" discriminator-value="person">
 7         <id name="id" type="java.lang.Integer">
 8             <column name="ID" />
 9             <generator class="native" />
10         </id>
11         
12         <!-- 配置辨别者列 -->
13         <discriminator column="TYPE" type="string"></discriminator>
14         
15         <property name="name" type="java.lang.String">
16             <column name="NAME" />
17         </property>
18         <property name="age" type="java.lang.Integer">
19             <column name="AGE" />
20         </property>
21 
22         <subclass name="Student" discriminator-value="student">
23             <property name="className" column="CLASS_NAME" type="string"></property>
24             <property name="schoolName" column="SCHOOL_NAME" type="string"></property>
25         </subclass>
26     </class>
27 </hibernate-mapping>

注意:

1)在这里并没有定义student.hbm.xml配置文件,而是只需要在Person.hbm.xml配置文件中设置subclass配置项就可以;

2)除了设置subclass节点,还需要添加discriminator(辨别列)节点配置,且需要在table和subclass节点中设置discriminator- value(辨别列值)。

添加测试类TestMain.java:

1 package com.dx.hibernate06.extend;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.Transaction;
 6 import org.hibernate.boot.Metadata;
 7 import org.hibernate.boot.MetadataSources;
 8 import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl;
 9 import org.hibernate.boot.registry.StandardServiceRegistry;
10 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
11 import org.junit.After;
12 import org.junit.Before;
13 import org.junit.Test;
14 
15 public class TestMain {
16     private SessionFactory sessionFactory = null;
17     private Session session = null;
18     private Transaction transaction = null;
19 
20     @Before
21     public void init() {
22         StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure().build();
23         Metadata metadata = new MetadataSources(standardRegistry).getMetadataBuilder().applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE).build();
24 
25         sessionFactory = metadata.getSessionFactoryBuilder().build();
26         session = sessionFactory.getCurrentSession();
27         transaction = session.beginTransaction();
28     }
29 
30 
31     @After
32     public void destory() {
33         transaction.commit();
34         session.close();
35         sessionFactory.close();
36     }
37 }

测试:

在TestMain.java中添加测试函数1:

1     @Test
 2     public void testInsert() {
 3         Person person = new Person();
 4         person.setName("person1");
 5         person.setAge(27);
 6 
 7         Student student = new Student();
 8         student.setName("student1");
 9         student.setAge(22);
10         student.setClassName("class-1");
11         student.setSchoolName("浙江大学");
12 
13         session.save(person);
14         session.save(student);
15     }

测试执行sql:

1 Hibernate: 
 2     
 3     create table PERSONS (
 4        ID integer not null auto_increment,
 5         TYPE varchar(255) not null,
 6         NAME varchar(255),
 7         AGE integer,
 8         CLASS_NAME varchar(255),
 9         SCHOOL_NAME varchar(255),
10         primary key (ID)
11     ) engine=InnoDB
12 Hibernate: 
13     insert 
14     into
15         PERSONS
16         (NAME, AGE, TYPE) 
17     values
18         (?, ?, 'person')
19 Hibernate: 
20     insert 
21     into
22         PERSONS
23         (NAME, AGE, CLASS_NAME, SCHOOL_NAME, TYPE) 
24     values
25         (?, ?, ?, ?, 'student')

数据库查寻结果:

添加测试函数2:

@Test
public void testSelect() {
    List<Person> persons = session.createQuery("FROM Person").list();
    System.out.println(persons.size());

    List<Student> students = session.createQuery("FROM Student").list();
    System.out.println(students.size());
}

执行sql及结果:

1 Hibernate: 
 2     select
 3         person0_.ID as ID1_0_,
 4         person0_.NAME as NAME3_0_,
 5         person0_.AGE as AGE4_0_,
 6         person0_.CLASS_NAME as CLASS_NA5_0_,
 7         person0_.SCHOOL_NAME as SCHOOL_N6_0_,
 8         person0_.TYPE as TYPE2_0_ 
 9     from
10         PERSONS person0_
11 2
12 Hibernate: 
13     select
14         student0_.ID as ID1_0_,
15         student0_.NAME as NAME3_0_,
16         student0_.AGE as AGE4_0_,
17         student0_.CLASS_NAME as CLASS_NA5_0_,
18         student0_.SCHOOL_NAME as SCHOOL_N6_0_ 
19     from
20         PERSONS student0_ 
21     where
22         student0_.TYPE='student'
23 1
  • 使用jioned-subclass进行映射

在工程中复制包com.dx.hibernate06.extend,并命名新包名称为:com.dx.hibernate06.joined.subclass

修改Person.hbm.xml:

1 <?xml version="1.0"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4 <!-- Generated 2017-6-8 15:16:00 by Hibernate Tools 3.5.0.Final -->
 5 <hibernate-mapping package="com.dx.hibernate06.joined.subclass">
 6     <class name="Person" table="PERSONS">
 7         <id name="id" type="java.lang.Integer">
 8             <column name="ID" />
 9             <generator class="native" />
10         </id>
11 
12         <property name="name" type="java.lang.String">
13             <column name="NAME" />
14         </property>
15         <property name="age" type="java.lang.Integer">
16             <column name="AGE" />
17         </property>
18 
19         <joined-subclass name="Student" table="STUDENTS">
20             <key>
21                 <column name="STUDENT_ID"></column>
22             </key>
23             <property name="className" column="CLASS_NAME" type="string"></property>
24             <property name="schoolName" column="SCHOOL_NAME" type="string"></property>
25         </joined-subclass>
26     </class>
27 </hibernate-mapping>

注意:

1)这里不需要添加discriminator(辨别列)节点配置,也不需要在table和joined- subclass节点中设置discriminator-value(辨别列值);

2)但需要在joined-subclass节点中指定表名,及在节点内部指定key column。

修改hibernate.cfg.xml,修改mapping节点指定文件路径:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        。。。
        <mapping resource="com/dx/hibernate06/joined/subclass/Person.hbm.xml" />            
    </session-factory>
</hibernate-configuration>

测试:

在com.dx.hibernate06.joined.subclass.TestMain.java中执行测试函数testInsert()(备注:该测试函数与上边测试函数一致),执行sql及结果:

1 Hibernate: 
 2     
 3     create table PERSONS (
 4        ID integer not null auto_increment,
 5         NAME varchar(255),
 6         AGE integer,
 7         primary key (ID)
 8     ) engine=InnoDB
 9 Hibernate: 
10     
11     create table STUDENTS (
12        STUDENT_ID integer not null,
13         CLASS_NAME varchar(255),
14         SCHOOL_NAME varchar(255),
15         primary key (STUDENT_ID)
16     ) engine=InnoDB
17 Hibernate: 
18     
19     alter table STUDENTS 
20        add constraint FK3md9kn7axci4c8qrnaav8ybo 
21        foreign key (STUDENT_ID) 
22        references PERSONS (ID)
23 Hibernate: 
24     insert 
25     into
26         PERSONS
27         (NAME, AGE) 
28     values
29         (?, ?)
30 Hibernate: 
31     insert 
32     into
33         PERSONS
34         (NAME, AGE) 
35     values
36         (?, ?)
37 Hibernate: 
38     insert 
39     into
40         STUDENTS
41         (CLASS_NAME, SCHOOL_NAME, STUDENT_ID) 
42     values
43         (?, ?, ?)

数据库查寻结果:

执行testSelect()测试函数,执行结果及sql:

1 Hibernate: 
 2     select
 3         person0_.ID as ID1_0_,
 4         person0_.NAME as NAME2_0_,
 5         person0_.AGE as AGE3_0_,
 6         person0_1_.CLASS_NAME as CLASS_NA2_1_,
 7         person0_1_.SCHOOL_NAME as SCHOOL_N3_1_,
 8         case 
 9             when person0_1_.STUDENT_ID is not null then 1 
10             when person0_.ID is not null then 0 
11         end as clazz_ 
12     from
13         PERSONS person0_ 
14     left outer join
15         STUDENTS person0_1_ 
16             on person0_.ID=person0_1_.STUDENT_ID
17 2
18 Hibernate: 
19     select
20         student0_.STUDENT_ID as ID1_0_,
21         student0_1_.NAME as NAME2_0_,
22         student0_1_.AGE as AGE3_0_,
23         student0_.CLASS_NAME as CLASS_NA2_1_,
24         student0_.SCHOOL_NAME as SCHOOL_N3_1_ 
25     from
26         STUDENTS student0_ 
27     inner join
28         PERSONS student0_1_ 
29             on student0_.STUDENT_ID=student0_1_.ID
30 1
  • 使用union-subclass进行映射

在工程中复制包com.dx.hibernate06.extend,并命名新包名称为:com.dx.hibernate06.union.subclass

修改Person.hbm.xml:

1 <?xml version="1.0"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4 <!-- Generated 2017-6-8 15:16:00 by Hibernate Tools 3.5.0.Final -->
 5 <hibernate-mapping package="com.dx.hibernate06.union.subclass">
 6     <class name="Person" table="PERSONS">
 7         <id name="id" type="java.lang.Integer">
 8             <column name="ID" />
 9             <generator class="increment" />
10         </id>
11         <property name="name" type="java.lang.String">
12             <column name="NAME" />
13         </property>
14         <property name="age" type="java.lang.Integer">
15             <column name="AGE" />
16         </property>
17         <union-subclass name="Student" table="STUDENTS">
18             <property name="className" column="CLASS_NAME" type="string"></property>
19             <property name="schoolName" column="SCHOOL_NAME" type="string"></property>
20         </union-subclass>
21     </class>
22 </hibernate-mapping>

注意:

1)这里只需要指定union-subclass对应的表,及指定其独有的列;

2)Person的ID生成类型不能为identity,不能为native,这里采用的increment。

修改hibernate.cfg.xml,修改mapping节点指定文件路径:

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-configuration PUBLIC
 3         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 4         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 5 <hibernate-configuration>
 6     <session-factory>
 7         。。。
 8         <mapping resource="com/dx/hibernate06/union/subclass/Person.hbm.xml" />            
 9     </session-factory>
10 </hibernate-configuration>

测试:

在com.dx.hibernate06.union.subclass.TestMain.java中执行测试函数testInsert()(备注:该测试函数与上边测试函数一致),执行sql及结果:

1 Hibernate: 
 2     
 3     create table PERSONS (
 4        ID integer not null,
 5         NAME varchar(255),
 6         AGE integer,
 7         primary key (ID)
 8     ) engine=InnoDB
 9 Hibernate: 
10     
11     create table STUDENTS (
12        ID integer not null,
13         NAME varchar(255),
14         AGE integer,
15         CLASS_NAME varchar(255),
16         SCHOOL_NAME varchar(255),
17         primary key (ID)
18     ) engine=InnoDB
19 Hibernate: 
20     select
21         max(ids_.mx) 
22     from
23         ( select
24             max(ID) as mx 
25         from
26             STUDENTS 
27         union
28         select
29             max(ID) as mx 
30         from
31             PERSONS 
32     ) ids_
33 Hibernate: 
34     insert 
35     into
36         PERSONS
37         (NAME, AGE, ID) 
38     values
39         (?, ?, ?)
40 Hibernate: 
41     insert 
42     into
43         STUDENTS
44         (NAME, AGE, CLASS_NAME, SCHOOL_NAME, ID) 
45     values
46         (?, ?, ?, ?, ?)

数据库查寻结果:

执行testSelect()测试函数,执行结果及sql:

1 Hibernate: 
 2     select
 3         person0_.ID as ID1_0_,
 4         person0_.NAME as NAME2_0_,
 5         person0_.AGE as AGE3_0_,
 6         person0_.CLASS_NAME as CLASS_NA1_1_,
 7         person0_.SCHOOL_NAME as SCHOOL_N2_1_,
 8         person0_.clazz_ as clazz_ 
 9     from
10         ( select
11             ID,
12             NAME,
13             AGE,
14             null as CLASS_NAME,
15             null as SCHOOL_NAME,
16             0 as clazz_ 
17         from
18             PERSONS 
19         union
20         select
21             ID,
22             NAME,
23             AGE,
24             CLASS_NAME,
25             SCHOOL_NAME,
26             1 as clazz_ 
27         from
28             STUDENTS 
29     ) person0_
30 2
31 Hibernate: 
32     select
33         student0_.ID as ID1_0_,
34         student0_.NAME as NAME2_0_,
35         student0_.AGE as AGE3_0_,
36         student0_.CLASS_NAME as CLASS_NA1_1_,
37         student0_.SCHOOL_NAME as SCHOOL_N2_1_ 
38     from
39         STUDENTS student0_
40 1


原文链接:https://www.cnblogs.com/yy3b2007com/p/6962852.html