user121196
user121196

Reputation: 30990

How to deal with "StaleObjectStateException" in Hibernate with pessimistic locking?

Edit: It turns out in this case since I was using "version" annotation, so I'm using optimistic locking, not pessimistic locking.
If I remove version and hence disable optimistic locking. The pessimistic locking takes over and performance degraded significantly.
So I guess I have to live with optimistic locking and occasional exceptions. Is there a better solution?

Original: I currently have multiple tomcat instances in an apache 2.2 load balancer via ajp. The backend system is hibernate. The system serves multiple users and requests, for request it deducts one credit from the user's account.

I use hibernate's pessimistic locking for managing credit deductions.
I kept getting the following from time to time on user account object in the logs:

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

Code

private boolean decUserAccountQuota(UserAccount userAccount, int creditDec) {

        if(userAccount.getBalance() <1) return false;
        String userName = userAccount.getUsername();
        Manager manager = getManager(userName);


        try{
            manager.beginTransaction();
            manager.refresh(userAccount, LockMode.UPGRADE); //this uses pessimistic locking, this calls sessionFactory.getCurrentSession().refresh();   
            userAccount.setBalance(userAccount.getBalance()-creditDec);
            manager.commitTransaction(); //this causes the Exception
        }catch(Exception exp){
            exp.printStackTrace();
            manager.rollbackTransaction();
            return false;
        }finally{
            manager.closeSession();
        }
        return true;
    }

Questions:

  1. How do I prevent this exception from happening. What happens here is more than one threads tries to update the same entity, one thread succeeds and hence, when the next thread goes to commit the data, it sees that its already been modified and ends up throwing StaleObjectStateException. But if I'm already using pessimistic locking, how can the exception still happen?
  2. Are there any better ways in terms of performance and integrity in managing the user account credit system?

Upvotes: 1

Views: 2303

Answers (2)

Manjunath
Manjunath

Reputation: 1685

In your code the below line is not taking a pessimistic lock, although you are specifying it to, because your database doesnot support SELECT.. FOR UPDATE .

manager.refresh(userAccount, LockMode.UPGRADE); 

For this reason hibernate uses an alternate mode of locking called LockMode.READ for userAccount which is optimistic locking based on the @Version attribute in your entity.

I searched to see if there is any alternative way of forcing hibernate to take pessimistic locking for your scenario but it looks like its not possbile.

Coming back to your question of how to minimize or avoid the StaleObjectStateException here are my thoughts:-

Synchronize on userAccount object . Though this seems to affect performance but this would happen only if the same user places too many concurrent requests. Also giving up a little on performance to make sure that user need not be thrown an exception and made to retry would be ideal scenario.

If you have found any alternative solution please do share it .

Upvotes: 0

Varun Phadnis
Varun Phadnis

Reputation: 681

Your Hibernate entity is either using @Version annotation or defines <version> in your XML Hibernate mapping. This will enable the optimistic locking provided by Hibernate.

If you are explicitly using pessimistic locking as you described, removing the above should fix your problem.

More info here

Upvotes: 0

Related Questions