文档章节

JPA实体继承实体的映射策略

志强朱
 志强朱
发布于 2016/05/08 17:46
字数 1675
阅读 7
收藏 0

注:这里所说的实体指的是@Entity注解的类

继承映射使用@Inheritance来注解,它的strategy属性的取值由枚举InheritanceType来定义(包括SINGLE_TABLE、TABLE_PER_CLASS、JOINED,分别对应三种继承策略)。@Inheritance注解只能作用于继承结构的超类上。如果不指定继承策略,默认使用SINGLE_TABLE。


JPA提供了三种继承映射策略:
1、 一个类继承结构一个表的策略。这是继承映射的默认策略。即如果实体类B继承实体类A,实体类C也继承自实体A,那么只会映射成一个表,这个表中包括了实体类A、B、C中所有的字段,JPA使用一个叫做“discriminator列”来区分某一行数据是应该映射成哪个实体。注解为:@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
2、 联合子类策略。这种情况下子类的字段被映射到各自的表中,这些字段包括父类中的字段,并执行一个join操作来实例化子类。注解为:@Inheritance(strategy = InheritanceType.JOINED)
3、 每个具体的类一个表的策略。注解为:@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

一、一个类继承结构一个表的策略
这种策略中,一个继承结构中的所有类都被映射到一个表中。该表中有一列被当作“discriminator列”,即使用该列来识别某行数据属于某个指定的子类实例。
这种映射策略对实体和涉及类继承结构的查询的多态系统提供了很好的支持。但缺点是要求与子类的指定状态对应的列可以为空。

实例如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.mikan;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.DiscriminatorColumn;  
  7. import javax.persistence.Entity;  
  8. import javax.persistence.GeneratedValue;  
  9. import javax.persistence.GenerationType;  
  10. import javax.persistence.Id;  
  11. import javax.persistence.Inheritance;  
  12. import javax.persistence.InheritanceType;  
  13. import javax.persistence.Table;  
  14.   
  15. @Entity  
  16. @Table(name = "EMP")  
  17. @Inheritance(strategy = InheritanceType.SINGLE_TABLE)  
  18. @DiscriminatorColumn(name = "emp_type")  
  19. public class Employee implements Serializable {  
  20.   
  21.     private static final long serialVersionUID = -7674269980281525370L;  
  22.       
  23.     @Id  
  24.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  25.     protected Integer empId;  
  26.       
  27.     @Column  
  28.     protected String name;  
  29.   
  30.     // getter/setter方法  
  31.       
  32. }  
  33.   
  34. package com.mikan;  
  35.   
  36. import javax.persistence.Column;  
  37. import javax.persistence.DiscriminatorValue;  
  38. import javax.persistence.Entity;  
  39.   
  40. @Entity  
  41. @DiscriminatorValue("FT")  
  42. public class FullTimeEmployee extends Employee {  
  43.   
  44.     private static final long serialVersionUID = 9115429216382631425L;  
  45.   
  46.     @Column  
  47.     private Double salary;  
  48.   
  49.     // getter/setter方法  
  50.       
  51. }  
  52.   
  53. package com.mikan;  
  54.   
  55. import javax.persistence.Column;  
  56. import javax.persistence.DiscriminatorValue;  
  57. import javax.persistence.Entity;  
  58.   
  59. @Entity  
  60. @DiscriminatorValue("PT")  
  61. public class PartTimeEmployee extends Employee {  
  62.   
  63.     private static final long serialVersionUID = -6122347374515830424L;  
  64.   
  65.     @Column(name = "hourly_wage")  
  66.     private Float hourlyWage;  
  67.   
  68.     // getter/setter方法  
  69.   
  70. }  
其中,超类的@DiscriminatorColumn注解可以省略,默认的“discriminator列”名为DTYPE,默认类型为STRING。
@DiscriminatorColumn注解只能使用在超类上,不能使用到具体的子类上。discriminatorType的值由DiscriminatorType枚举定义,包括STRING、CHAR、INTEGER。如果指定了discriminatorType,那么子类上@ DiscriminatorValue注解的值也应该是相应类型。
@DiscriminatorValue注解只能使用在具体的实体子类上。同样@DiscriminatorValue注解也可以省略,默认使用类名作为值。
上面的例子中,只会生成一个表,包含了字段emp_type、empId、name、salary、hourly_wage。当保存FullTimeEmployee时,emp_type的值为“FT”, 当保存PartTimeEmployee时,emp_type的值为“PT”。

