Nice programing

다른 트랜잭션에 의해 행이 업데이트 또는 삭제되었습니다 (또는 저장되지 않은 값 매핑이 잘못됨).

nicepro 2020. 12. 30. 20:21
반응형

다른 트랜잭션에 의해 행이 업데이트 또는 삭제되었습니다 (또는 저장되지 않은 값 매핑이 잘못됨).


웹 서버에서 실행되는 Java 프로젝트가 있습니다. 나는 항상이 예외를 맞았다.

일부 문서를 읽고 비관적 잠금 (또는 낙관적이지만 비관적 일수록 더 낫다는 것을 읽었습니다)이이 예외를 방지하는 가장 좋은 방법이라는 것을 알았습니다.

그러나 나는 그것을 사용하는 방법을 설명하는 명확한 예를 찾을 수 없습니다.

내 방법은 다음과 같습니다.

@Transactional
Public void test(Email email, String Subject){
   getEmailById(String id);
   email.setSubject(Subject);
   updateEmail(email);
}

동안:

  • Email 최대 절전 모드 클래스입니다 (데이터베이스의 테이블이 됨).
  • getEmailById(String id)를 반환하는 함수입니다 email(이 메서드는 주석이 없습니다 @Transctional).
  • updateEmail(email): 이메일을 업데이트하는 방법입니다.

참고 : 나는 갱신, 저장 및 등등을 위해 최대 절전 모드를 사용합니다 (예 : session.getcurrentSession.save(email))

예외 :

ERROR 2011-12-21 15:29:24,910 Could not synchronize database state with session [myScheduler-1]
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [email#21]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy130.generateEmail(Unknown Source)
    at com.admtel.appserver.tasks.EmailSender.run(EmailNotificationSender.java:33)
    at com.admtel.appserver.tasks.EmailSender$$FastClassByCGLIB$$ea0d4fc2.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
    at com.admtel.appserver.tasks.EmailNotificationSender$$EnhancerByCGLIB$$33eb7303.run(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273)
    at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
    at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:204)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)
