How to detect transaction conflicts with Hibernate?

3.4k Views Asked by At

I am using Hibernate 2.6 with hibernate-entitymanager. I am trying to catch and handle situations when 2 transactions conflict on an object. Here is what happens:

Two threads are updating a single object wich has a @Version field. The thread which looses commit race logs StaleObjectStateException on flush. The exception is not thrown, it is just logged. I guess the transaction is marked rollback-only at that moment.

After that when the thread tries to perform commit it fails with RollbackException. I did not find a way to find out in the code why the transaction is rolled back.

Is there a way to catch and handle such situations in the code? Basicly I want to catch StaleObjectStateException, but the problem is - it is not thrown.

UPDATE: The thing I am trying to accomplish from the birds eye view is the following:

I have a J2EE app running under JBoss. It has some internal timer-invoked services and the ones invoked from the UI. It also has one critical entity. I need to ensure that different threads can not update objects of that entity class simultaniously because it may result in data inconsistency. This is why I am implementing optimistic locking.

When an optimistick lock problem occurs I am trying to handle this situation generally. I want to catch it at the very high level and show a valid user message (in my case the highest level is ExceptionMapper for RestEasy). The problem is - when I catch RollbackException - it is already too late.

I do not flush manually. Most of my EJBs use CMT and the sessions are flushed automatically.

2

There are 2 best solutions below

2
On

Take a look at NHProf. It can help you with all kind of things related to Nhibernate profiling.

3
On

Artem,

Can you briefly explain what is it you're trying to achieve? Bird's eye view, obviously - like is this (invoked from) UI code? Or is it server-side process (so you'd have direct controls over threads)? The reason I ask is I get a (perhaps incorrect) feeling from this and your other related questions that you're trying to use optimistic locking for something it wasn't designed for and that's what's causing all the trouble.

As far as StaleObjectStateException goes, it is most definitely thrown from both DefaultFlushEventListener and AutoFlushEventListener that handle explicit / implicit flushes. Are you invoking flush() manually? If not, perhaps the exception is being caught / logged by wrapper code around auto flushing (Spring? TransactionManager? EntityManager?)

Update

Thank you for clarifying the question. I'm still a bit unclear about whether you want to prevent multiple threads from concurrently modifying1 the same entity or prevent multiple users from trying to concurrently edit it.

Former scenario can be handled via optimistic locking; however without explicit flush() it becomes non-deterministic (thread that made the modification first may not be flushed / committed first). See my answer to this question for more details. Another problem with automatic flush is what you're currently experiencing - failed version check is not discovered until flush and, if that flush coincides with attempt to commit transaction, exception thrown is RollbackException. Either way, entire transaction is rolled back.

Latter scenario (preventing users from editing) can NOT be handled by optimistic locking. You will need to implement pessimistic locking instead - either at the database or application level. In other words, the process is:

  1. user wants to edit entity
  2. check if lock exists on entity
  3. YES - prohibit editing (allow read-only view?)
  4. NO - lock entity, allow editing
  5. commit (cancel) changes; release lock

Be sure to expire existing locks after a certain period of user inactivity if you go with this approach.


concurrently modifying1 is not really accurate in this case (that's what transactions are for); we're talking about preventing one thread from overwriting edits of another based on older version.