为什么选择查询会在 JPA 休眠中触发乐观锁定错误?

问题描述

抛出了一个 ObjectOptimisticLockingFailureException。我检查了日志,发现这个异常是由一个选择查询触发的。这很奇怪。

这是异常堆栈跟踪。

    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

日志显示它在更新 POS 表时遇到了乐观锁错误。这条线 "at net.homecredit.mcd.app.dao.UserRolePosDao.countOtherRoles(UserRolePosDao.java:225)",触发此乐观错误。我检查了这段代码。

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

相关查询如下

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

这只是对另一个 UserRole 表的选择查询,为什么会触发 POS 表的乐观锁错误?

解决方法

如果您仔细查看堆栈跟踪,您会注意到 Hibernate 会“自动刷新”。当您的持久性上下文中有“脏”实体并尝试使用与脏实体的表相交的表进行查询时,就会发生这种情况。如果它不刷新脏条目,您可能会得到错误的结果。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...