Я работаю над многопоточным Java-приложением, использующим Hibernate. Мы получаем org.hibernate.StaleObjectStateException
, потому что у нас есть оптимистическая блокировка, а некий объект (назовем его Pojo
) обновляется из многих модулей приложения, которые могут столкнуться и столкнулись в условиях гонки (я не проектировал заявление). Проблемный блок кода выглядит примерно так:
Transaction txn = null;
HibernateException ex = null
for(int retryAttempt = 0; retryAttempt < 5; retryAttempt++) {
try {
txn = hibnSessn.beginTransaction();
int pojoID = pojo.getID();
pojo = hibnSessn.get(Pojo.class, pojoID);
pojo.setSomeVar("xyz");
txn.commit();
ex = null;
} catch(HibernateException e) {
if (txn != null)
{
txn.rollback();
}
ex = e;
// Gonna keep retrying
continue;
}
break;
}
И Pojo.hbm.xml
начинает примерно так:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.myproject.Pojo" table="pojo">
<id name="id" column="id" type="int">
<generator class="identity"/>
</id>
<!-- this is used to enforce optimistic locking -->
<version name="dbVersion" column="db_version"/>
...
Первоначальный автор этого кода ввел указанный выше цикл повторных попыток именно потому, что есть другие модули, которые также обновляют Pojo
, и если первоначальная фиксация завершается неудачно из-за состояния гонки, мы предпринимаем дополнительные попытки, надеясь добиться успеха в чистой, неконфликтной транзакции. Мне не нравится этот шаблон, но пока приходится с ним работать.
Мне удалось обработать состояние гонки, разорвав строку pojo.setSomeVar("xyz");
, обновив базу данных из другого потока/метода и продолжив работу, которая не выполнила фиксацию и попала в ловушку из-за оптимистичной блокировки, отката и перехода к следующему итерация повторного цикла.
Проблема в том, что во второй итерации строка pojo = hibnSessn.get(Pojo.class, pojoID);
не обновляет pojo
свежими данными из БД, а извлекает устаревший объект из сеанса, лишая цель повторной попытки.
Еще одна интересная вещь заключается в том, что hibnSessn.get(...)
действительно попадает в БД (при нормальных условиях), потому что, когда я изменяю данные из другого потока/метода до hibnSessn.get
в первой итерации перед ошибкой фиксации, объект действительно обновляется. Это не удается сделать только после сбоя фиксации и отката транзакции.
Я ищу способы заставить Hibernate войти в БД и обновить объект после первоначальной ошибки фиксации.
ОБНОВЛЕНИЕ: я попытался evict/refresh
в перехвате, чтобы обновить объект, но это также привело к ошибке StaleObjectStateException
.