-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Avoid StaleObjectStateException when deleting entity
PostPosted: Wed Oct 19, 2011 1:58 pm 
Newbie

Joined: Thu Aug 11, 2011 7:31 am
Posts: 3
Hi all,
I have 2 concurrent threads that at the same time enter a (Spring) transaction service.

Using Hibernate, the service method loads some entities, processes those, finds one and deletes it from the DB. The pseudo code is as following:

Code:
@Transactional
public MyEntity getAndDelete(String prop) {
    List<MyEntity> list = (List<MyEntity>)sessionFactory
        .getCurrentSession()
        .createCriteria(MyEntity.class)
        .add( Restrictions.eq("prop", prop) )
        .list();

    // process the list, and find one entity
    MyEntity entity = findEntity(list);
    if (entity != null) {
        sessionFactory.getCurrentSession().delete(entity);
    }
    return entity;
}


In case two threads at the same time pass the same parameter, both will "find" the same entity an both will call delete. One of them will fail throwing an org.hibernate.StaleObjectStateException when the session is close.

I would like that both threads would return the entity, with no exception being thrown. In order to achieve this, I tried to lock (with "select... for update") the entity before deleting it, as following:

Code:
@Transactional
public MyEntity getAndDelete(String prop) {
    List<MyEntity> list = (List<MyEntity>)sessionFactory
        .getCurrentSession()
        .createCriteria(MyEntity.class)
        .add( Restrictions.eq("prop", prop) )
        .list();

    // process the list, and find one entity
    MyEntity entity = findEntity(list);
    if (entity != null) {
        // reload the entity with "select ...for update"
        // to ensure the exception is not thrown
        MyEntity locked = (MyEntity)sessionFactory
            .getCurrentSession()
            .load(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE));
        if (locked != null) {
            sessionFactory.getCurrentSession().delete(locked);
        }
    }
    return entity;
}


I use load() instead of get() since according to the hibernate APIs, get would return the entity if already in the session, while load should re-read it.

If two threads enter at the same time the method above, one of them blocks a the locking stage, and when the first thread closes the transaction, the second is awoken throwing an org.hibernate.StaleObjectStateException. Why?

Why does the locked load not just return null? How could I achieve this?

Is it possible to avoid the exception when the same entity is deleted simultaneously?

Thanks
Andrea


Top
 Profile  
 
 Post subject: Re: Avoid StaleObjectStateException when deleting entity
PostPosted: Thu Oct 20, 2011 2:33 pm 
Newbie

Joined: Thu Aug 11, 2011 7:31 am
Posts: 3
I spent some time investigating this issue and I finally understood what happens.

The PESSIMISTIC_WRITE lock tries to "lock" the Entity that is already loaded in the session, it does not re-read the object from the DB. Debugging the call, I saw that entity == locked returned true (in Java terms). Both variables were pointing to the same instance.

To force hibernate to reload the Entity, it must be removed from the session first.

The following code does the trick:

Code:
@Transactional
public MyEntity getAndDelete(String prop) {
    List<MyEntity> list = (List<MyEntity>)sessionFactory
        .getCurrentSession()
        .createCriteria(MyEntity.class)
        .add( Restrictions.eq("prop", prop) )
        .list();

    // process the list, and find one entity
    MyEntity entity = findEntity(list);
    if (entity != null) {

        // Remove the entity from the session.
        sessionFactory.getCurrentSession().evict(entity);

        // reload the entity with "select ...for update"
        MyEntity locked = (MyEntity)sessionFactory
            .getCurrentSession()
            .get(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE));
        if (locked != null) {
            sessionFactory.getCurrentSession().delete(locked);
        }
    }
    return entity;
}


The PESSIMISTIC_WRITE mut be used with get instead of load, because otherwise a org.hibernate.ObjectNotFoundExceptionwould be thrown.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.