Ошибка Hibernate StaleObjectStateException при вызове слияния

Я пытаюсь протестировать оптимистическую блокировку, используя аннотацию JPA @Version, которую я добавил к своему объекту сущности:

@Version    
@Setter(AccessLevel.NONE)
@Column(name = "VERSION")
private long version; 

Когда я запускаю 2 сервера одновременно, я получаю StaleObjectStateException:

Exception message is : Object of class [com.myPackage.WorkQueue] with identifier [9074]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.myPackage.WorkQueue#9074]

Я ожидал увидеть 1) появление OptimisticLockException и 2) откат @transaction в результате.

Пример использования следующий: записи вставляются в таблицу базы данных Oracle со статусом «NEW». Как только поток извлекает строку со статусом «NEW», он обновляет статус строки в таблице на «IN_PROGRESS». Мне нужно гарантировать, что любые транзакции, читающие одну и ту же строку одновременно, завершатся ошибкой / откатом, если другая транзакция успешно обновила эту строку.

Обслуживание:

@Override
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor=Exception.class, readOnly=false)
public WorkQueue retrieveWorkQueueItemByStatus(WorkQueueStatusEnum workQueueStatus) {
    return workQueueRepository.retrieveWorkQueueItemByStatus(workQueueStatus);
}

Реализация:

@Override
public WorkQueue retrieveWorkQueueItemByStatus(WorkQueueStatusEnum workQueueStatus) {
    log.debug("Start - Attempting to select a " + workQueueStatus + " workQueue item in retrieveWorkQueueItemByStatus()");  

    try {
        String sql = "SELECT a FROM WorkQueue a WHERE workQueueStatus = :workQueueStatus ORDER BY idWorkQueue ASC";
        TypedQuery<WorkQueue> query = em.createQuery(sql, WorkQueue.class).setParameter("workQueueStatus", workQueueStatus)
        .setFirstResult(0).setMaxResults(1);
        WorkQueue workQueue = (WorkQueue) query.getSingleResult();
        if (workQueue != null) {
            workQueue.setWorkQueueStatus(WorkQueueStatusEnum.IN_PROGRESS);
            WorkQueue updatedWorkQueue = em.merge(workQueue);               
            log.debug("Finish - selected the following workQueue item "+ workQueue.getIdWorkQueue() + " with the Audit Event Key from retrieveWorkQueueItemByStatus() : " + updatedWorkQueue.getAuditEventKey());
            return updatedWorkQueue;
        }
    } catch (IllegalArgumentException iae) {
        log.error("An IllegalArgumentException occured in workQueueRepositoryImpl.retrieveWorkQueueItemByStatus() attempting to execute query : " + sql + ". Exception message is : " + iae.getMessage());
    } catch(Exception ex) {
        log.error("An Exception occured in workQueueRepositoryImpl.retrieveWorkQueueItemByStatus() executing query : " + sql + ". Exception message is : " + ex.getMessage());
    }
    log.debug("Finish - returning null from retrieveWorkQueueItemByStatus()");
    return null;
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
267
1

Ответы 1

См. этот вопрос о типе исключения.

Транзакция не откатывается автоматически. Этот вид исключения никогда не следует перехватывать и продолжать использовать сеанс Hibernate. Причина в том, что сеанс мог быть частично синхронизирован с базой данных, поэтому состояние базы данных могло быть несогласованным. Так что пусть исключение будет выброшено за пределами транзакции, где оно откатывается, и начнется с нового сеанса.

Вы говорите, что приведенный выше синтаксис специфичен для спящего режима, а не для JPA, поэтому я не получаю правильный тип исключения? Почему бы автоматически не откатить транзакцию?

Orby 24.04.2018 15:06

Стефан, я удалил операторы try catch из приведенной выше реализации и добавил throws RuntimeException в сигнатуру метода retrieveWorkQueueItemByStatus(). Правильно ли я предполагаю, что транзакция откатится, когда Hibernate выбрасывает StaleObjectStateException?

Orby 25.04.2018 11:30

Я бы проверил код Hibernate, когда для вас важно, чтобы транзакция откатывалась в тот момент, когда генерируется исключение. Обычно исключение выбрасывается из некоторого блока попытки (с ресурсами), и транзакция и сеанс закрываются. Это тот момент, когда он хотя бы откатывается.

Stefan Steinegger 26.04.2018 13:38

Другие вопросы по теме