views:

602

answers:

2

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.

A: 

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

Johannes Rudolph
I use Hibernate for Java, not NHibernate. And I the question is not about profiling, it is about exceptional situations handling
artemb
a profiler will help you detecting such problems and narrow them down. NHProf -does- support Hibernate aswell.
Johannes Rudolph
+2  A: 

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.

ChssPly76
Thank you. I have updated the question with the info you asked.
artemb
I am trying to prevent multiple threads from concurrently modifying. Exactly preventing one thread from overwriting edits of another based on older version.So your advice is flushing manually? Only that way I can achieve my goal?
artemb
Consider this: threads T1 and T2 read your entity at the same time, modify it, then T1 saves it and T2 saves it. With auto flush you don't know whose changes (T2 or T1) are going to be flushed first. Furthermore, until changes are flushed (and / or transactions committed depending on isolation) the other thread won't even know that object version has changed so you only get the error back when transaction fails and is being rolled back. If you care about change order or want to get exception early, you have to flush manually.
ChssPly76