ERROR 2011-12-21 15:29:24,915 [ exception thrown < EmailNotificationSender.run() > exception message Object of class [Email] with identifier [211]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Email#21] with params ] [myScheduler-1]
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [Email] with identifier [21]: optimistic locking failed; nested exception is 

비관적 잠금은 일반적으로 권장되지 않으며 데이터베이스 쪽의 성능 측면에서 매우 비쌉니다. 언급 한 문제 (코드 부분)는 다음과 같이 명확하지 않습니다.

  • 동시에 여러 스레드에서 코드에 액세스하는 경우.
  • session객체를 어떻게 생성하고 있습니까 (Spring을 사용하고 있는지 확실하지 않음)?

Hibernate Session 객체는 스레드로부터 안전하지 않습니다 . 따라서 동일한 세션에 액세스하고 동일한 데이터베이스 엔터티를 업데이트하려는 여러 스레드가있는 경우 코드는 잠재적으로 이와 같은 오류 상황에 처할 수 있습니다.

그래서 여기서 일어나는 일은 둘 이상의 스레드가 동일한 엔티티를 업데이트하려고 시도하고 하나의 스레드가 성공하고 다음 스레드가 데이터를 커밋 할 때 이미 수정 된 것을보고 StaleObjectStateException.

편집 :

Hibernate에서 비관적 잠금을 사용하는 방법이 있습니다. 이 링크를 확인하십시오 . 그러나이 메커니즘에는 몇 가지 문제가있는 것 같습니다. 그러나 최대 절전 모드 ( HHH-5275 ) 에서 버그를 게시했습니다 . 버그에 언급 된 시나리오는 다음과 같습니다.

두 스레드가 동일한 데이터베이스 레코드를 읽고 있습니다. 이러한 스레드 중 하나는 비관적 잠금을 사용하여 다른 스레드를 차단해야합니다. 그러나 두 스레드 모두 데이터베이스 레코드를 읽을 수 있으므로 테스트가 실패합니다.

이것은 당신이 직면하고있는 것과 매우 가깝습니다. 이것이 작동하지 않으면 이것을 시도하십시오. 내가 생각할 수있는 유일한 방법은 쿼리postgres 데이터베이스 에서 비관적 잠금을 달성 할 수있는 네이티브 SQL 쿼리를 사용하는 것입니다 SELECT FOR UPDATE.


실제로 데이터베이스에서 검색 한 이메일을 사용하는 것이 아니라 매개 변수로받은 이전 사본을 사용하고있는 것으로 보입니다. 행의 버전 제어에 사용되는 내용이 이전 버전을 검색 한 시점과 업데이트를 수행하는 시점 사이에 변경되었습니다.

코드가 다음과 같이 보이기를 원할 것입니다.

@Transactional
Public void test(String id, String subject){
   Email email = getEmailById(id);
   email.setSubject(subject);
   updateEmail(email);
}

나는 이것이 오래된 질문이라는 것을 알고 있지만 우리 중 일부는 여전히 그것을 치고 하늘을 헤매는 방법을 봅니다. 여기 제가 직면 한 한 가지 문제가 있습니다.

데이터를 폴링하고 처리를 위해 핸들러에 제공하는 큐 관리자가 있습니다. 동일한 이벤트를 다시 선택하지 않도록 큐 관리자는 데이터베이스의 레코드를 LOCKED 상태로 잠급니다.

   void poll() {
        record = dao.getLockedEntity();
        queue(record);
     }

이 메소드는 트랜잭션이 아니었지만 dao.getLockedEntity()'REQUIRED'로 트랜잭션되었습니다.

모든 것이 좋고 이동 중일 때 생산 몇 달 후 낙관적 잠금 예외로 실패했습니다.

많은 디버깅과 세부 사항을 확인한 후에 누군가가 이와 같이 코드를 변경했음을 알 수 있습니다.

@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
void poll() {
        record = dao.getLockedEntity();
        queue(record);              
     }

따라서 레코드는 dao.getLockedEntity ()의 트랜잭션이 커밋되기 전에 대기열에 추가되었고 (동일한 poll 메서드 트랜잭션 사용) poll () 메서드 트랜잭션이 가져올 때까지 처리기 (다른 스레드)에 의해 객체가 변경되었습니다. 약속했다.

문제를 해결했으며 이제 괜찮아 보입니다.

낙관적 잠금 예외는 혼란스럽고 디버깅하기 어렵 기 때문에 공유하려고 생각했습니다. 어떤 사람은 내 경험으로부터 혜택을받을 수 있습니다.

감사합니다 Lyju


이 예외는 아마도 낙관적 잠금 (또는 코드의 버그)으로 인해 발생합니다. 당신은 아마 모르게 그것을 사용하고있을 것입니다. 그리고 의사 코드 (문제를 진단 할 수 있도록 실제 코드로 대체해야 함)가 잘못되었습니다. Hibernate는 연결된 엔티티에 대한 모든 수정 사항을 자동으로 저장합니다. 첨부 된 엔터티에 대해 update, merge 또는 saveOrUpdate를 호출해서는 안됩니다. 그냥 해

Email email = session.get(emailId);
email.setSubject(subject);

업데이트를 호출 할 필요가 없습니다. Hibernate는 트랜잭션을 커밋하기 전에 변경 사항을 자동으로 플러시합니다.


내 프로젝트 에서이 문제가 발생했습니다.

After I implemented optimistic locking, I got the same exception. My mistake was that I did not remove the setter of the field that became the @Version. As the setter was being called in java space, the value of the field did not match the one generated by the DB anymore. So basically the version fields did not match anymore. At that point any modification on the entity resulted in:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

I am using H2 in memory DB and Hibernate.


check if the object exists or not in DB, if it exists get the object and refresh it:

if (getEntityManager().contains(instance)) {
    getEntityManager().refresh(instance);
    return instance;
}

if it fails the above if condition... find the Object with Id in DB, do the operation which you need, in this case exactly changes will reflects.

if (....) {
    } else if (null != identity) {
        E dbInstance = (E) getEntityManager().find(instance.getClass(), identity);
        return dbInstance;
    }

This error occurred for me when I was trying to update the same row from 2 different sessions. I updated a field in one browser while a second was open and had already stored the original object in its session. When I attempted to update from this second "stale" session I get the stale object error. In order to correct this I refetch my object to be updated from the database before I set the value to be updated, then save it as normal.


I had the experienced the same issue in different context of my project and there are different scenarios like

 - object is accessed from various source like (server side and client)
 - without any interval accessing the same object from a different place

In the first case

When I issue a server cal, before save the that object their one call from js and trying to save and another place, I got like, js call is going two, three times(I thing that call binding thing cause the issue)

I solved by

e.preventDefault()

The second case,

object.lock()

I had the same problem and in my case the problem was missing and/or incorrect equals implementation on some types of fields in the entity object. At commit time, Hibernate checks ALL entities loaded in the session to check if they are dirty. If any of the entities are dirty, hibernate tries to persist them - no matter of the fact that the actual object that is requested a save operation is not related to the other entities.

Entity dirtiness is done by comparing every property of given object (with their equals methods) or UserType.equals if property has an associated org.Hibernate.UserType.

Another thing that surprised me was, in my transaction (using Spring annotation @Transactional), I was dealing with a single entity. Hibernate was complaining about some random entity that's unrelated to that entity being saved. What I realized is there is an outermost transaction we create at REST controller level, so the scope of the session is too big and hence all objects ever loaded as part of request processing get checked for dirtiness.

Hope this helps someone, some day.

Thanks Rags


Just in case someone checked this thread and had the same issue as mine...

Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

I'm using NHibernate, I receive same error, during creating an object...

I was passing the key manually, and also specified a GUID generator in mapping, so hibernate generate same exact error for me, so once I removed the GUID, and left the field empty, everything went just fine.

this answer may not help you, but will help someone like me, who just your thread becasue of same error


I also ran into this error when attempting to update an existing row after creating a new one, and spent ages scratching my head, digging through transaction and version logic, until I realised that I had used the wrong type for one of my primary key columns.

I used LocalDate when I should have been using LocalDateTime – I think this was causing hibernate to not be able to distinguish entities, leading to this error.

After changing the key to be a LocalDateTime, the error went away. Also, updating individual rows began to work as well – previously it would fail to find a row for updating, and testing this separate issue was actually what led me to my conclusions regarding the primary key mapping.


Don't set an Id to the object you are saving as the Id will be autogenerated


I had the same problem in my grails project. The Bug was, that i overwrite the getter method of a collection field. This returned always a new version of the collection in other thread.

class Entity {
    List collection

    List getCollection() {
        return collection.unique()
    }
}

The solution was to rename the getter method:

class Entity {
    List collection

    List getUniqueCollection() {
        return collection.unique()
    }
}

I've also receiving such an exception, but the problem was in my Entity identifier. I am using UUID and there are some problems in the way Spring works with them. So I just added this line to my entity identifier and it began working:

@Column(columnDefinition = "BINARY(16)")

Here you can find a little bit more information.


In order to prevent StaleObjectStateException, in your hbm file write below code:

<timestamp name="lstUpdTstamp" column="LST_UPD_TSTAMP" source="db"/>

I have also faced this issue. First check your imports, when you use session,transaction it should be org.hibernate and remove @Transactinal annotation. and most important in Entity class if you have used @GeneratedValue(strategy=GenerationType.AUTO) or anyother then at the time of model object creation/entity object creation should not create id. final conclusion is if you want pass id filed i.e PK then remove @GeneratedValue from entity class.


I had this problem in one of my apps, now, I know this is an old thread but here is my solution; I figured out by looking at the data inside the debugger that JVM actually didn't load it properly when Hibernate was trying to update the database (that is actually done in a different thread), so I added the keyword "volatile" to every field of the entities. It has some performance issues to do that but rather that than Heavy objects beeing thrown around...

ReferenceURL : https://stackoverflow.com/questions/8645694/row-was-updated-or-deleted-by-another-transaction-or-unsaved-value-mapping-was

반응형