title: Cannot catch ObjectOptimisticLockingFailureException Exception
body: <p>In order to have concurrency in my application, I've decided to use Optimistic locking and I can throw an exception now when my one of instance try to modify the entity. So I am at good shape about performing concurrency. I think that there is no issue about using locking mechanism on my entity.</p>
  <p>Version annotation at entity part</p>
  <pre><code>@Version
  private Integer version;
  </code></pre>
  <p>Locking in repository</p>
  <pre><code>@Transactional
  @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
  Optional&lt;Account&gt; findByAccountId(String accountId);
  </code></pre>
  <p>I wanted to catch <strong>ObjectOptimisticLockingFailureException</strong> to perform something in catch block. I was not able to catch <strong>ObjectOptimisticLockingFailureException</strong> even if I could see exception tree below. I've only seen an error and exception tree but program is going down however it is not sufficient for me.</p>
  <p>Please note that I am using Spring Boot version 2.3.2.RELEASE and PostgreSQL.</p>
  <p>Complete log is that:</p>
  <pre><code>2020-09-11 21:46:00.047 ERROR 18548 --- [   scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task
  
  org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.oisleyen.app.entity.Account#******] with identifier [******]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.oisleyen.app.entity.Account]
      at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:337) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:538) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:149) ~[spring-data-jpa-2.3.2.RELEASE.jar:2.3.2.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at com.sun.proxy.$Proxy148.findByAccountId(Unknown Source) ~[na:na]
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
      at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at com.sun.proxy.$Proxy90.findByAccountId(Unknown Source) ~[na:na]
      at com.oisleyen.app.service.AccountService(AccountService.java:32) ~[main/:na]
      at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:1.8.0_252]
      at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) ~[na:1.8.0_252]
      at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_252]
      at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_252]
      at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_252]
      at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:1.8.0_252]
      at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:1.8.0_252]
      at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_252]
      at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485) ~[na:1.8.0_252]
      at com.oisleyen.app.service.AccountService(AccountService.java:32) ~[main/:na]
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
      at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
      at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_252]
      at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_252]
      at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_252]
      at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_252]
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_252]
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_252]
      at java.lang.Thread.run(Thread.java:748) [na:1.8.0_252]
  Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.oisleyen.app.entity.Account#******]
      at org.hibernate.persister.entity.AbstractEntityPersister.forceVersionIncrement(AbstractEntityPersister.java:1879) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.action.internal.EntityIncrementVersionProcess.doBeforeTransactionCompletion(EntityIncrementVersionProcess.java:46) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:954) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:525) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2379) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
      at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:534) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
      ... 47 common frames omitted
  </code></pre>
  <p>Here is what I did</p>
  <pre><code>try {
      Account account = accountService.getAccount(accountId); // getAccount method calls findByAccountId which is locked above
      account.setProcessDate(LocalDateTime.now());
      accountService.saveAccount(myAccount);
  } catch (ObjectOptimisticLockingFailureException e) {
      // I would like to do something in there
  }
  </code></pre>
  <p>I've also tried to get <strong>StaleObjectStateException</strong> with <strong>ObjectOptimisticLockingFailureException</strong></p>
  <pre><code>try {
      Account account = accountService.getAccount(accountId); // getAccount method calls findByAccountId which is locked above
      account.setProcessDate(LocalDateTime.now());
      accountService.saveAccount(myAccount);
  } catch (ObjectOptimisticLockingFailureException | StaleObjectStateException  e) {
      // I would like to do something in there
  }
  </code></pre>
  <p>Could you please help me about above mentioned problem? Is there any suggestion how can I catch this exception properly?</p>