首页 > 解决方案 > 在春季获取 org.springframework.orm.ObjectOptimisticLockingFailureException

问题描述

我创建了一个函数,它试图在启用了版本的数据库中保存一些东西,因此它可以给出乐观锁异常。我捕捉到异常并在从数据库中获取后重试。我也每次都使用@transactional 注释来启动一个新事务。但是即使在调用我的保存函数之前,我也会收到 org.springframework.orm.ObjectOptimisticLockingFailureException 。

@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);
            }
        }

    }
}

我得到的详细例外是:

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]

executionBuilder.save 在内部调用 dao.save ,这在堆栈跟踪中是看不到的。为什么甚至在调用 save 之前就抛出此异常。当调用 save 时,异常被捕获并且一切正常。但偶尔会在没有保存的情况下抛出此异常,因此不会被捕获。

我的直觉说这与@Transactional 有关。

有关信息,我的应用程序是多线程的,并且许多线程可能会同时修改相同的数据。

任何帮助,将不胜感激。

标签: javaspringhibernatespring-bootjpa

解决方案


您可以使用控制器内的弹簧重试选项处理乐观锁。下载此课程

import org.springframework.retry.annotation.Retryable;

例如

@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;
}

这是方法顶部的重要添加

@Retryable(value = { StaleObjectStateException.class,
        HibernateOptimisticLockingFailureException.class }, maxAttempts = 1, backoff = @Backoff(delay = 5000))

推荐阅读