昔悩んでいたこのエラー。やっと原因がわかりました。
例えば以下のようなエンティティがあったとします。
@Entity public class Shop { @ManyToMany // fetchを指定しない場合、デフォルトはLAZYであることに注意 private Set<Product> products; ... } @Entity public class Product { ... }
見ての通り、ShopとProductはN:Nの関連です。
このとき、以下のようなコードを書くと上記のエラーが発生するようです。
// 新しくセットするShop.productsの値を作成 Set<Product> newProducts = new HashSet(); newProducts.add(...); newProducts.add(...); EntityManager em = ...; Shop shop = em.createTypedQuery("select s from Shop s where ...", Shop.class).getSingleResult(); shop.setProducts(newProducts); em.merge(shop); em.flush();// ←ココでConcurrentModificationExceptionが発生!!
で、コレを解決する秘訣は「遅延ロード対象かつ更新したいフィールドをロードしてしまう」こと。つまり、
Set<Product> newProducts = new HashSet(); newProducts.add(...); newProducts.add(...); EntityManager em = ...; Shop shop = em.createTypedQuery("select s from Shop s where ...", Shop.class).getSingleResult(); shop.getProducts().clear(); // ←この行に注目!! shop.setProducts(newProducts);
として、強制的にProductをロードしてしまいます。
(ここではclearメソッドを使って強制ロード。isEmptyとかでも行けるかも)
パフォーマンス的には確かに無駄な処理なのですが、ひとまずはこれで動くようになります。