jap和java有关系吗_Java小技巧:JPA和Hibernate中的继承关系

继承是面向对象编程中的一种常见模式,但是它不容易在数据库中复制。这个Java技巧向您展示了如何使用Hibernate在JPA中为继承关系建模。

了解四种不同的ORM继承策略的优缺点,并获得选择最能满足您的应用程序需求的技巧。

我假设您已经通过JPA和Hibernate入门到Java持久性,包括如何建模实体和关系以及如何与JPA一起使用EntityManager。

7247ab5558250b9af971e0d361f30d0d.png

ORM中的继承模式

继承是一种面向对象的模式,其中一个类扩展(或专门化)另一类以借用(或继承)其某些功能。例如,假设我们有一个名为的类Car,代表所有汽车,包括品牌,型号和年份。但是现在我们要创建一些更专业的汽车类型,例如SportsCar和SportUtilityVehicle。这些汽车将具有原始Car类的所有功能,但具有一些附加功能。A SportsCar可能需要特殊的参数来指定速度。A SportsUtilityVehicle可能需要标志来确定座位和牵引能力。

JPA指定了四种定义继承关系的策略:

· 映射超类

· 每课桌

· 单桌

· 已加入

我们将研究Hibernate实施的每种策略。

继承与多态

继承的好处之一是它允许您通过子类的超类对子类执行操作,这被称为多态。举例来说,如果你有一个Car基类,有一个drive()方法,和两个子类Car:SportsCar和SportsUtilityVehicle,然后你可以自由存储SportCarS和SportsUtilityVehicle集合s中CarS和调用drive()方法-全不知道,如果你驾驶SportsCar或一个SportsUtilityVehicle。我们将回顾支持多态性的JPA继承策略。

继承策略1:映射超类

映射超类是最简单的继承策略。它允许您在基类中定义通用属性,然后在您的其他类中扩展该超类。这是一个基于我们的Car类的示例:

@MappedSuperclasspublicabstractclassCar{

@Id

@GeneratedValue

privateIntegerid;

privateStringmake;

privateStringmodel;

privateIntegeryear;

...}

本Car类注解与@MappedSuperclass诠释和定义了通用汽车的属性,如厂名,型号和年份,以及自动生成的id车。

现在让我们看看定义一个SportsCar类和SportUtilityVehicle扩展类时会发生什么Car:

@Entity@Table(name = "SPORTSCAR")publicclassSportsCarextendsCar{

privateIntegertopSpeed;

privateDoublezeroToSixty;

...}

@Entity@Table(name = "SUV")publicclassSportsUtilityVehicleextendsCar{

privateIntegertowingCapacity;

privateBooleanthirdRowSeating;

...}

映射超类策略很简单,但有局限性,因为这两个类都将映射到包含所有汽车属性的表:原始Car属性和新的专用类属性。

使用这种模式意味着Hibernate将无法对单个汽车运行多态查询。如果我们定义了一个CarDealer管理不同类型的汽车类,有没有办法维持的列表Cars中CarDealer; 相反,我们需要创建的列表SportsCar和的列表SportUtilityVehicle。

使用稍微复杂一些的继承策略,我们可能会做得更好。

继承策略2:每类表

实现继承的下一个策略称为“每类表”。就像我们对映射的超类所做的那样,此策略将所有数据存储在其专用类表中,但是它保留了专用类与其基类之间的关系。

在这种情况下,专用类SportsCar和和SportsUtilityVehicle保持不变,但是基类被视为一个实体。

@Entity@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)publicabstractclassCar{

@Id

@GeneratedValue

privateIntegerid;

privateStringmake;

privateStringmodel;

privateIntegeryear;

...}

我们使用@Entity注释将基类定义为实体。然后@Inheritance,我们引入注释,指定InstanceType.TABLE_PER_CLASS策略。

使用此策略,我们可以执行多态查询,因此可以将的列表映射Car到CarDealer。

复杂的工会

尽管“每类表”提供了我们所需要的功能,但其生成的查询却很复杂。该策略还存在潜在的性能问题,因为我们必须执行所有Car子类表的并集,然后从中解析结果集。

例如,假设我们要在应用程序代码中添加两辆汽车:一辆SUV和一辆跑车。结果数据库表将包含以下内容:

Table: CARTable: SPORTSCAR

{ID: 1, MAKE: Carrera, MODEL: Porche, YEAR: 2018, TOPSPEED: 150, ZEROTOSIXTY: 4.5}Table: SUV

{ID: 2, MAKE: CX-9, MODEL: Mazda, YEAR: 2017, THIRDROW: true, TOWINGCAPACITY: 3000}

