Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA:如何拥有相同实体类型的一对多关系_Java_Orm_Jpa_Hierarchy_One To Many - Fatal编程技术网

Java JPA:如何拥有相同实体类型的一对多关系

Java JPA:如何拥有相同实体类型的一对多关系,java,orm,jpa,hierarchy,one-to-many,Java,Orm,Jpa,Hierarchy,One To Many,有一个实体类“A”。类A可能有相同类型“A”的子类。如果它是一个孩子,“A”也应该包含它的父母 这可能吗?如果是这样,我应该如何映射实体类中的关系? [“A”有一个id列。]是的,这是可能的。这是标准双向@manytone/@OneToMany关系的特例。它之所以特别,是因为关系两端的实体是相同的。一般情况详见本手册第2.10.2节 这里有一个成功的例子。首先,实体类A: @Entity public class A implements Serializable { @Id

有一个实体类“A”。类A可能有相同类型“A”的子类。如果它是一个孩子,“A”也应该包含它的父母

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

是的,这是可能的。这是标准双向
@manytone
/
@OneToMany
关系的特例。它之所以特别,是因为关系两端的实体是相同的。一般情况详见本手册第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...
}
在这种情况下,所有三个实体实例都必须在事务提交之前持久化。如果无法持久化父子关系图中的一个实体,则会在
commit()
上引发异常。在Eclipselink上,这是一个
回滚异常
,详细说明了不一致性

此行为可通过
A
@OneToMany
@ManyToOne
注释上的
级联
属性进行配置。例如,如果我在这两个注释上都设置了
cascade=CascadeType.ALL
,我就可以安全地保留其中一个实体而忽略其他实体。假设我在我的事务中保留了
parent
。JPA实现遍历父级
子级
属性,因为它被标记为
CascadeType.ALL
。JPA实现在那里找到了
。然后它代表我坚持两个孩子,即使我没有明确要求

再来一张。更新双向关系的双方始终是程序员的责任。换句话说,每当我向某个父对象添加子对象时,我都必须相应地更新该子对象的父对象属性。根据JPA,仅更新双向关系的一侧是错误的。始终更新关系的双方。JPA 2.0规范第42页明确地写了这一点:

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


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


为什么不使用更简单的@OneToMany(mappedBy=“DIV\u PARENT\u ID”)而不是带有自引用属性的@manytomy(…)呢?像这样重新键入表名和列名违反了DRY。也许这是有原因的,但我看不出来。此外,EntityListener示例简洁但不可移植,假设
Top
是一种关系。JPA 2.0规范第93页,实体侦听器和回调方法:“通常,可移植应用程序的生命周期方法不应调用EntityManager或查询操作、访问其他实体实例或修改关系”。对吗?如果我退出了,请告诉我。我的解决方案使用JPA1.0已经3年了。我根据生产代码对其进行了修改。我相信我可以去掉一些列的名称,但这不是重点。您的答案准确且简单,不确定当时我为什么使用多对多-但它确实有效,我相信更复杂的解决方案是有原因的。不过,我现在必须重新讨论这个问题。是的,top是一个自我参照,因此是一种关系。严格地说,我不修改它-只是初始化。而且,它是单向的,因此没有依赖关系,相反,它不引用除self之外的其他实体。根据您的报价,规范中有“一般”,这意味着它不是严格的定义。我相信在这种情况下,如果有的话,可移植性风险很低。非常感谢您的详细解释!这个例子切中要害,在第一次运行中就成功了。@sunnyj很高兴能提供帮助。祝您的项目好运。在创建包含子类别的类别实体时,它以前遇到过此问题。这很有帮助@DanLaRocque也许我误解了(或者有实体映射错误),但我看到了意想不到的行为。我在用户和地址之间有一对多的关系。当现有用户添加地址时,我按照您的建议同时更新用户和地址(并同时调用“保存”)。但这导致在我的地址表中插入了一个重复的行。这是因为我在用户的地址字段上错误配置了级联类型吗?@DanLaRocque是否可以将这种关系定义为单向关系??
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();
}
@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;
  }
...
}
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);
        }
    }

}