Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

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.

Do you have any @PostRead code in Keyword to update the content of the entity, or is there any special logic in Dao that will update the Keyword entity after you retrieved? – Adrian Shum Jan 21, 2013 at 9:09 Yup, my bad. There is a part to update the keyword. I marked your question as correct. I am able to refactor the code and remove unnecessary update, thus reduce the staleobject exception. – Khue Vu Jan 21, 2013 at 9:38

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.

Thanks for your answer. I am trying to identify the cause to see if I can avoid it. Currently if I do a retry, the run time end up lasting the same even if I have more threads because of large number of retries. – Khue Vu Jan 21, 2013 at 7:29 I have tried logging out the session.isDirty() checked between all DB operation before this exception-thrown q.list(). But interestingly, none of the previous operations cause the session to be dirty (They are all read). – Khue Vu Jan 21, 2013 at 9:01 I will suggest you to make use of jdbcdslog and dump out the actual update statement & binded variables. That may give you hints on which entity is causing the problem. – Adrian Shum Jan 21, 2013 at 9:06 I marked your question as correct. It pointed me to understanding that query.list() result in a auto flush by hibernate prior to the select statement. I have thought that flush() by default is at manual mode. – Khue Vu Jan 21, 2013 at 9:36 @AdrianShum, StaleObjectException does not exist in Hibernate. StaleStateException and StaleObjectStateException do, they are related(see below), and difference is important. Other than that, great answer. – igor.zh Feb 10, 2016 at 20:40 True, StaleStateException happens also when (from API doc) "we try delete or update a row that does not exist" But the question was about StaleObjectStateException, not StaleStateException. They are related, though. First, StaleStateException is thrown, then, if "versioned" entity is being updated, it gets caught and then StaleObjectStateException is finally thrown. So your statement is correct, but, strictly speaking, unrelated to the original question. – igor.zh Feb 10, 2016 at 20:25

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.

I am not OP, but from my understanding, optimistic concurrency checking occurs when you actually do an update and when flushing that to DB. Simply query will not trigger the optimistic concurrency check. – Adrian Shum Jan 21, 2013 at 6:15 @Sublin: thanks for your answer. I don't have problem retry the operation but I want to know why a read op. can returned a StaleObject. I don't share Session object between thread, so the read operation should read the object from DB, which should not lead to any out of date object right ? I will update my answer with the data access code – Khue Vu Jan 21, 2013 at 6:17 The stale object is not caused by the read directly. Please refer to my answer to know why a read can indirectly cause optimistic concurrency checking – Adrian Shum Jan 21, 2013 at 7:21

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.