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
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。