如何通过JPA存储树结构?

时间:2012-08-17 14:09:18

标签: jpa tree unique-constraint composite-primary-key

我想通过JPA存储树结构。该模型由两个实体类组成:TreeVertex。类Vertex基本上只包含顶点'name,类Tree一个名为parents的映射,它存储每个子顶点(child -> parent)的父顶点。

这种结构使我可以简单快速地访问信息,无论给定的子顶点是父顶点还是实际的父顶点(如果有的话)。

我使用以下注释来指定JPA(在我的情况下是EclipseLink)应该如何存储关系:

@MapKeyClass(Vertex.class)
@MapKeyJoinColumn(name = "child_id", nullable = false)
@OneToMany(targetEntity = Vertex.class, cascade = CascadeType.ALL)
@JoinTable(name = "bug_492_tree_parents", inverseJoinColumns = { @JoinColumn(name = "parent_id") })
private final Map<Vertex, Vertex> parents = Maps.newHashMap();

不幸的是,使用上面的映射,我无法存储两个具有相同父级的子顶点,例如V = {parent, child1, child2}E = {{child1, parent}, {child2, parent}}(因此,child1 -> parentchild2 -> parent)因为我获得完整性约束违规:

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '3-4' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO bug_492_tree_parents (parent_id, tree_id, child_id) VALUES (?, ?, ?)

这是由JPA / EclipseLink如何在连接表中指定主键引起的:

Incorrect primary key definition

不幸的是,EclipseLink选择了复合键(tree_id,parent_id)作为主键。使用此主键,无法存储包含两个不同子顶点的树,其中父顶点与上面的简单示例相同。

只有在将主键(在MySQL数据库模式中)手动修复到(tree_id,child_id)之后,我才能存储该树:

Fixed primary key

我尝试了很多不同的映射;但是,我总是遇到一些(其他)问题。

最后,我的问题是:我应该如何通过JPA存储这个简单的树结构?我必须选择不同的映射吗?我需要改变模型吗?

1 个答案:

答案 0 :(得分:0)

我不认为JPA可以做你想做的事。

当把地图放在一个名为E的实体中时,JPA期望你想使用V的某个属性作为地图的K,并在E和V之间建立一个不在K和V之间的OneToMany关系。

最简单的解决方案是忘记您的地图并在您的类中放置一个List<Vertex>,其中只包含顶部节点,并在顶点类中为List<Vertex>放置顶点之间的子父关系。

如果你想要一个更像你现在的结构,我想你将不得不引入一个额外的实体,如:

@Entity
VertexRelation {
    private Vertex parent;
    private Vertex child;
    ...
}

然后您的地图可能会成为Map<Vertex, VertexRelation>,您可以使用@MapKey(name="parent")告诉JPA您希望使用parent作为地图的关键字。

注意:使用附加实体只是一个想法,我自己从未尝试过。我总是使用类似于我对树木的第一个建议。