【问题标题】:How do I express a polymorphic association in JPA?如何在 JPA 中表达多态关联?
【发布时间】:2009-06-26 14:40:53
【问题描述】:

polymorphic association 类似于外键或多对一关系,不同之处在于目标可能是多种类型之一(语言中的类,数据库中的表)。

我正在将我使用了几年的数据库设计从 PHP 移植到 Java。在旧代码中,我推出了自己的 ORM,由于多种原因,这并不是最优的。虽然我可能稍后会开始调整,最终可能会再次自己实现,但现在我想在我的实体类上使用现成的 ORM 和 JPA。

现在,关于数据库布局有一点我不知道如何在 JPA 中表达:

我有一个 Node 和一个 Edge 表来存储一个图表(一个 DAG,如果重要的话)。每个节点可以选择性地引用数据库中的另一个实体。这些实体可能在整个图表中被多次引用,也可能存在“孤立”实体,用户无法访问,但至少保留一段时间可能有意义。

这些对象在继承等方面完全不相关,但具有自然层次结构,类似于 Customer->Site->Floor->Room。事实上,几年前,我一开始只使用指向“父”对象的外键字段。但是,这种层次结构不够灵活,开始分崩离析。

例如,我想允许用户在文件夹中对对象进行分组,一些对象可以有多个“父对象”,并且关系也会随着时间而变化。我需要跟踪过去的关系,所以图的边有一个与之关联的时间跨度,说明从什么时候到什么时候那个边是有效的。

从节点到对象的链接存储在节点表的两列中,一列带有外部表中的id,一列带有其名称。例如(省略了一些列):

table Node:
+--------+-------+----------+
| ixNode | ixRef | sRefType |
+--------+-------+----------+
|    1   |  NULL |   NULL   |  <-- this is what a "folder" would look like
|    2   |   17  |  Source  |
|    3   |   58  |  Series  |  <-- there's seven types of related objects so far
+--------+-------+----------+

table Source (excerpt):
+----------+--------------------+
| ixSource |        sName       |
+----------+--------------------+
|    16    | 4th floor breaker  |
|    17    | 5th floor breaker  |
|    18    | 6th floor breaker  |
+----------+--------------------+

可能有与使用 JPA 不同的解决方案。我可以更改表格布局或引入新表格等。但是,我已经考虑了很多,表格结构对我来说似乎还可以。也许还有第三种我没有想到的方式。

【问题讨论】:

    标签: jpa database-design modeling polymorphic-associations


    【解决方案1】:

    我想你已经找到了答案。创建一个抽象类(@Entity 或@MappedSuperclass)并让不同的类型对其进行扩展。

    这样的事情可能会奏效

    @MappedSuperclass
    @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
    public abstract class Edge { 
        // . . .
        @OneToMany
        Collection<Node> nodes; 
    }
    
    @Entity 
    public class Source extends Edge { 
    }
    
    @Entity public class Series extends Edge { 
    }
    
    @Entity
    public class Node { 
        // . . .
        @ManyToOne
        Edge edge; 
    }
    

    我知道您可能不想暗示 Source 和 Series 之间的关系,但扩展一个通用的抽象(无表)类是我能想到的做您想做的唯一方法。

    InheritanceType.TABLE_PER_CLASS 将 Source 和 Series 保存在单独的表中(您可以使用 SINGLE_TABLE 来执行类似上一个答案的操作)。

    如果这不是您想要的,许多 JPA 提供程序都提供了一种工具,可以根据现有的一组表创建映射。在 OpenJPA 中,它被称为 ReverseMappingTool [1]。该工具将生成 Java 源文件,您可以将其用作映射的起点。我怀疑 Hibernate 或 EclipseLink 有类似的东西,但您可以只使用 OpenJPA 并使用具有不同提供程序的实体定义(据我所知,该工具不会生成任何 OpenJPA 特定代码)。

    [1]http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_pc_reverse

    【讨论】:

    • 这很容易实现,但我不明白的是:持久性提供者如何知道将哪个类实例化为 Node 中特定行的引用对象?
    • 好问题。我已经使用 OpenJPA 对这个解决方案进行了建模(其他供应商的方法可能不同)。 OpenJPA 在 Node 表中存储一个字符串,该字符串包含边缘的类名和 PK。基本上它将 ixRef + sRefType 组合成一列。我不确定如何说服 JPA 提供者使用您当前拥有的表定义(ReverseMappingTool 或类似的东西可能会弄清楚)。我假设您需要保持现有表的完整性,而这种方法对您不起作用?
    • 不,我可以随心所欲地调整架构,我只是好奇(或称其为怀疑 :-))。那么,似乎这是解决方案。我仍然想知道如果您让一个提供者设置数据库然后更改提供者会发生什么。但我想这是你推迟到真正必须知道的事情之一。
    • 应该可以随时更换供应商。当然,它有一些警告。 Hibernate 和 OpenJPA 可能会以不同的方式处理生成的值(例如,OpenJPA 创建一个 OPENJPA_SEQUENCE_TABLE 表)。他们也可能对约束做出不同的假设。
    • 同时,我对此进行了测试,遗憾的是,它不起作用。使用@MappedSuperclass,我的提供者(eclipselink)不接受基类型作为@ManyToOne 注释的目标。使用@Entity,我得到一个总是空的多余表和不合格的引用键(即只有 id,没有关于 id 指向的键)
    【解决方案2】:

    答案是:

    • 继承(正如 Mike 已经建议的那样)
    • 加上@DiscriminatorColumn 提供信息,哪个列存储有关应使用哪个子类的信息:sxRef。我看到的唯一疑问是“sxRef”是一个 nullable 列。我猜这是被禁止的。

    【讨论】:

      【解决方案3】:

      你看过@Any注解吗?它不是 JPA 的一部分,而是对其的 Hibernate Annotation 扩展。

      【讨论】:

      • 虽然这似乎至少接近我想要的,但我目前没有使用 Hibernate,我也不想切换,也不想被绑定到这样一个核心的特定提供商我的应用程序的元素。不过,为“很高兴知道”+1。
      【解决方案4】:

      Source 和 Series 表中存储了多少信息?它只是一个名字吗?如果是这样,您可以将它们组合到一个表中,并添加一个“类型”列。您的 Node 表将丢失其 sRefType,并且您将拥有一个如下所示的新表:

      ixSource        sName                  sType
        16            4th floor breaker      SOURCE
        17            5th floor breaker      SOURCE
        18            6th floor breaker      SOURCE
        19            1st floor widget       SERIES
        20            2nd floor widget       SERIES
      

      此表将替换 Source 和 Series 表。 Source 和 Series 都属于超类吗?这将是此表的自然名称。

      【讨论】:

      • 抱歉,这对我不起作用。表格中有更多信息,也有更多表格,示例已简化。有多种类型的对象,我需要一个图表来更灵活地了解它们之间的关系。
      • 也许,我可以设想某种抽象的 RelatedEntity 和一个存储键值对的通用属性表,但这似乎有点太过分了。我在数据库中几乎没有结构化信息了。
      猜你喜欢
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 2014-03-24
      • 1970-01-01
      • 2019-10-20
      相关资源
      最近更新 更多