二、联合子类策略
这种策略超类会被映射成一个单独的表,每个子类也会映射成一个单独的表。子类对应的表中只包括自身属性对应的字段,默认情况下使用主键作为超类对应的表的外键。
这种策略对于实体间的多态关系提供了很好的支持。但缺点是实例化子类实例时需要一个或多个表的关联操作。在深层次的继承结构中,这会导致性能很低。实例如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.mikan;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.Entity;  
  7. import javax.persistence.GeneratedValue;  
  8. import javax.persistence.GenerationType;  
  9. import javax.persistence.Id;  
  10. import javax.persistence.Inheritance;  
  11. import javax.persistence.InheritanceType;  
  12. import javax.persistence.Table;  
  13.   
  14. @Entity  
  15. @Table(name = "EMP")  
  16. @Inheritance(strategy = InheritanceType.JOINED)  
  17. public class Employee implements Serializable {  
  18.   
  19.     private static final long serialVersionUID = -7674269980281525370L;  
  20.       
  21.     @Id  
  22.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  23.     protected Integer empId;  
  24.       
  25.     @Column  
  26.     protected String name;  
  27.   
  28.     // getter/setter方法  
  29.       
  30. }  
  31.   
  32. package com.mikan;  
  33.   
  34. import javax.persistence.Column;  
  35. import javax.persistence.DiscriminatorValue;  
  36. import javax.persistence.Entity;  
  37. import javax.persistence.Table;  
  38.   
  39. @Entity  
  40. @Table(name = "FT_EMP")  
  41. public class FullTimeEmployee extends Employee {  
  42.   
  43.     private static final long serialVersionUID = 9115429216382631425L;  
  44.   
  45.     @Column  
  46.     private Double salary;  
  47.   
  48.     // getter/setter方法  
  49.       
  50. }  
  51.   
  52. package com.mikan;  
  53.   
  54. import javax.persistence.Column;  
  55. import javax.persistence.DiscriminatorValue;  
  56. import javax.persistence.Entity;  
  57. import javax.persistence.Table;  
  58.   
  59. @Entity  
  60. @Table(name = "PT_EMP")  
  61. public class PartTimeEmployee extends Employee {  
  62.   
  63.     private static final long serialVersionUID = -6122347374515830424L;  
  64.   
  65.     @Column(name = "hourly_wage")  
  66.     private Float hourlyWage;  
  67.   
  68.     // getter/setter方法  
  69.   
  70. }  
这会映射成三个具体的表,分别是,Employee对应EMP表,字段包括empId、name;FullTimeEmployee对应FT_EMP表,字段包括empId、salary;PartTimeEmployee对应PT_EMP表,字段包括empId、hourly_wage。其中,表FT_EMP和PT_EMP中的empId作为表EMP的外键,同是它也是主键。默认情况下,使用超类的主键作为子类的主键和外键。当然,可以通过@PrimaryKeyJoinColumn注解来自己指定外键的名称,如FullTimeEmployee使用@PrimaryKeyJoinColumn(name = "FT_EMPID")注解,那么该子类实体的字段为FT_EMPID、name,FT_EMPID作为表FT_TIME的主键,同时它也是EMP表的外键。
子类实体每保存一条数据,会在EMP表中插入一条记录,如FT_EMP表插入一条数据,会先在EMP表中插入name,并生成empId,再在FT_EMP表中插入empId和salary。PT_EMP同理。
不管超类是抽象类还是具体类,都会生成对应的表。

三、每个具体的类一个表的策略
这种映射策略每个类都会映射成一个单独的表,类的所有属性,包括继承的属性都会映射成表的列。
这种映射策略的缺点是:对多态关系的支持有限,当查询涉及到类继承结构时通常需要发起SQL UNION查询。实例如下:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.mikan;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.Entity;  
  7. import javax.persistence.GeneratedValue;  
  8. import javax.persistence.GenerationType;  
  9. import javax.persistence.Id;  
  10. import javax.persistence.Inheritance;  
  11. import javax.persistence.InheritanceType;  
  12. import javax.persistence.Table;  
  13.   
  14. @Entity  
  15. @Table(name = "EMP")  
  16. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)  
  17. public class Employee implements Serializable {  
  18.   
  19.     private static final long serialVersionUID = -7674269980281525370L;  
  20.       
  21.     @Id  
  22.     @GeneratedValue(strategy = GenerationType.TABLE)  
  23.     protected Integer empId;  
  24.       
  25.     @Column  
  26.     protected String name;  
  27.   
  28.     // getter/setter方法  
  29.       
  30. }  
  31.   
  32. package com.mikan;  
  33.   
  34. import javax.persistence.Column;  
  35. import javax.persistence.DiscriminatorValue;  
  36. import javax.persistence.Entity;  
  37. import javax.persistence.Table;  
  38.   
  39. @Entity  
  40. @Table(name = "FT_EMP")  
  41. public class FullTimeEmployee extends Employee {  
  42.   
  43.     private static final long serialVersionUID = 9115429216382631425L;  
  44.   
  45.     @Column  
  46.     private Double salary;  
  47.   
  48.     // getter/setter方法  
  49.       
  50. }  
  51.   
  52. package com.mikan;  
  53.   
  54. import javax.persistence.Column;  
  55. import javax.persistence.DiscriminatorValue;  
  56. import javax.persistence.Entity;  
  57. import javax.persistence.Table;  
  58.   
  59. @Entity  
  60. @Table(name = "PT_EMP")  
  61. public class PartTimeEmployee extends Employee {  
  62.   
  63.     private static final long serialVersionUID = -6122347374515830424L;  
  64.   
  65.     @Column(name = "hourly_wage")  
  66.     private Float hourlyWage;  
  67.   
  68.     // getter/setter方法  
  69.   
  70. }  
