有一个实体类"A",它可能有与自身相同类型的子类"A"。此外,如果"A"是子类,它应该保存它的父类。
这是否可行?如果是,我应该如何映射实体类中的关系? ["A"有一个id列。]
有一个实体类"A",它可能有与自身相同类型的子类"A"。此外,如果"A"是子类,它应该保存它的父类。
这是否可行?如果是,我应该如何映射实体类中的关系? ["A"有一个id列。]
@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()
上抛出异常。在Eclipselink中,这是一个详细说明不一致性的RollbackException
。A
的@OneToMany
和@ManyToOne
注释上的cascade
属性可以配置此行为。例如,如果我在这些注释上设置了cascade=CascadeType.ALL
,则可以安全地持久化其中一个实体并忽略其他实体。假设我在事务中持久化了parent
。JPA实现遍历parent
的children
属性,因为它被标记为CascadeType.ALL
。JPA实现在那里找到son
和daughter
。然后,它代表我持久化了两个孩子,即使我没有明确请求。对于我来说,关键是使用多对多的关系。假设您的实体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);
}
}
}
Top
是一个关系。JPA 2.0规范第93页,实体侦听器和回调方法:“一般来说,可移植应用程序的生命周期方法不应调用EntityManager或Query操作,访问其他实体实例或修改关系”。对吗?如果我错了,请告诉我。 - Dan LaRocque