Search code examples
hibernatejpaoptimistic-locking

Why a select query trigger optimistic locking error in JPA hibernate?


There is one ObjectOptimisticLockingFailureException thrown. I check the log and find this exception is triggered by a select query. This is werid.

Here is the exception stack trace.

    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:317)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy117.countOtherRoles(Unknown Source)
    at net.homecredit.mcd.app.service.UserRolePosService.countOtherRoles(UserRolePosService.java:315)
    at net.homecredit.mcd.app.service.UserRolePosService.canRemoveEmployer(UserRolePosService.java:375)
    at net.homecredit.mcd.app.service.UserRolePosService.deactivate(UserRolePosService.java:339)
    at net.homecredit.mcd.app.service.UserRolePosService.fillRoleData(UserRolePosService.java:241)
    at net.homecredit.mcd.app.service.UserRolePosService.fillRoleData(UserRolePosService.java:199)
    at net.homecredit.mcd.app.service.UserRolePosService.fillRoleData(UserRolePosService.java:180)
    at net.homecredit.mcd.app.service.pos.PosBusinessService.saveUserRole(PosBusinessService.java:224)
    at net.homecredit.mcd.app.service.pos.PosBusinessService.updatePos(PosBusinessService.java:121)
    at net.homecredit.mcd.app.service.pos.PosBusinessService$$FastClassBySpringCGLIB$$ad825d06.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at net.homecredit.mcd.common.security.ForbiddingSecurityInterceptor.invoke(ForbiddingSecurityInterceptor.java:17)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at net.homecredit.mcd.common.aop.AuditInterceptor.invoke(AuditInterceptor.java:21)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
    at net.homecredit.mcd.app.service.pos.PosBusinessService$$EnhancerBySpringCGLIB$$7cfba5de.updatePos(<generated>)
    at net.homecredit.mcd.app.service.homesis.kafka.HsisKafkaConsumer.updatePos(HsisKafkaConsumer.java:122)
    at net.homecredit.mcd.app.service.homesis.kafka.HsisKafkaConsumer.updatePos(HsisKafkaConsumer.java:97)
    at net.homecredit.mcd.app.service.homesis.kafka.HsisKafkaConsumer.consumePos(HsisKafkaConsumer.java:73)
    at sun.reflect.GeneratedMethodAccessor871.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:180)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:112)
    at org.springframework.kafka.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:48)
    at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:174)
    at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:82)
    at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:50)
    at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:975)
    at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:959)
    at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:901)
    at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:786)
    at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:656)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [net.homecredit.mcd.app.domain.Pos#2307536273]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2400)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3202)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3077)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3457)
    at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
    at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:50)
    at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1398)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1483)
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1445)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1414)
    at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1463)
    at net.homecredit.mcd.app.dao.UserRolePosDao.countOtherRoles(UserRolePosDao.java:225)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    ... 47 common frames omitted

The log shows it encounters a optimistic lock error while updating the POS table. This line "at net.homecredit.mcd.app.dao.UserRolePosDao.countOtherRoles(UserRolePosDao.java:225)", triggers this optimistic error. I check this code.

    public Integer countOtherRoles(User user, UserRolePos userRole) {
        return em.createNamedQuery(UserRolePos.COUNT_OTHER_ROLE_FOR_USER, Long.class)
                .setParameter("user", user)
                .setParameter("userRole", userRole)
                .getSingleResult().intValue();
    }

Related query is as below

 @NamedQuery(name = UserRolePos.COUNT_OTHER_ROLE_FOR_USER,
        query = "SELECT COUNT(urp) FROM UserRolePos urp " +
                "WHERE urp.user = :user " +
                "AND urp != :userRole "
        )

It is just a select query with another UserRole table, why this trigger the optimistic lock error with POS table?


Solution

  • If you look closely in the stacktrace, you will notice that Hibernate does "auto-flushing". This happens when you have "dirty" entities in your persistence context and try to do a query that uses tables, which intersect with the tables of the dirty entities. If it wouldn't flush the dirty entries, you might get wrong results.