继承策略3:单一表格

下一个策略将所有实体的所有字段映射到单个表,并带有一个鉴别符列以标识实例类型:

@Entity@Inheritance(strategy = InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(name = "Car_Type")publicabstractclassCar{

@Id

@GeneratedValue

privateIntegerid;

privateStringmake;

privateStringmodel;

privateIntegeryear;

...}

@Entity@Table(name = "SPORTSCAR")@DiscriminatorValue("SportsCar")publicclassSportsCarextendsCar{

privateIntegertopSpeed;

privateDoublezeroToSixty;

...}

@Entity@Table(name = "SUV")@DiscriminatorValue("SUV")publicclassSportsUtilityVehicleextendsCar{

privateIntegertowingCapacity;

privateBooleanthirdRowSeating;

...}

单表策略是使用@Inheritance带有的注释实现的InstanceType.SINGLE_TABLE。我们为此添加了一个@DiscriminatorColumn注释,该注释定义了用于区分特定类类型的列名。特殊类(例如SportsCar和SportsUtilityVehicle)扩展了基类并添加了@DiscriminatorValue注释,该注释指定名称以标识其在数据库中的实例。

这种实现方式的挑战在于,每个表都维护所有专用类的所有属性的所有字段。您还会失去将专用字段定义为非null的功能,因为对于其他专用类,它们将为null。例如,代表a的SportsCar行将具有topSpeed和zeroToSixty值,但将具有null towingCapacity和thirdRowSeating列值。

基于我们对这种模式的实现,数据库的内容如下:

Table: CAR

{CAR_TYPE: SportsCar, ID: 1, MAKE: Carrera, MODEL: Porche, YEAR: 2018, TOPSPEED: 150, ZEROTOSIXTY: 4.5, THIRDROW: null, TOWINGCAPACITY: null},

{CAR_TYPE: SUV, ID: 2, MAKE: CX-9, MODEL: Mazda, YEAR: 2017, TOPSPEED: null, ZEROTOSIXTY: null, THIRDROW: true, TOWINGCAPACITY: 3000},

请注意,这两辆车都存储在CAR表中,并且每个表[都有一个] CAR_TYPE属性(来自@DiscriminatorColumn),该属性告诉Hibernate汽车的类型(来自@DiscriminatorValue。)还要注意,每一行都包含所有列值,未使用的列值设置为null。

这种策略允许进行高效且多态的查询。不利的一面是,可能需要维护许多null和不必要的列。

继承策略4:已加入

最终的JPA继承策略将基类和专用类之间的数据分离到各自的表中。基类表包含基类中定义的所有通用属性,而专用类表仅包含其特定属性。我们通过使用InstanceType.JOINED继承策略完成此分离:

@Entity@Inheritance(strategy = InheritanceType.JOINED)publicabstractclassCar{

@Id

@GeneratedValue

privateIntegerid;

privateStringmake;

privateStringmodel;

privateIntegeryear;

...}

定义继承关系时,您最期望的就是这种类型的继承的效果。对于我们的示例,数据库包含以下值:

Table: CAR

{ID: 1, MAKE: Carrera, MODEL: Porche, YEAR: 2018}

{ID: 2, MAKE: CX-9, MODEL: Mazda, YEAR: 2017}Table: SPORTSCAR

{TOPSPEED: 150, ZEROTOSIXTY: 4.5, ID: 1}Table: SUV

{THIRDROW: true, TOWINGCAPACITY: 3000, ID: 2}

使用此策略会导致表中没有很多null值,就像使用该SINGLE_TABLE策略一样。您也不必编写所需的全套表联合TABLE_PER_CLASS。不利的一面是,Hibernate必须在基类表和专用类表之间执行联接,这会使查询变得相当复杂。

选择JPA继承策略

通过四个选择,决定使用哪种策略取决于您的用例。通常,TABLE_PER_CLASS由于查询开销,我建议避免使用该策略。我还建议避免使用该MAPPED_SUPERCLASS策略,或者仅在不需要多态使用类的情况下使用该策略。

剩下SINGLE_TABLE和JOINED策略。

· 如果您需要快速查询,并且不介意null在单个表中维护多余的未使用列,则可以采用该SINGLE_TABLE策略。

· 如果您不想维护未使用的列,并且想要像在考虑数据库模型时那样对继承关系进行建模,请使用该JOINED策略。

就个人而言,我几乎总是JOINED在JPA中使用继承关系。

最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。

5d9da29036fab64cde7e8a0a26269e89.png

8d202125a51d32b27a87c2713c80df86.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值