java - JPA ManyToMany ConcurrentModificationException 问题

标签 java hibernate orm 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;
}

没有孤儿的概念 - 从应用程序的角度来看,实体是“独立的” - 大多数时候我们都会有一把 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 );
}

如果集合(这里的a.getBs())包含多个元素,则会抛出ConcurrentModificationException。我已经绞尽脑汁有一段时间了,但想不出一种合理的方法来删除链接而不干扰集合,这让底层的迭代器很生气。

问题 1:考虑到当前的 ORM 设置,我应该如何执行此操作? (如果有的话……)

Q2:是否有更合理的方法来设计 OR 映射,让 JPA(在本例中由 Hibernate 提供)处理所有事情。如果我们不必包含那些我现在就会被删除,所以我认识的每个人,请仔细听:你不需要知道这个!-循环,就目前情况而言,无论如何都不起作用......

最佳答案

据我所知,这个问题与 ORM 无关。 您不能使用 Java 中的语法糖 foreach 构造从集合中删除元素。

Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.

Source

有问题代码的简化示例:

List<B> bs = a.getBs();
for (B b : bs)
{
    if (/* some condition */)
    {
        bs.remove(b); // throws ConcurrentModificationException
    }
}

必须使用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
    }
}
<小时/>

编辑:我认为这会起作用;然而未经测试。如果没有,您应该停止看到 ConcurrentModificationException,但(我认为)您会看到 ConstraintViolationException

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
    }
}

关于java - JPA ManyToMany ConcurrentModificationException 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4627221/

相关文章:

java - Hibernate 中的 ManyToOne 多重连接列

java - 验证所有 JDBC 调用都发生在一个事务中

java - hibernate OGM : Having trouble starting MongoDB database

java - Hibernate ManyToOne 使用外键连接到非主键目标列

java - Guava LoadingCache getAll - 但没有任何参数?

java - 对整数堆栈的数组列表进行排序时出现问题

java - android - 仅当条件为真时获取位置并将数据发送到远程服务器

java - 在圆弧中心画线

error-handling - Sequelize 模型重复值检查

php - 使用许多一对多关系(ORM)减少MySQL中的查询