JPA:如何具有相同实体types的一对多关系

有一个实体类“A”。 A类可能有同一types“A”的孩子。 另外“A”应该是它的父母,如果它是一个孩子。

这可能吗? 如果是的话我应该如何映射实体类中的关系? [“A”有一个id列。]

是的,这是可能的。 这是标准的双向@ManyToOne / @OneToMany关系的@OneToMany 。 这是特别的,因为关系的每一端的实体是相同的。 一般情况在JPA 2.0规范的第2.10.2节中有详细说明 。

这是一个成功的例子。 首先,实体类A

 @Entity public class A implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private A parent; @OneToMany(mappedBy="parent") private Collection<A> children; // Getters, Setters, serialVersionUID, etc... } 

下面是一个粗略的main()方法,它坚持三个这样的实体:

 public static void main(String[] args) { EntityManager em = ... // from EntityManagerFactory, injection, etc. em.getTransaction().begin(); A parent = new A(); A son = new A(); A daughter = new A(); son.setParent(parent); daughter.setParent(parent); parent.setChildren(Arrays.asList(son, daughter)); em.persist(parent); em.persist(son); em.persist(daughter); em.getTransaction().commit(); } 

在这种情况下,所有三个实体实例必须在事务提交之前持久化。 如果我无法坚持父子关系图中的一个实体,则在commit()上抛出exception。 在Eclipselink上,这是一个详细说明不一致性的RollbackException

此行为可通过A@OneToMany@ManyToOne批注中的cascade属性进行configuration。 例如,如果我在这两个注释上设置cascade=CascadeType.ALL ,我可以安全地坚持其中一个实体并忽略其他实体。 说我坚持parent在我的交易。 JPA实现遍历parentchildren属性,因为它被标记为CascadeType.ALL 。 JPA实现在那儿findsondaughter 。 然后,我代表我的两个孩子坚持下来,即使我没有明确要求。

再一个注意。 更新双向关系的双方始终是程序员的责任。 换句话说,每当我把一个孩子添加到父母身上时,我必须相应地更新孩子的父母财产。 只更新双向关系的一方是JPA下的错误。 始终更新关系的双方。 这是在JPA 2.0规范的第42页上明确写的:

请注意,应用程序负责维护运行时关系的一致性,例如,在应用程序在运行时更新关系时确保双向关系的“一”和“多”方面彼此一致。

对我来说,诀窍是使用多对多的关系。 假设你的实体A是一个可以进行细分的部门。 然后(跳过不相关的细节):

 @Entity @Table(name = "DIVISION") @EntityListeners( { HierarchyListener.class }) public class Division implements IHierarchyElement { private Long id; @Id @Column(name = "DIV_ID") public Long getId() { return id; } ... private Division parent; private List<Division> subDivisions = new ArrayList<Division>(); ... @ManyToOne @JoinColumn(name = "DIV_PARENT_ID") public Division getParent() { return parent; } @ManyToMany @JoinTable(name = "DIVISION", joinColumns = { @JoinColumn(name = "DIV_PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "DIV_ID") }) public List<Division> getSubDivisions() { return subDivisions; } ... } 

由于我在层次结构上有一些广泛的业务逻辑,JPA(基于关系模型)很弱,所以我引入了接口IHierarchyElement和实体监听器HierarchyListener

 public interface IHierarchyElement { public String getNodeId(); public IHierarchyElement getParent(); public Short getLevel(); public void setLevel(Short level); public IHierarchyElement getTop(); public void setTop(IHierarchyElement top); public String getTreePath(); public void setTreePath(String theTreePath); } public class HierarchyListener { @PrePersist @PreUpdate public void setHierarchyAttributes(IHierarchyElement entity) { final IHierarchyElement parent = entity.getParent(); // set level if (parent == null) { entity.setLevel((short) 0); } else { if (parent.getLevel() == null) { throw new PersistenceException("Parent entity must have level defined"); } if (parent.getLevel() == Short.MAX_VALUE) { throw new PersistenceException("Maximum number of hierarchy levels reached - please restrict use of parent/level relationship for " + entity.getClass()); } entity.setLevel(Short.valueOf((short) (parent.getLevel().intValue() + 1))); } // set top if (parent == null) { entity.setTop(entity); } else { if (parent.getTop() == null) { throw new PersistenceException("Parent entity must have top defined"); } entity.setTop(parent.getTop()); } // set tree path try { if (parent != null) { String parentTreePath = StringUtils.isNotBlank(parent.getTreePath()) ? parent.getTreePath() : ""; entity.setTreePath(parentTreePath + parent.getNodeId() + "."); } else { entity.setTreePath(null); } } catch (UnsupportedOperationException uoe) { LOGGER.warn(uoe); } } }