【问题标题】:Best approach for linking diverse entity types in JPA在 JPA 中链接不同实体类型的最佳方法
【发布时间】:2016-07-18 09:21:48
【问题描述】:

短版适合仓促:

我的域模型中有各种表/实体具有相同的字段(UUID)。有一个表,我需要将此类实体的行/实例链接到其他 JPA 管理的实体。换句话说,该链接表中的字段实例不会预先知道。我能想到的两种方法是:

  • 使用抽象实体和 TABLE_PER_CLASS 策略,或者
  • 使用@MappedSuperClass 将实例的类名也存储在链接表中,或者类似的东西让我定义从正确的表中获取实际实例的逻辑。

两者在复杂性和性能方面各有优缺点。您认为哪个最好,是否有第三种选择,或者您过去曾尝试过类似的方法并建议/强烈警告不要这样做?

加长版如果您想要更多背景:

我有一个数据库/对象模型,其中许多类型都有一个公共字段:通用唯一标识符 (UUID)。原因是这些类型的实例可能会发生变化。更改遵循命令模型,并且它们的数据可以被封装并保持自身。让我们将这种变化称为“突变”。必须能够找出任何给定实体的数据库中存在哪些突变,反之亦然,存储的突变在哪个实体上运行。

以以下具有 UUID 的实体作为(极其简化的)示例:

为了存储“突变”,我们使用了一个名为MutationHolder 的表/实体。要将突变链接到其目标实体,可以使用MutationEntityLink。这些数据不直接在MutationHolder 上的唯一原因是因为可以有直接或间接链接,但这在这里并不重要,所以我把它省略了:

问题归结为如何在MutationEntityLink 中为entity 字段建模。我能想到两种方法。

首先是用UUID字段做一个抽象的@Entity注解类。 CustomerContractAddress 将扩展它。所以这是一个 TABLE_PER_CLASS 策略。我假设我可以将其用作entity 字段的类型,尽管我不确定。但是,我担心这可能会导致严重的性能损失,因为 JPA 需要查询许多表才能找到实际实例。

第二种是简单地使用@MappedSuperClass,并将实体的UUID存储在MutationEntityLinkentity字段中。为了获得具有该 UUID 的实际实体,我必须以编程方式解决它。添加一个带有实体类名的附加列,或者允许我识别它或将它粘贴到 JPQL 查询中的其他内容。这需要更多的工作,但似乎更有效率。如果需要,我不反对编写一些实用程序类或做一些反射/自定义注释工作。

我的问题是这些方法中哪一种看起来最好?或者,您可能有更好的建议,或者注意到我遗漏了一些东西;例如,也许有一种方法可以添加类型列,即使具有 TABLE_PER_CLASS 继承以将 JPA 指向正确的表?也许您已经尝试过这样的事情并想警告我可能会出现的许多问题。

一些附加信息:

  • 我们创建数据库架构,因此我们可以添加任何我们想要的内容。
  • 单表继承策略不是一个选项。这些表必须保持不同。出于同样的原因,联合继承似乎也不合适。
  • JPA 提供程序是 Hibernate,使用不属于 JPA 标准的东西不是问题。

【问题讨论】:

    标签: java hibernate jpa inheritance orm


    【解决方案1】:

    如果实体除了具有 uuid 之外没有任何共同点,我会使用您描述的第二种方法:使用 MappedSuperclass。使公共超类成为实体会阻止您在需要时使用不同的继承策略,即使没有实例存在,也需要该超实体的表,并且从业务角度来看这是错误的。

    链接本身可以通过多种方式实现,例如您可以为每个要映射的实体子类MutationEntityLink(例如CustomerMutationEntityLink 等)或按照您的描述进行操作,即仅存储uuid以及一些鉴别器/类型信息并以编程方式解析(我们正在使用该方法顺便说一句,类似的东西。)。

    【讨论】:

    • 很高兴听到您实际上正在使用这种方法。这表明它是可行的。我认为为每个映射实体子类化 MutationEntityLink 会非常不方便,所以现在最好使用一种鉴别器列。
    • @G_H 是的,这就是决定反对子类的原因。
    • 我喜欢@Thomas 为每个实体子类化 MutationEntityLink 的想法。因为它是一个提供突变和实体之间链接的技术类,所以我会选择单表继承。该表将具有其鉴别器列,以及每个实体的一个外键列。该表可能不是您的域模型最优雅的表示,但您可以从中获得良好的性能。
    • @NándorElődFekete 这将在模型中为我希望能够链接到突变的每个实体生成一个额外的表和额外的实体类。并且域模型可能会随着时间的推移而增长。看到这些子类/表中的每一个如何完全相同但对于不同的类型,我宁愿以通用方式以编程方式解决它。在我看来,这是强静态类型的一个弱点,我不认为仅仅为了满足 Java 和一些 API 而创建大量样板文件是值得的。
    • 只有一个带有单表继承的额外表,尽管列与实体一样多。但额外的好处是您不必编写特殊的数据访问代码,Hibernate 会创建简单的快速连接语句。样板部分只是每个实体的不同子类,但 Eclipse 几乎可以使用基类中的适当抽象方法将它们生成到 98% 的完整性。
    【解决方案2】:

    您需要在继承关联/方法/属性时使用@MappedSuperclass,而 TABLE_PER_CLASS 通常在您拥有实体和子实体时使用。如果有实体与模型中的基类有关联,则使用 TABLE_PER_CLASS,因为基类的行为类似于实体。否则,由于基类将包含对彼此不相关的实体通用的属性/属性和方法,因此使用@MappedSuperclass 会是一个更好的主意

    示例1:您需要为一些不同的活动设置闹钟,例如“吃药”、“打电话给妈妈”、“去看医生”等。闹钟消息的内容无关紧要,您需要提醒。所以使用 TABLE_PER_CLASS 因为警报消息,这是您的基类,就像这里的实体一样。

    示例 2: 假设基类 AbstractDomainObject 允许您为没有实体与基类关联的每个对象创建登录 ID、登录名、创建/修改日期,您需要指定关联以便以后清除,如“公司”、“大学”等。这种情况下,使用@MappedSuperclass会更好。

    【讨论】:

    • 映射超类和每个类继承策略的表之间的区别对我来说已经很清楚了。在我的情况下的挑战是,实际上有一个表需要引用作为抽象类实现的实体,但是对于域模型和性能@MappedSuperClass 比 TABLE_PER_CLASS 更有意义,所以要使用我需要自己为正确的实体类型创建查找逻辑。问题归结为这是否可行(Thomas 证实了这一点)以及是否存在现有的解决方案/最佳实践。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 2019-11-05
    • 2012-11-01
    • 2010-09-16
    • 1970-01-01
    • 2013-04-14
    相关资源
    最近更新 更多