StaleObjectStateException with Hibernate in read operation? - java

I am using Hibernate in a listener of Spring DefaultMessageLisenerContainer.
When I let the listener run with multiple threads, I often encounter this StaleStateException for a read only operation:
Query q = session.createQuery("SELECT k FROM Keyword k WHERE k.name = :name").setParameter("name", keywordName);
List<Keyword> kws = q.list()
The exception is thrown at q.list():
optimistic locking failed; nested exception is
org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.aurora.common.model.Keyword#7550]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1934)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2578)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2478)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:114)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:267)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:259)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:179)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:64)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1251)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
It is really strange as read operation should read a fresh copy from DB rather than check for a version conflict and throw StaleObjectStateException.
The name attribute is not the primary key of Keyword object.
UPDATE:
My data access code: I am using Spring's HibernateTransactionManager which support thread-bound Hibernate session. The Hibernate session is retrieved through SessionFactory.getCurrentSession() method.
Each transaction wrap around a invoke of listener by assigning the HibernateTransactionManager to MessageListenerContainer:
<jms:listener-container connection-factory="connectionFactory" concurrency="3-3" prefetch="6" transaction-manager="transactionManager">
<jms:listener destination="${requests}" response-destination="${replies}" ref="chunkHandler" method="handleChunk" />
</jms:listener-container>
UPDATE :
As in the suggested answer, there might be other operations causing staleObjectStateException.
I have tried logging out the Session.isDirty(), for all other operations prior to that. They are all read operation. Interestingly, the session is actually marked as dirty after the keyword select by name operation. The actual code is something like this:
for (String n : keywordNames) {
Keyword k = keywordDao.getKeywordByName(n);
}
The session is dirty after the first iteration. (KeywordDao.getKeywordByName implmentation is as above).
Any idea ? Thanks,
Khue.

