Сеанс гибернации не синхронизируется при обнаружении взаимоблокировки

У меня есть следующий сценарий для сохранения моего объекта

// Started transaction
User objUser = getUser("123");// get user from DB
objUser.set(...)
.
.
UserAddress objUserAddress = objUser.getUserAddress();
objUserAddress.set(..);
.
.
hibernateSession.flush(); //#Line 1
hibernateSession.saveOrUpdate(objUserAddress); //#Line 2
hibernateSession.flush(); //#Line 3
hibernateSession.saveOrUpdate(objUser); //#Line 4
// Commit transaction

Вот сопоставление гибернации между пользователем и классом адреса

<class name="com.service.core.bo.impl.User" table="USERS">
.
.
<many-to-one name="userAddress" class="com.service.core.bo.impl.UserAddress"
        column="ADDRESS_ID" not-null="false" unique="true" cascade="save-update"
        lazy="false" />
.
.
</class>

В какой-то момент я получил тупик на #Line 1. вот стек исключений.

[2017-12-12 11:15:02.131 GMT] WARN [] [] [] [] [] [] [] [] [] http-bio-8280-exec-14 org.hibernate.util.JDBCExceptionReporter - SQL Error: 60, SQLState: 61000
[2017-12-12 11:15:02.131 GMT] ERROR [] [] [] [] [] [] [] [] [] http-bio-8280-exec-14 org.hibernate.util.JDBCExceptionReporter - ORA-00060: deadlock detected while waiting for resource

[2017-12-12 11:15:02.131 GMT] ERROR [] [] [] [] [] [] [] [] [] http-bio-8280-exec-14 org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:87)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at sun.reflect.GeneratedMethodAccessor706.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
       .
       .
Caused by: java.sql.BatchUpdateException: ORA-00060: deadlock detected while waiting for resource
        at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:12296)
        at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:246)
        at sun.reflect.GeneratedMethodAccessor553.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at oracle.ucp.jdbc.proxy.StatementProxyFactory.invoke(StatementProxyFactory.java:353)
        at oracle.ucp.jdbc.proxy.PreparedStatementProxyFactory.invoke(PreparedStatementProxyFactory.java:178)
        at com.sun.proxy.$Proxy66.executeBatch(Unknown Source)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
        ... 71 more

Проблема в том, что эта ошибка непостоянна. Это может произойти один или несколько раз в день, а в другой день ничего. Не уверен, почему flush() вызывает Could not synchronize database state with session и ORA-00060: deadlock detected while waiting for resource. Я нашел несколько ссылок, таких как Не удалось синхронизировать состояние базы данных с сеансом для сеанса состояние, но моя фактическая причина - тупик, как указано выше.


person Navnath    schedule 23.02.2018    source источник


Ответы (1)


Не удалось синхронизировать состояние базы данных с сеансом — это общая ошибка перехода в спящий режим, которая может иметь несколько основных причин. Ошибка, на которую следует обратить внимание:

ORA-00060: обнаружена взаимоблокировка при ожидании ресурса

Это специфично для Oracle, и это происходит, когда обновления одних и тех же данных (строки в базе данных) происходят из более чем одного соединения. Всякий раз, когда Oracle (и фактически любая база данных) обновляет строку, он блокирует ее на время обновления. Эта ошибка возникает при попытке другого обновления той же строки, когда она заблокирована. Вот официальное объяснение ошибки от Oracle:

ORA-00060: обнаружена взаимоблокировка при ожидании ресурса

Причина. Транзакции заблокировали друг друга в ожидании ресурсов.

Действие: Просмотрите файл трассировки, чтобы увидеть задействованные транзакции и ресурсы. При необходимости повторите попытку.

Одним из способов решения этой проблемы является использование управления версиями:

https://www.intertech.com/Blog/versioning-optimistic-locking-in-hibernate/

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

person Matei Florescu    schedule 23.02.2018
comment
Спасибо и извините за задержку с ответом. Вы правы, что взаимоблокировка произошла, когда два ресурса пытались обновиться одновременно. Но здесь, в моем случае, я получаю это исключение только для метода flush(). - person Navnath; 01.03.2018
comment
Да, это нормально, потому что только тогда вносимые вами изменения действительно применяются к базе данных. saveOrUpdate() не будет выполнять операции с базой данных, все делается при вызове flush(). - person Matei Florescu; 01.03.2018