数据设计
我的实体< - > 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;
}
关系不是双向的:用户界面允许将B实体添加到A实体,但编辑B实体不允许编辑aList
。
到目前为止:问题
在创建,更新和删除时,R实体在数据库中被正确保留和删除
bList
中正确拥有B,但B在aList
中没有A aList
中有B 我尝试了什么
slave
属性中添加一些级联属性,这是一个坏主意bList
==&gt;中的关系。因so-far > 1)
而失败:B无法识别与aList
中添加R实体来更新B实体,然后继续entityManager.merge()
操作=&gt; JPA错误,因为R已存在(即使我没有级联合并属性?)aList
并且无法识别它与哪个A实体相关< / LI>
醇>
到目前为止,为了正确配置关系,我错过了什么?
我附加解决方案以连接评论和答案的反馈。正如克里斯正确提到的,它是关于JPA交易范围的。在进一步讨论之前,请先详细说明一下:
我的解决方案是挖掘我的第三次尝试(创建和更新A的操作,也更新B实体),如Chris&#39;回答:
public class aEjb{
// ...
public void update(A a){
// by A -> R cascade, all the bList will be updated
em.merge(a);
// BUT !!!
// as the transaction did not end, the newly created
// relationship do not have a primary key generated
// yet so if I do:
for(R r : a.getBList()){
B b = r.getSlave();
// I'm here adding r = (null, a, b)
b.add(r);
// as r has a null primary key, JPA will try to
// create it, even if I removed all cascade annotation
// on b side => I didn't really understand why though
bEjb.update(b);
// as there is UNIQUE(a,b) constraint, JPA will
// throw an exception
}
// the solution was to fetch a fresh copy of the
// cascaded created R entity. As the primary key
// is not known yet, I have to go through sth like
for(R r : a.getBlist()){
B b = r.getSlave();
R updatedR = rEjb.findByMasterAndSlave(a, b);
// I'm here adding updatedR = (123, a, b)
b.add(updatedR)
// as updatedR primary key is not null, JPA
// won't force any cascade operation, and that's
// great becaues I don't want any cascade here
bEjb.update(b);
}
}
// ...
}
到目前为止,bList
和aList
是最新的。如果删除了B实体,我可以循环使用aList
。但是,又出现了一个问题:如果我从A实体中删除了B实体但只删除了链接,而不是删除了这些实体中的任何一个,该怎么办?
解决方案是避免选项orphanRemoval = true
:
在:
@Entity
// ...
public class A{
// ...
@OneToMany(mappedBy="master", cascade = Cascade.ALL, orphanRemoval = true)
@OrderBy("ordering")
private List<R> bList;
// ....
}
后:
@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);
}
}
然后我继续进行类似的EJB操作,以更新受影响的B实体
答案 0 :(得分:1)
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仍然是受管理的,因此会自动选择更改。由于Cascade.ALL设置在A-&gt; R关系上,因此甚至不需要持久调用,但我发现最好明确调出。无论您是在查看bList还是aList,这都将确保R始终在列表中。
要删除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的更改合并回到持久化上下文中。
答案 1 :(得分:0)
你需要添加这样的构造函数来解决问题1:
var persons = new List<Person> {
new Person { Name = "James", Age = 18, Male = true },
new Person { Name = "Nicolas", Age = 52, Male = true },
new Person { Name = "Susan", Age = 37, Male = false }
};
var result = persons.Aggregate(new Result(), (c, n) =>
{
c.Names.Add(n.Name);
c.Age.Add(n.Age);
c.Male.Add(n.Male);
return c;
});
尝试按照相同的方式解决问题2。