I believe other answers given are not correct. Accessing row does not exist does not give StaleObjectStateException, and simply query an entity is not going to trigger optimistic lock for that entity too.
Further inspection on the stack trace will give some hints for the cause:
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102) When you are calling query.list()
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175) Hibernate will determine if auto flush of the session is required. By some reason Hibernate believe auto flush is required. (Probably due to you have previously done update on some Keyword entity in the same session, or other entities... that's something I cannot tell honestly)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805) Then Hibernate will flush all the changes in the session to DB. And, the problem of StaleObjectStateException occurs here, which means Optimistic Concurrency check failure. The optimistic concurrency check failure MAY or MAY NOT relates to Keyword entity (coz it is simply flushing all updated entities in session to DB). However, in your case, it is actually related to Keyword entity ( Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.ncs.singtel.aurora.common.model.Keyword#7550])
Please verify what is the cause for the optimistic concurrency failure. Normally we simply rethrow the optimistic concurrency exception to caller and let caller decide if they want to invoke the function again. However it all depends on your design.

The stalestateException occurs when we try to access a row that doesn't exist. check your keyword.getName() to see what it returns.

Some other transactions could be updating Keyword entity at the same time as you read and your read operation could result in Stale objects.
This is optimistic locking. You can consider pessismistic locking , but it will seriously affect the performance.
I would suggest catch StaleObjectStateException and try to read again.

Related

JPA use flush to trigger exception and halt execution

In a recent task, after I created an object I flushed the result to the database. The database table had a unique constraint, meaning that if I tried to flush the same record for the second time, I would get a ConstraintViolationException. A sample snippet is shown below:
createEntityAndFlush(result);
sendAsyncRequestToThirdSystem(param);
The code for the createEntityAndFlush:
private T createEntityAndFlush(final T entity) throws ServiceException {
log.debug("Persisting {}", entity.getClass().getSimpleName());
getEntityManager().persist(entity);
getEntityManager().flush();
return entity;
}
The reason I used flush was that I wanted to make sure that a ConstraintViolationException would be thrown prior to finishing the transaction and thus calling the sendAsyncRequestToThirdSystem. But that was not the case, since sendAsyncRequestToThirdSystem was called after the exception was thrown.
To test the code in racing conditions, I used the ManagedExecutorService and created two runnable tasks (Future<?> submit(Runnable task)) to replicate the incoming request.
Eventually the problem was solved by trying performing a lock on a new table for each unique request id, but I would like to know where I was wrong in my first approach (ex. wrong use of flash, ManagedExecutorService was responsible for awkward behaviour). Thanks in advance!
The issue is that while flush() does flush the changes into the database, the transaction is still open, and the unique constraint will be checked when the transaction is committed (this may depend on the database, but at least with Postgres and any MVCC using DB).
So you will need to make sure that createEntityAndFlush(result); runs in its own transaction, possibly with a #Transactional(propagation = Propagation.REQUIRES_NEW) (or equivalent, if not using Spring) to see if the unique index is violated.

How to handle Optimistic Locked item quantity update with concurrent users?

I have method which updates to Item quantity.
MyEntity has #Version annoted version property as long.
There is an item list endpoint /items
Also there is an item update endpoint /item/update (consider as product stock, buying an item)
So N concurrent users want's to update same item.
But there throws org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 that exception. While updating.
And also at this time, /items endpoint couldn't return data. Or waits user to return with too much latency.(If updating users count too much at this time it also gets an exception timeout).
So How can I handle that situation without any missing? (Can be good implementation )
Unfortunately, JPA/Hibernate does not play nice with batch inserts when there is contention: whenever any exception is thrown in the context of a Hibernate session, you're out of luck.
See 13.2.3. Exception handling of: https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch13.html#transactions-optimistic
Specifically:
No exception thrown by Hibernate can be treated as recoverable. Ensure
that the Session will be closed by calling close() in a finally block.
In the past I have had to migrate JPA code to QueryDSL or fall back to raw SQL and JdbcTemplate (something like How to do multiple inserts in database using spring JDBC Template batch?).

How can I isolate entity validation in order to prevent transaction rollback of a whole bulk (persist) transaction?

I have entities with validation annotations such as #NotNull. I don't know how to prevent a container managed transaction from rolling back in the case of a ConstraintViolationException in a bulk persist operation such as:
public void persistAll(List<TheEntity> entities) throws Exception{
for(TheEntity ent : entities){
em.persist(ent);
}
}
Wrapping the persist operation in a try-catch would not solve the issue because even by catching the Constraint exception, the transaction would be flagged for rollback (none of the other "validated" entities would be persisted). I could isolate the transaction per entity to persist, but I think this will impact a lot on performance (not sure about this, I'm using eclipselink with batch JDBC optimization).
I know that the behavior of the ContraintValidationException is working as the JPA spec mandates (flagging rollback), but I'm not sure if I'm understanding how the eclipselink batch optimization works (does the bulk opeations need to be in a single transaction?).
Thanks for the interest.
Regards.
EDIT: Welp, eclipelink docs state that "Batch writing can improve database performance by sending groups of INSERT, UPDATE, and DELETE statements to the database in a single transaction, rather than individually.", so yes, it needs to be done in a single transaction.
EDIT: I could also inject a constraint validator from the context and disable the JPA validator on persistence.xml, so I can validate the entity list prior the JPA PrePersist operation. However, this will affect other entities that won't need bulk operations on them but still need validation. Ahh! almost there.
You can do manual validation and just skip the entities that are not valid, like this:
// in the class inject validator
#Resource
Validator validator;
...
for(TheEntity ent : entities){
if( validator.validate(ent).size() == 0) {
// valid - persist
em.persist(ent);
} else {
// invalid - skip
}
}

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

I have a java project that runs on a webserver. I always hit this exception.
I read some documentation and found that pessimistic locking (or optimistic, but I read that pessimistic is better) is the best way to prevent this exception.
But I couldn't find any clear example that explains how to use it.
My method is like:
#Transactional
public void test(Email email, String subject) {
getEmailById(String id);
email.setSubject(subject);
updateEmail(email);
}
while:
Email is a Hibernate class (it will be a table in the database)
getEmailById(String id) is a function that returns an email (this method is not annotated with #Transactional)
updateEmail(email): is a method that updates the email.
Note: I use Hibernate for save, update & so on (example: session.getcurrentSession.save(email))
The exception:
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
Pessimistic locking is generally not recommended and it's very costly in terms of performance on database side. The problem that you have mentioned (the code part) a few things are not clear such as:
If your code is being accessed by multiple threads at the same time.
How are you creating session object (not sure if you are using Spring)?
Hibernate Session objects are NOT thread-safe. So if there are multiple threads accessing the same session and trying to update the same database entity, your code can potentially end up in an error situation like this.
So what happens here is that more than one threads try to update the same entity, one thread succeeds and when the next thread goes to commit the data, it sees that its already been modified and ends up throwing StaleObjectStateException.
EDIT:
There is a way to use Pessimistic Locking in Hibernate. Check out this link. But there seems to be some issue with this mechanism. I came across posting a bug in hibernate (HHH-5275), however. The scenario mentioned in the bug is as follows:
Two threads are reading the same database record; one of those threads
should use pessimistic locking thereby blocking the other thread. But
both threads can read the database record causing the test to fail.
This is very close to what you are facing. Please try this if this does not work, the only way I can think of is using Native SQL queries where you can achieve pessimistic locking in postgres database with SELECT FOR UPDATE query.
We have a queue manager that polls data and gives it to handlers for processing. To avoid picking up the same events again, the queue manager locks the record in the database with a LOCKED state.
void poll() {
record = dao.getLockedEntity();
queue(record);
}
this method wasn't transactional but dao.getLockedEntity() was transactional with REQUIRED.
All good and on the road, after few months in production, it failed with an optimistic locking exception.
After lots of debugging and checking in details we could find out that some one has changed the code like this:
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
void poll() {
record = dao.getLockedEntity();
queue(record);
}
So the record was queued even before the transaction in dao.getLockedEntity() gets committed (it uses the same transaction of poll method) and the object was changed underneath by the handlers (different threads) by the time the poll() method transaction gets committed.
We fixed the issue and it looks good now. I thought of sharing it because optimistic lock exceptions can be confusing and are difficult to debug.
It doesn't appear that you are actually using the email that you retrieve from the database, but an older copy that you get as a parameter. Whatever is being used for version control on the row has changed between when the previous version was retrieved and when you are doing the update.
You probably want your code to look more like:
#Transactional
public void test(String id, String subject) {
Email email = getEmailById(id);
email.setSubject(subject);
updateEmail(email);
}
I had the this problem on my project.
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.
This exception is probably caused by optimistic locking (or by a bug in your code). You're probably using it without knowing. And your pseudo-code (which should be replaced by real code to be able to diagnose the problem) is wrong. Hibernate saves all the modifications done to attached entities automatically. You shouldn't ever call update, merge or saveOrUpdate on an attached entity. Just do
Email email = session.get(emailId);
email.setSubject(subject);
No need to call update. Hibernate will flush the changes automatically before committing the transaction.
I had problems with the same error on more than one Spring project.
For me a general solution was, to split my service Method, that each INSERT, UPDATE and DELETE action got an own Method with #Transactional.
I think this problem relates to the internal Spring managment, where database interactions are executed at the end of the method and, in my oppinion, this is the point, where the Exception is triggered.
Update and further solutions.
My problem was that I queried an #Entity Class object and changed a value without saving it because, strictly speaking, it was updated by another query (outside the scope), but since this object was internal to the sessions in a map now it had a different value, the next request was blocked with this message.
So I created a variable and saved the new values there and then sent them to the UpdateQuery, so Hibernate did not register any unsaved changes and the line could be updated.
Hibernate seems to send a lock statement to the database every time an object of the #Entity class is changed or at least to spear the line locally by primary key.
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 received same error, during creation of an object...
I was passing the key manually, and also specified a GUID generator in mapping...
And 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 viewed your thread because of same error
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;
}
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 was 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.
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 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 issue and for me, the case was a bit different, I was using Spring Data JPA and the entity class was annotated with #Entity and #Table annotation, and on the ID field I had #Id annotation but I missed adding #GeneratedValue since the DB table had the auto-increment identity field.
But the issue happened when we were doing bulk insert for these entities and since there was no Generator specified on the ID field, all entities had the default value (0) as the id field. and Started giving this exception:
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) :[dao.entity.OrderAssortmentReportEntity#0]
We added the #GeneratedValue(strategy = GenerationType.IDENTITY) along with #Id and it worked.
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()
}
}
if you are using Hibernate with Dropwizard,
this could happen if you are using id as autogenerated.
Remove #GeneratedValue
enter image description here
1. Reason for error
There is another situation: Error data.
#Column(name = "ID", unique = true, nullable = false, length = 32)
private String id;
One of the data is blank or null. When the front-end value is saved,
{
"cause": {
"cause": null,
"message": "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx#]"
},
"message": "Object of class [com.xxx] with identifier []: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx#]"
}
2 .solving
Delete error data.
This problem happens if you are trying to update an object which is the same instance but retrieved from different List/Hash/ and so on, right from a different sub-thread.
In order to prevent StaleObjectStateException, in your hbm file write below code:
<timestamp name="lstUpdTstamp" column="LST_UPD_TSTAMP" source="db"/>
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 any other 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.
Hibernate uses versioning to know that modified object you had is older than one which is currently persisted.
so when you update an entity don't include version in json body if its unwanted. just annotate with #Version in version column.
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...

