![3e33d59a4ded00d5dfb115809c4e98f1.png](https://img-blog.csdnimg.cn/img_convert/3e33d59a4ded00d5dfb115809c4e98f1.png)
我们知道在面向对象的Java语言的世界里,对象的组织是有继承和组合两种形式的。当面向对象的概念映射到关系型数据库时,有着这样的对应,类对应了表,对象对应了表行记录,对象属性对应了表列,对象的组合关系对应的是表之间的连接。但是关系型数据库并不能直接表示“继承”这种关系。
想要对象继承在关系型数据库中表达出来,不难看有三种表达方法。
- 父类单表模式,父类整棵对象继承树的所有属性共用一个表,表会有父类重复冗余的字段和子类冗余的空值字段。
- 子类单表模式,每个可实例化的子类的继承链上的所有属性共用一个表,表会有父类重复冗余的字段。
- 连表模式,子类和父类都单独使用各自的表,表中没有冗余字段。
前两种不需要连表,如何选择,本质还是空间vs时间的问题。
根据上面关系型数据库中客观存在的三种表达方法,Hibernate给我们提供了五种模式来映射对象继承关系。我们一起来看看下面这个简单的继承模型,并且观察发生多态查询,也就是调用父类的DAO查询时,它是怎样组织SQL语句的。
![f3ed10e04a594c583c3bcb75e795a30b.png](https://img-blog.csdnimg.cn/img_convert/f3ed10e04a594c583c3bcb75e795a30b.png)
- MappedSuperclas —— 简化的子类单表模式
@MappedSuperclass
public abstract class Person{
String name;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
@Entity
public class Worker extends Person{
String typeOfWork;
}
@Entity
public class Manager extends Person{
Long numberOfManageWorker;
}
这种方式继承则无法进行多态查询,只能查具体子类。这种方式父类要做的事情最少,只需要一个@MappedSuperclas注解,id也可以放在父类,但要注意父类并不是实体。
select id,type_of_work from worker;
select ID,number_of_manage_worker from manager;
- TABLE_PER_CLASS —— 子类单表模式
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Person{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
@Entity
public class Worker extends Person{
String typeOfWork;
}
@Entity
public class Manager extends Person{
Long numberOfManageWorker;
}
和前一种方式对比父类是实体,可以进行多态查询,查询父类时会union多个子类查询结果作为子查询。classz_用来做类型标记。
select person.id,
person.name,
person.number_of_manage_worker,
person.type_of_work,
person.clazz_
from (
select id, name, number_of_manage_worker, null as type_of_work, 1 as clazz_ from manager
union all
select id, name, null as number_of_manage_worker, type_of_work, 2 as clazz_ from worker
) person
- SINGLE_TABLE —— 父类单表模式
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn("PERSON_TYPE")//可不写,默认是DTYPE
public abstract class Person{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
@Entity
@DiscriminatorValue("WORKER")
public class Worker extends Person{
String typeOfWork;
}
@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Person{
Long numberOfManageWorker;
}
这种方式所有子类和父类的字段都放在一个表里,不同的子类实例对应的数据库行记录用默认用DTYPE列区分。
select person.id,
person.name,
person.number_of_manage_worker,
person.type_of_work,
person.person_type
from person;
- JOINED —— 连表模式
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Person{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
@Entity
@PrimaryKeyJoinColumn("WORKER_ID")//可以不写指定连接到父类表的列,默认用id列
public class Worker extends Person{
String typeOfWork;
}
@Entity
public class Manager extends Person{
Long numberOfManageWorker;
}
这种方式把父类和子类分别放到各自的表里面,多态查询时会发生连表。如果不用PrimaryKeyJoinColumn则默认使用id主键列。
select person.id,
person.name,
manager.number_of_manage_worker,
worker.type_of_work,
case
when manager.manager_id is not null then 1
when worker.id is not null then 2
when person.id is not null then 0 end as clazz_
from person
left outer join manager on person.id = manager.id
left outer join worker on person.id = worker.worker_id
- SecondaryTable —— 混合模式(单表+连表)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn("PERSON_TYPE")
public abstract class Person{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
}
@Entity
@DiscriminatorValue("WORKER")
@SecondaryTable(name = "WORKER",pkJoinColumns = @PrimaryKeyJoinColumn(name = "WORKER_ID"))
public class Worker extends Person{
@Column(table = "WORKER")
String typeOfWork;
}
@Entity
@DiscriminatorValue("MANAGER")
@SecondaryTable(name = "MANAGER",pkJoinColumns = @PrimaryKeyJoinColumn(name = "MANAGER_ID"))
public class Manager extends Person{
@Column(table = "MANAGER")
Long numberOfManageWorker;
}
//todo