我想通过JPA存储树结构。该模型由两个实体类组成:Tree
和Vertex
。类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 -> parent
和child2 -> 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如何在连接表中指定主键引起的:
不幸的是,EclipseLink选择了复合键(tree_id,parent_id)
作为主键。使用此主键,无法存储包含两个不同子顶点的树,其中父顶点与上面的简单示例相同。
只有在将主键(在MySQL数据库模式中)手动修复到(tree_id,child_id)
之后,我才能存储该树:
我尝试了很多不同的映射;但是,我总是遇到一些(其他)问题。
最后,我的问题是:我应该如何通过JPA存储这个简单的树结构?我必须选择不同的映射吗?我需要改变模型吗?
答案 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作为地图的关键字。
注意:使用附加实体只是一个想法,我自己从未尝试过。我总是使用类似于我对树木的第一个建议。