Does hibernate a default optimistic locking for detached objects?

I have an application that does:
void deleteObj(id){
MyObj obj = getObjById(id);
if (obj == null) {
throw new CustomException("doesn't exists");
}
em.remove(obj);//em is a javax.persistence.EntityManager
}
I haven't explicitly configure optimistic locking with version field.However, if two request are running in parallel, trying to delete the same object, then I get sometimes an HibernateOptimisticLockingFailureException and other times the "CustomException".
Is it normal to get HibernateOptimisticLockingFailureException without explicitly setting optimistic locking ? Does hibernate a default optimistic locking for detached objects ?
What are you doing to handle this HibernateOptimisticLockingFailureException ? Retry or inform to the user with a default message like "server busy" ?
First of all, HibernateOptimisticLockingFailureException is a result of Spring's persistence exception translation mechanism. It's thrown in response to StaleStateException, whose javadoc says:
Thrown when a version number or timestamp check failed, indicating that the Session contained stale data (when using long transactions with versioning). Also occurs if we try delete or update a row that does not exist.
From the common sense, optimistic lock exception occurs when data modification statement returns unexpected number of affected rows. It may be caused by mismatch of version value as well as by absence of the row at all.
To make sure that entity was actually removed you can try to flush the context by em.flush() right after removing and catch an exception thrown by it (note that it should be subclass of PersistenceException having StaleStateException as a cause).

Categories