Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.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
JPA带关系(排序)表的多个级联_Jpa_Many To Many_Eclipselink - Fatal编程技术网

JPA带关系(排序)表的多个级联

JPA带关系(排序)表的多个级联,jpa,many-to-many,eclipselink,Jpa,Many To Many,Eclipselink,数据设计 我的A实体B实体关系通过R实体完成。但是,R有额外的信息阻止使用@ManyToMany: id | id_a | id_b | previous | ordering_index ---------------------------------------------- 1 | 1 | 10 | 2 | 1 2 | 1 | 15 | null | 0 3 | 1 | 18 | 1

数据设计

我的A实体B实体关系通过R实体完成。但是,R有额外的信息阻止使用@ManyToMany:

id  | id_a | id_b | previous | ordering_index
----------------------------------------------
 1  |  1   |  10  |    2     |       1
 2  |  1   |  15  |   null   |       0
 3  |  1   |  18  |    1     |       2
JPA实体

@Entity
// ...
public class A{
    // ...
    @OneToMany(mappedBy="master", cascade = Cascade.ALL, orphanRemoval = true)
    @OrderBy("ordering")
    private List<R> bList;
    // ....
}

@Entity
// ...
public class B{
    // ...
    @OneToMany(mappedBy="slave", cascade = Cascade.REMOVE)
    private List<R> aList;
    // ....
}

@Entity
// ...
public class R{
    @Id
    // ...
    private long id;

    @ManyToOne
    @JoinColumn(name = "id_a", referencedColumnName = "id")
    private A master;

    @ManyToOne
    @JoinColumn(name = "id_b", referencedColumnName = "id")
    private B slave;

    @OneToOne
    @JoinColumn(name = "previous", referencedColumnName = "id")
    private R previous;

    @Column(name = "ordering_index")
    private int ordering;
}
附加说明 到目前为止,
bList
aList
都是最新的。如果删除了一个B实体,我有一个可用的
列表
。然而,又出现了一个问题:如果我从a实体中删除了B实体,但只删除了链接,而不删除这些实体中的任何一个,该怎么办

解决方案是避免选项
删除=true

之前:

@Entity
// ...
public class A{
    // ...
    @OneToMany(mappedBy="master", cascade = Cascade.ALL, orphanRemoval = true)
    @OrderBy("ordering")
    private List<R> bList;
    // ....
}
@实体
// ...
公共A类{
// ...
@OneToMany(mappedBy=“master”,cascade=cascade.ALL,orphan=true)
@订购人(“订购”)
私人名单;
// ....
}
之后:

@Entity
// ...
public class A{
    // ...
    @OneToMany(mappedBy="master", cascade = Cascade.ALL)
    @OrderBy("ordering")
    private List<R> bList;

    @Transient
    private List<R> orphanBList;
    // ....

    public void addB(B b){
        R r = new R(a,b);
        bList.add(r);
    }

    public void removeR(R r){
        bList.remove(r);
        orphanBList.add(r);
    }
}
@实体
// ...
公共A类{
// ...
@OneToMany(mappedBy=“master”,cascade=cascade.ALL)
@订购人(“订购”)
私人名单;
@短暂的
私人名单孤儿名单;
// ....
公共无效地址B(B){
R=新的R(a,b);
b添加(r);
}
公共空隙清除器(R){
b.删除(r);
添加(r);
}
}

然后,我继续执行一个类似的EJB操作,按照定义更新受影响的B实体

您需要添加这样的构造函数来解决问题1:

public R(A master, B slave, R previous, int ordering){
    this.master = master;
    master.getBList().add(this);
    this.slave = slave;
    slave.getAList().add(this);
    this.previous = previous;
    this.ordering = ordering;
}

对于问题2,尝试采用相同的方法。

A->R和B->R是两个独立的双向关系,必须维护它们,以保持对象模型与数据库中的内容同步。当您添加一个新的R实例时,您需要将a和B引用的两侧都关联起来,并且由于您同时更改a和B实例,所以如果这些更改被分离,您需要负责将它们合并回来。有很多方法可以解决这个问题,最简单的方法是:

em.getTransaction().begin();
A a = em.find(A.class, AsID);
B b = em.find(B.class, BsID);
R r = new R(a, b);
a.bList.add(r);
b.aList.add(r);
em.persist(r);
em.getTransaction().commit();
在上面的代码中,顺序并不是那么重要,因为它都是在同一个上下文中完成的——A和B仍然是受管理的,所以更改会自动拾取。persist调用实际上并不需要,因为您在A->R关系上设置了Cascade.ALL,但我发现显式调用更好。这将确保R始终在列表中,无论您是查看bList还是aList

要删除A,必须执行以下操作:

em.getTransaction().begin();
A a = em.find(A.class, AsID);
for (R r: a.bList) {
  if (r.slave != null)
    r.slave.aList.remove(r);
}
em.remove(a);
em.getTransaction().commit();

同样,这是有效的,因为一切都是在同一个上下文中管理的。如果您要通过DAO类型调用来回传递此信息,您必须自己处理合并分离的实体,特别是将对r.slave的更改合并回持久性上下文。

Prob 1:您需要将B添加到A,将A添加到B。您的r未设置为级联,因此,除非手动合并,否则对A或B所做的更改不会自动拾取。对于问题2,这是相同的。当R被删除时,应用程序负责清除对R的引用。您需要手动从A的bList中删除R,或者需要强制刷新缓存中的所有内容。问题1:很遗憾,我无法直接将B添加到A。我需要创建R1=新的R(A,B),然后将R1添加到A。到目前为止,这还可以。但是,如果我将R1添加到B中,JPA将尝试持久化现有R1。我不能创建一个R2=R1的新R(a,B)。我没有在R的属性中标记任何cascade,因为据说cascade是@ManyToOne是不好的实践rob 2:看起来这是同一问题的另一面。给定一个B实体,我需要找到相关的a实体来清除引用。我最初的设计是获取R列表。对于每个Rx(R1,R2,…),获取A实体,删除相应的Rx,然后自行更新A实体。问题是我的R列表总是空的。即使数据库中的数据是正确的,似乎我在B实体中错误地配置了
aList
,我找不到什么是说级联是一种不好的做法?它不是,除非你真的需要,否则不应该使用它。当您对A和B进行更改时,您需要合并A和B,并且您不需要在R映射上使用级联选项来完成它,这只是使它更容易。如果将R添加到A,还需要将相同的R添加到B,并合并A和B。分别使用级联或显式合并。如果您得到了重复的插入,说明您做错了什么,需要检查正在合并的内容是否设置了ID值。检查您是否只使用从merge call传回的实例,而不是直接在R的构造函数中,而是在A将B实体添加为“子实体”时,它已经执行了类似的操作。我正在合并A,它级联到R,但B的状态没有更新。当我简单地在B上进行合并时,与a合并的方法相同,JPA试图持久化已经存在的R,这导致了一个例外。我觉得我有一个JPA事务问题。问题是,在分离A时会生成R。当用户单击“编辑”时,实体管理员将获取A的最后一个副本。当他想将B实体添加为子实体时,R实体将在此时实例化,并反向添加到B的列表中。然后我合并A,它正确地级联了R,但如果我尝试在同一事务中合并B,JPA尝试在数据库中持久化R,但R已经存在。您的回答给了我关于事务的致命提示。由于我使用的是JTA,我认为我不能完全控制JPA事务范围,所以这就是诀窍。此外,我被JPA级联欺骗了:我不确定级联实体何时生成主键。我已经用我的临时解决方案编辑了我的帖子,稍后将在测试后确认,但我对此感觉很好。无论如何,非常感谢!
em.getTransaction().begin();
A a = em.find(A.class, AsID);
for (R r: a.bList) {
  if (r.slave != null)
    r.slave.aList.remove(r);
}
em.remove(a);
em.getTransaction().commit();