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