这会映射成三个具体的表,分别是,Employee对应EMP表,字段包括empId、name;FullTimeEmployee对应FT_EMP表,字段包括empId、salary;PartTimeEmployee对应PT_EMP表,字段包括empId、hourly_wage。其中,表FT_EMP和PT_EMP中的empId和EMP表的empId没有任何关系。子类实体每保存一条数据,EMP表中不会插入记录。
而且主键的生成策略不能使用GenerationType.AUTO或GenerationType.IDENTITY,否则会出现异常:
org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: com.mikan.PartTimeEmployee

因为TABLE_PER_CLASS策略每个表都是单独的,没有并且各表的主键没有任何关系,所以不能使用GenerationType.AUTO或GenerationType.IDENTITY主键生成策略,可以使用GenerationType.TABLE。

具体可参考:http://stackoverflow.com/questions/916169/cannot-use-identity-column-key-generation-with-union-subclass-table-per-clas

如果超类是抽象类,那么不会生成对应的表。如果超类是具体的类,那么会生成对应的表。

以上实例使用JPA的hibernate实现测试通过。

© 著作权归作者所有

共有 人打赏支持
志强朱
粉丝 4
博文 209
码字总数 108290
作品 0
郑州
程序员
私信 提问
spring Data JPA

什么是JPA? 全称Java Persistence API,可以通过注解或者XML描述【对象-关系表】之间的映射关系,并将实体对象持久化到数据库中。 为我们提供了: 1)ORM映射元数据:JPA支持XML和注解两种元...

狼王黄师傅
2018/10/20
0
0
OpenXava 4.5发布,Web 快速开发框架

OpenXava 4.5发布了,该版本的主要改进包括: 支持所有的JPA继承映射策略 在使用JDBC之前,列表和集合中的数据通过JPA来获取 新增了自动化业务逻辑(Automated Business Logic)库 全新的布局...

oschina
2012/07/19
1K
3
SSH与SSH2这种框架组合的历史原由

早在2001年时当时的J2EE推崇的是EJB,EJB被称为J2EE的核心,当时要学J2EE就是Servlet+EJB,在EJB里其实早已经有了AOP与实体映射这些概念了。 EJB有三种形态的BEAN,SessionBean, Entity Bea...

天行健J
2012/11/20
0
0
企业级 SpringBoot 教程 (四)SpringBoot 整合JPA

JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发...

itcloud
2018/09/26
0
0
(四)企业级java springcloud b2bc商城系统开源源码二次开发-SpringBoot 整合JPA

JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发...

02/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring学习记录

Java类定义配置 @Configuration //标记为配置类@ComponentScan //标记为扫描当前包及子包所有标记为@Component的类@ComponentScan(basePackageClasses = {接口.class,...}) //标记为扫描当...

CHONGCHEN
今天
1
0
如何开发一款以太坊(安卓)钱包系列2 - 导入账号及账号管理

这是如何开发一款以太坊(安卓)钱包系列第2篇,如何导入账号。有时用户可能已经有一个账号,这篇文章接来介绍下,如何实现导入用户已经存在的账号。 导入账号预备知识 从用户需求上来讲,导...

Tiny熊
今天
3
0
intellJ IDEA搭建java+selenium自动化环境(maven,selenium,testng)

1.安装jdk1.8; 2.安装intellJ; 3.安装maven; 3.1 如果是单前用户,配置用户环境变量即可,如果是多用户,则需配置系统环境变量,变量名为MAVEN_HOME,赋值D:\Application\maven,往path中...

不最醉不龟归
今天
4
0
聊聊ShenandoahGC的Brooks Pointers

序 本文主要研究一下ShenandoahGC的Brooks Pointers Shenandoah Shenandoah面向low-pause-time的垃圾收集器,它的GC cycle主要有 Snapshot-at-the-beginning concurrent mark包括Init Mark(P......

go4it
昨天
4
0
Makefile通用编写规则

#简单实用的Makefile模板: objs := a.o b.o test:$(objs) gcc -o test $^ # .a.o.d .b.o.d dep_files := $(foreach f,$(objs),.$(f).d) dep_files := $(wildcard $(dep_files)) ifneq ($(d......

shzwork
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部