Explication du message d'erreur pour les accès concurrents

Le message d'erreur est de ce type (j'ai mis en valeur les passages importants) :

A system exception occurred during an invocation on EJB GestionnaireCompte, 
method: public void fr.uns.grin.tpbanque.ejb.GestionnaireCompte.deposer(fr.uns.grin.tpbanque.entities.CompteBancaire,int)|#]
javax.ejb.EJBException
	at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:750)
	at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:700)
	at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:505)
	at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4761)
	at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2147)
	at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2117)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:220)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:90)
	at com.sun.proxy.$Proxy625.deposer(Unknown Source)
	at fr.uns.grin.tpbanque.ejb.__EJB31_Generated__GestionnaireCompte__Intf____Bean__.deposer(Unknown Source)
	at fr.uns.grin.tpbanque.jsf.MouvementBean.enregistrerMouvement(MouvementBean.java:83)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   ...
Caused by: javax.persistence.OptimisticLockException: Exception [EclipseLink-5010] (Eclipse Persistence Services - 2.7.4.payara-p2): org.eclipse.persistence.exceptions.OptimisticLockException
Exception Description: The object [fr.uns.grin.tpbanque.entities.CompteBancaire[ id=1 ]] cannot be merged 
because it has changed or been deleted since it was last read. 
Class> fr.uns.grin.tpbanque.entities.CompteBancaire
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.mergeInternal(EntityManagerImpl.java:645)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.merge(EntityManagerImpl.java:620)
	at com.sun.enterprise.container.common.impl.EntityManagerWrapper.merge(EntityManagerWrapper.java:305)
	at fr.uns.grin.tpbanque.ejb.GestionnaireCompte.update(GestionnaireCompte.java:57)
	at fr.uns.grin.tpbanque.ejb.GestionnaireCompte.deposer(GestionnaireCompte.java:71)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   ...

L'exception première est une OptimisticLockException mais elle est enveloppée par une EJBException. La ligne qui a provoqué l'erreur correspond à l'instruction
return em.merge(compteBancaire);

Le déroulement des faits :

  1. L'erreur a lieu dans N1 car il a été le premier à récupérer le compte bancaire de Ringo Starr. Il a récupéré en même temps son numéro de version, disons 45.
  2. Ensuite N2 récupère le même compte avec le même numéro de version. N2 modifie le compte. Au moment de l'enregistrement le numéro de version est 45 et donc tout est correct. Cette modification passe le numéro de version à 46.
  3. Lorsque N1 modifie le compte et veut enregistrer la modification, JPA constate que le numéro de version actuel (46) n'est pas égal à celui au moment de la récupération du compte dans la base (45). Donc le compte a été modifié concuremment et une exception est lancée pour éviter les problèmes liés aux accès concurrents.