Hibernate 实体映射的继承关系

实体与实体之间有时候是有一定的关系的,他们中间的大部分字段是相同的,而相同的代码需要我们写两遍,这是我们所不能忍受的,所以就有了继承,抽象出一个公共的父类,将相同的部分放在其中。而我们今天要说的Hibernate 的映射策略,是针对实体与数据表的关系说的。

问题描述

两个系统,他们公用同一个后台。每个系统都有一个菜单类,现在两个菜单类实体的属性基本相同,需要用实体继承来实现。

策略一:单表映射(single)

描述

单表映射,顾名思义,只有一张表。它表示父类和继承它的子类共同使用一张数据表,然后另设一个字段,用来区分不同的实体。

用法演示

父类:

在父类中使用注解@Inheritance来声明这是继承的父类,然后在参数中定义继承的策略:单表继承SINGLE_TABLE

使用注解@DiscriminatorColumn来生成一个新的字段,用来区别两个不同的子类。参数为字段的名字。

1
2
3
4
5
6
7
@Entity
@Table(name = "web_app_menu")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DB_TYPE")
public abstract class BaseWebAppMenu {
// 省略具体内容
}

子类:

在子类中,只需要通过注解@DiscriminatorValue来设置自己对应的用来区分的字段的名字。然后继承父类就行了。

1
2
3
4
@Entity
@DiscriminatorValue("web_app_menu")
public class WebAppMenu extends BaseWebAppMenu {
}
1
2
3
4
@Entity
@DiscriminatorValue("automation")
public class AutomationWebAppMenu extends BaseWebAppMenu {
}

最后会生成类似下面的表结构:

策略二:Joined策略

描述

这种策略从字面上来看:加入,加盟。它的意思就是父类和子类都各自生成数据表,但是,父类的数据表中只有公共的字段,子类的数据表中只有扩展的字段。你生成你的字段,我生成我的字段,然后我们合起来就是完整的表了。

用法演示

父类:

父类中只需要通过@Inheritance注解来声明这是父类,然后策略为:JOINED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class BaseWebAppMenu {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String pinyin;
private String description;
private boolean isAbstract = false;
private Boolean status = true;
private String remark;
private Boolean isShow = true;
// 省略相关函数
}

子类:

由于需要对应不同的数据表,而又没有重新定义主键,所以需要使用@PrimaryKeyJoinColumn注解来声明一下主键。

1
2
3
4
5
@Entity
@PrimaryKeyJoinColumn(name = "web_app_menu_id")
public class WebAppMenu extends BaseWebAppMenu {
public String head;
}
1
2
3
4
5
@Entity
@PrimaryKeyJoinColumn("automation_id")
public class AutomationWebAppMenu extends BaseWebAppMenu {
public String foot;
}

策略三:Table per Class策略

描述

这个策略也很好理解:一个类一张表嘛。而这个策略的特点是,每个子类单独创建数据表,并且拥有父类中的所有属性,数据也不会共享。

用法演示

父类:

父类的改动很小,只需要将上面的策略换成:TABLE_PER_CLASS就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseWebAppMenu {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String pinyin;
private String description;
private boolean isAbstract = false;
private Boolean status = true;
private String remark;
private Boolean isShow = true;
// 省略相关函数
}

子类:

由于子类生成的表中会包含所有字段,所以子类什么也不需要做。

1
2
3
4
@Entity
public class WebAppMenu extends BaseWebAppMenu {
public String head;
}
1
2
3
4
@Entity
public class AutomationWebAppMenu extends BaseWebAppMenu {
public String foot;
}

总结

Hibernate为我们解决了实体继承的问题,让我们不必再纠结实体与数据表的关系。但是这三种映射策略并不是什么时候都能使用的,还是需要看实际的项目中是什么样的情况,针对不同的情况使用不同的策略,才能发挥他最强大的功能。

坚持原创技术分享,您的支持将鼓励我继续创作!