У меня есть 2 одновременных потока, которые одновременно входят в сервис транзакций (Spring).
Используя Hibernate, метод сервиса загружает некоторые объекты, обрабатывает их, находит один и удаляет его из БД.Псевдокод выглядит следующим образом:
@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;
}
Если два потока одновременно передают один и тот же параметр, оба «найдут» один и тот же объект, и оба вызовут delete
.Один из них не сможет сгенерировать org.hibernate.StaleObjectStateException
, когда сессия будет закрыта.
Мне бы хотелось, чтобы оба потока возвращали объект, без исключения.Чтобы добиться этого, я попытался заблокировать (с помощью «select ... for update») объект перед его удалением, как показано ниже:
@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;
}
Я использую load()
вместо get()
, так каксогласно hibernate API, get возвращает объект, если он уже находится в сеансе, а load должен перечитать его.
Если два потока одновременно входят в метод, описанный выше, один из них блокирует блокировкуэтап, и когда первый поток закрывает транзакцию, второй пробуждается, выбрасывая org.hibernate.StaleObjectStateException
.Почему?
Почему заблокированная загрузка не просто возвращает ноль?Как мне этого добиться?