Gus*_*ors 6 java orm hibernate jpa
我们在 A <-> B <-> C“层次结构”中有三个具有双向多对多映射的实体,就像这样(当然是简化的):
@Entity
Class A {
@Id int id;
@JoinTable(
name = "a_has_b",
joinColumns = {@JoinColumn(name = "a_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "b_id", referencedColumnName = "id")})
@ManyToMany
Collection<B> bs;
}
@Entity
Class B {
@Id int id;
@JoinTable(
name = "b_has_c",
joinColumns = {@JoinColumn(name = "b_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "c_id", referencedColumnName = "id")})
@ManyToMany(fetch=FetchType.EAGER,
cascade=CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<C> cs;
@ManyToMany(mappedBy = "bs", fetch=FetchType.EAGER,
cascade={CascadeType.MERGE,CascadeType.PERSIST, CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<A> as;
}
@Entity
Class C {
@Id int id;
@ManyToMany(mappedBy = "cs", fetch=FetchType.EAGER,
cascade={CascadeType.MERGE,CascadeType.PERSIST, CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<B> bs;
}
Run Code Online (Sandbox Code Playgroud)
没有孤儿的概念——从应用程序的角度来看,实体是“独立的”——而且大多数时候我们会有一堆 A:s,每个都有几个 B:s(有些可能是在 A:s) 和大约 1000 个 C:s 之间“共享”,并非所有这些都总是被任何 B“使用”。我们已经得出结论,我们需要双向关系,因为无论何时删除实体实例,所有链接(连接表中的条目)也必须删除。这是这样做的:
void removeA( A a ) {
if ( a.getBs != null ) {
for ( B b : a.getBs() ) { //<--------- ConcurrentModificationException here
b.getAs().remove( a ) ;
entityManager.merge( b );
}
}
entityManager.remove( a );
}
Run Code Online (Sandbox Code Playgroud)
如果a.getBs()这里的集合包含多个元素,则ConcurrentModificationException抛出 a。我现在一直在敲我的头一段时间,但想不出一个合理的方法来删除链接而不干预集合,这让潜在的Iterator愤怒。
Q1:考虑到当前的 ORM 设置,我应该怎么做?(如果有的话……)
Q2:有没有更合理的方法来设计 OR 映射,让 JPA(在这种情况下由 Hibernate 提供)处理一切。如果我们不必包括那些I'll be deleted now, so everybody I know, listen carefully: you don't need to know about this!无论如何都不起作用的 -loops,那就太好了,就目前而言......
据我所知,这个问题与 ORM 无关。不能使用foreachJava 中的语法糖构造从集合中删除元素。
请注意,这
Iterator.remove是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改了基础集合,则行为是未指定的。
有问题的代码的简化示例:
List<B> bs = a.getBs();
for (B b : bs)
{
if (/* some condition */)
{
bs.remove(b); // throws ConcurrentModificationException
}
}
Run Code Online (Sandbox Code Playgroud)
您必须使用该Iterator版本在迭代时删除元素。正确的实现:
List<B> bs = a.getBs();
for (Iterator<B> iter = bs.iterator(); iter.hasNext();)
{
B b = iter.next();
if (/* some condition */)
{
iter.remove(); // works correctly
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:我认为这会奏效;然而未经测试。如果没有,你应该停止看到ConcurrentModificationExceptions 而是(我认为)你会看到ConstraintViolationExceptions。
void removeA(A a)
{
if (a != null)
{
a.setBs(new ArrayList<B>()); // wipe out all of a's Bs
entityManager.merge(a); // synchronize the state with the database
entityManager.remove(a); // removing should now work without ConstraintViolationExceptions
}
}
Run Code Online (Sandbox Code Playgroud)
如果此处的集合
a.getBs()包含多个元素,则ConcurrentModificationException抛出a
问题是 A、B 和 C 内部的集合是神奇的 Hibernate 集合,因此当您运行以下语句时:
b.getAs().remove( a );
Run Code Online (Sandbox Code Playgroud)
这会从 b 的集合中删除 a,但也会从 a 的列表中删除 b,而 a 的列表恰好是 for 循环中正在迭代的集合。这会生成ConcurrentModificationException.
如果您确实要删除集合中的所有元素,那么马特的解决方案应该有效。如果您不这样做,那么另一种解决方法是将所有 b 复制到一个集合中,从而从进程中删除神奇的 Hibernate 集合。
// copy out of the magic hibernate collection to a local collection
List<B> copy = new ArrayList<>(a.getBs());
for (B b : copy) {
b.getAs().remove(a) ;
entityManager.merge(b);
}
Run Code Online (Sandbox Code Playgroud)
这应该会让你在路上走得更远。
| 归档时间: |
|
| 查看次数: |
5978 次 |
| 最近记录: |