Сеанс гибернации не обновляет данные из БД после первоначальной ошибки фиксации

Я работаю над многопоточным 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.

СВЯЗАННЫЕ: https://stackoverflow.com/questions/19364343/staleobjectstateexception-when-evicting-refreshing-stale-object-from-hibernate-s


person amphibient    schedule 12.10.2013    source источник
comment
Как насчет использования запроса вместо обновления? Что-то вроде Pojo, где id=? Это всего лишь попытка, так как в принципе они не должны отличаться.   -  person Ean V    schedule 15.10.2013


Ответы (2)


Вы можете попробовать исключить объект из сеанса в catch.

hibnSessn.evict(pojo);

Получение при повторной попытке должно затем получить свежую копию из БД.

Обновление также может сработать.

hibnSessn.refresh(pojo);
person jend    schedule 12.10.2013
comment
refresh() - правильный подход. Вы потеряете все существующие изменения в обновленном объекте, но конфликт блокировки будет разрешен, поэтому вам может потребоваться повторно применить логические изменения к обновленному объекту. - person Thomas W; 12.10.2013
comment
Я попробовал именно это решение (с выселением и без него), и оба раза я получил StaleObjectStateException - person amphibient; 14.10.2013

hibnSessn.flush() удалит устаревший объект (каждый объект в этом отношении) из сеанса. Таким образом, извлеките из БД, например.

    hibnSessn.flush()
    pojo = hibnSessn.get(Pojo.class, pojoID);
person Dennis Leon    schedule 12.10.2013
comment
Я получаю StaleObjectStateException, когда пытаюсь смыть - person amphibient; 12.10.2013
comment
Можете ли вы добавить трассировку стека в качестве дополнительной информации, пожалуйста. Я бы добавил int pojoID = pojo.getID(); вне вашего цикла for - person Dennis Leon; 12.10.2013
comment
flush() бесполезен, он предназначен для записи данных в БД, но вы находитесь в состоянии конфликта блокировок, из которого вы не можете писать. - person Thomas W; 12.10.2013