Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting org.springframework.orm.ObjectOptimisticLockingFailureException in spring

I have created a function which is trying to save something in DB where version is enabled hence it can give optimistic lock exception. I catch the exception and try it again after fetching from DB. I am also using @transactional annotation to start a new transaction everytime. But I get org.springframework.orm.ObjectOptimisticLockingFailureException even before my save function is called.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleExecution(@NonNull final Execution execution, final Map<String, Object> data, final String pattern) {

    log.info("Processing notification pattern {} with data {} for execution {}",pattern,data,execution);

    if (ExecutionStatus.PROCESSING.equals(execution.getStatus()) || ExecutionStatus.WAITING.equals(execution.getStatus())) {
        //Put the received notification in the execution queue

        List<Map<String, Object>> notificationQueue = execution.getNotificationQueue();
        Map<String,Object> notificationMap = new HashMap<>();
        notificationMap.put(pattern, data);
        notificationQueue.add(notificationMap);
        execution.setNotificationQueue(notificationQueue);

        try
        {
            executionBuilder.save(execution);
        }
        catch(OptimisticLockingFailureException e)
        {
            log.error("Save of execution {} failed due to optimistic locking, retrying",execution);
            //Fetch the object again and call again
            Optional<Execution> savedExecution = executionBuilder.findOne(execution.getId());
            if(savedExecution.isPresent())
            {
                handleExecution(savedExecution.get(),data, pattern);
            }
        }

    }
}

The detailed exception which I get is:

2018-07-11 08:09:39.408  INFO 8251 --- [thread_pool_executor-45] 
o.h.e.j.b.internal.AbstractBatchImpl     : HHH000010: On release of 

batch it still contained JDBC statements
2018-07-11 08:09:39.408 ERROR 8251 --- [thread_pool_executor-45] reactor.bus.EventBus                     : Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:320) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
        at com.gor.platform.process.support.service.builder.ExecutionBuilderImpl$$EnhancerBySpringCGLIB$$a036b2e6.patch(<generated>) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
        at com.gor.platform.process.support.service.event.receiver.ProcessEventReceiver.accept(ProcessEventReceiver.java:58) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
        at com.gor.platform.process.support.service.event.receiver.ProcessEventReceiver.accept(ProcessEventReceiver.java:23) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
        at reactor.bus.EventBus$3.accept(EventBus.java:317) ~[reactor-bus-2.0.8.RELEASE.jar:na]
        at reactor.bus.EventBus$3.accept(EventBus.java:310) ~[reactor-bus-2.0.8.RELEASE.jar:na]
        at reactor.bus.routing.ConsumerFilteringRouter.route(ConsumerFilteringRouter.java:72) ~[reactor-bus-2.0.8.RELEASE.jar:na]
        at reactor.bus.EventBus.accept(EventBus.java:591) [reactor-bus-2.0.8.RELEASE.jar:na]
        at reactor.bus.EventBus.accept(EventBus.java:63) [reactor-bus-2.0.8.RELEASE.jar:na]
        at reactor.core.dispatch.AbstractLifecycleDispatcher.route(AbstractLifecycleDispatcher.java:160) [reactor-core-2.0.8.RELEASE.jar:na]
        at reactor.core.dispatch.MultiThreadDispatcher$MultiThreadTask.run(MultiThreadDispatcher.java:74) [reactor-core-2.0.8.RELEASE.jar:na]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_171]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_171]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_171]
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
        at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:67) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:54) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3082) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2961) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3341) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2339) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
        at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]

The executionBuilder.save internally calls dao.save which is not seen in the stack trace. Why is this exception getting thrown even before the save is called. When save is called then the exception is caught and things work fine. But occasionally this exception is thrown without the save and hence doesn't get caught.

My gut feeling says it has something to do with @Transactional.

For the information my application is multithreaded and many thread might modify the same data at the same time.

Any help would be appreciated.

like image 495
Arpan Agrawal Avatar asked Sep 08 '26 13:09

Arpan Agrawal


1 Answers

you can handle optimistic lock using spring retry option inside the controller. Download for this class

import org.springframework.retry.annotation.Retryable;

For Example

@RequestMapping(value = CommonConstants.DETAILS_ADD_OR_UPDATE, method = RequestMethod.POST)
@Retryable(value = { StaleObjectStateException.class,
        HibernateOptimisticLockingFailureException.class }, maxAttempts = 1, backoff = @Backoff(delay = 5000))
public ResponseJson addOrUpdate(@RequestBody User user, HttpServletRequest request) {
    try{
    responseJson.setResponse(adminService.update(userId, user));
    }
    catch (DataIntegrityViolationException | ConstraintViolationException e) {
        ErrorInfo errorInfo = errorCodeHelper.getErrorInfo(CommonConstants.E1071_ERROR_CODE,
                CommonConstants.E1071_ERROR_DESCRIPTION);
        throw new ServiceException(errorInfo);
    }
    return responseJson;
}

This is the important one add on the top of the method

@Retryable(value = { StaleObjectStateException.class,
        HibernateOptimisticLockingFailureException.class }, maxAttempts = 1, backoff = @Backoff(delay = 5000))
like image 192
Faiz Akram Avatar answered Sep 11 '26 03:09

Faiz Akram



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!