如何在 JPA 中表达多态关联?

发布于 2024-07-25 02:52:43 字数 1509 浏览 3 评论 0原文

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

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

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

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

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

例如,我想允许用户对文件夹中的对象进行分组,某些对象可以有多个“父对象”,并且关系随着时间的推移而变化。 我需要跟踪关系过去的情况,因此图表的边有一个与之关联的时间跨度,该时间跨度说明该边从何时到何时有效。

从节点到对象的链接存储在节点表的两列中,一列携带外部表中的 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 不同的解决方案。 我可以更改表格布局或引入新表格等。但是,我已经考虑了很多,而且表格结构对我来说似乎还不错。 也许还有我没想到的第三种方式。

A polymorphic association is similar to a foreign key or many-to-one relationship, with the difference being that the target might be one of a number of types (classes in the language, tables in the db).

I'm porting a database design I've been using for some years from PHP to Java. In the old code, I had rolled my own ORM, which wasn't optimal for a number of reasons. Although I might start to tweak things later, and maybe end up implementing things myself again, for now I'd like to use an off-the-shelf ORM and JPA on my entity classes.

Now, there's one thing about the database layout that I don't know how to express in JPA:

I have a Node and an Edge table storing a graph (a DAG, if it matters). Each node may optionally reference one other entity from the database. These entites may be refrenced multiple times throughout the graph and there may also be "orphaned" entites, which wouldn't be accesible for the user, but which may make sense to keep at least for a while.

These objects are not at all related in terms of inheritance etc. but have a natural hierarchy, similar to Customer->Site->Floor->Room. In fact, years ago, I started out with just foreign key fields pointing to the "parent" objects. However, this hierarchy isn't flexible enough and started falling apart.

For example, I want to allow users to group objects in folders, some objects can have multiple "parents" and also the relations change over time. I need to keep track of how the relations used to be, so the edegs of the graph have a timespan associated with them, that states from when to when that edge was valid.

The link from a node to an object is stored in two columns of the node table, one carries the id in the foreign table, one carries its name. For example (some columns omitted):

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  |
+----------+--------------------+

There might be a different solution than using JPA. I could change something about the table layout or introduce a new table etc. However, I have thought about this a lot already and the table structure seems OK to me. Maybe there's also a third way that I didn't think of.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

执手闯天涯 2024-08-01 02:52:43

我想你已经找到答案了。 创建一个抽象类(@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; 
}

我知道你可能不想暗示源和系列之间的关系,但扩展一个公共抽象(无表)类是我能想到的做你想做的事情的唯一方法。

InheritanceType.TABLE_PER_CLASS 会将 Source 和 Series 保留在单独的表中(您可以使用 SINGLE_TABLE 执行类似于前面答案的操作)。

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

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

I think you've already hit on an answer. Create an abstract class (either @Entity or @MappedSuperclass) and have the different types extend it.

Something like this might work

@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; 
}

I understand you might not want to imply a relationship between the Source and Series, but extending a common abstract (table-less) class is the only way I can think of to do what you want.

InheritanceType.TABLE_PER_CLASS will keep Source and Series in separate tables (you could use SINGLE_TABLE to do something like the previous answer).

If this isn't what you're looking for, many JPA providers provide a tool that creates mappings based on an existing set of tables. In OpenJPA it's called the ReverseMappingTool [1]. The tool will generate Java source files that you can use as a starting point for your mappings. I suspect Hibernate or EclipseLink have something similar, but you could just use the OpenJPA one and use the entity definitions with a different provider (the tool doesn't generate any OpenJPA specific code as far as I know).

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

终止放荡 2024-08-01 02:52:43

答案是:

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

The answer would be:

  • inheritance (as suggested already by Mike)
  • plus @DiscriminatorColumn to provide information which column stores the information about which subclass should be used: sxRef. The only doubt I see is the "sxRef" being a nullable column. I guess that it's forbidden.
∝单色的世界 2024-08-01 02:52:43

您是否看过 @Any 注释? 它不是 JPA 的一部分,而是其 Hibernate Annotation 扩展。

Have you looked at the @Any annotation? It's not part of JPA but is a Hibernate Annotation extension to it.

仲春光 2024-08-01 02:52:43

源表和系列表中存储了多少信息? 这只是一个名字吗? 如果是这样,您可以将它们合并到一个表中,并添加一个“类型”列。 您的 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 都属于超类吗? 这是该表的自然名称。

How much information is stored in the Source and Series tables? Is it just a name? If so, you could combine them into one table, and add a "type" column. Your Node table would lose its sRefType, and you would have a new table that looks like this:

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

This table would replace the Source and Series tables. Do Source and Series both belong to a superclass? That would be a natural name for this table.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文