Monthly Archives: December 2013

JPA java.util.ConcurrentModificationException on @manytomany List

I just had a java.util.ConcurrentModificationException when updating a List with JPA. Below the code which caused it, the required fix and some attention points.

Beurs.java

@Entity
@Table(name = "Beurs")
public class Beurs implements Serializable {
	private static final long serialVersionUID = -6250201709027758975L;

    @Id
	@Column(name = "beurscode", unique = true, nullable = false)
    private Long beurscode;

    private String beursnaam;

    private Integer iexFondsCode;

    private String iexFondsnaam;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "BeursFonds"  , joinColumns = { @JoinColumn(name = "beurs_beurscode", nullable = false, updatable = true) }
                                    , inverseJoinColumns = { @JoinColumn(name = "fonds_fondscode", nullable = false, updatable = true) })
	private List<Fonds> fondsen;

Fonds.java

@Entity
@Table(name = "Fonds")
public class Fonds implements Serializable {
	private static final long serialVersionUID = 4655056015858729584L;

	@Id
	@Column(name = "fondscode")
	private Long fondscode;

	@ManyToMany(mappedBy = "fondsen", fetch=FetchType.EAGER, cascade = CascadeType.ALL)
	private List<Beurs> beurzen;

The following code caused the exception, since while iterating over the List it is tried to update an entry of the same List. On some fora it is advised to use a ListIterator and call the add() and remove() functions on these.

	public void store(Fonds fonds) {
        List<Beurs> beurzen = fonds.getBeurzen();
        for(Beurs beurs: beurzen) {
            Beurs beursRepo = entityManager.find(Beurs.class, beurs.getBeurscode());

            beursRepo.getFondsen().add(fonds);
            entityManager.merge(beursRepo);
        }
	}

But since i also want an easy way to update existing objects in the array, I wrote the following code. First it updates the child [Fonds], then throws away all related entries in the jointable [if any] and then inserts the entries to the jointable.

Note that no update is being done on the parent [Beurs], so this one might be outdated on the first level cache. Especially the entries of the Fonds object. So in order to prevent first level cache issues, a flush() and a refresh() are done.

        entityManager.merge(fonds);

        // reset the manytomany
            beursRepository.deleteBeursFondsByFonds(fonds.getFondscode());

            for(Beurs beurs: fonds.getBeurzen()) {
                beursRepository.store(new BeursFonds(beurs.getBeurscode(), fonds.getFondscode()));

                Beurs beursRepo = entityManager.find(Beurs.class, beurs.getBeurscode());

                entityManager.flush();
                entityManager.refresh(beursRepo);
            }

Finally, the original exception. Which we just fixed.

java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
	at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:580)
	at com.gjdb.tradetracker.repository.HibernateFondsRepository.store(HibernateFondsRepository.java:33)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
	at com.sun.proxy.$Proxy34.store(Unknown Source)