检测到死锁时,Hibernate会话不同步

时间:2018-02-23 08:24:05

标签: java hibernate hibernate-mapping database-deadlocks

我有以下方案来保存我的对象

// 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

这是User和Address类之间的hibernate映射

<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 sessionORA-00060: deadlock detected while waiting for resource的原因。我找到了一些类似Could not synchronize database state with session的链接用于会话状态,但我的实际原因是死锁,如上所述。

1 个答案:

答案 0 :(得分:2)

无法将数据库状态与会话同步是一个通用的hibernate错误,可能有多个根本原因。 您应该关注的错误是:

ORA-00060:在等待资源时检测到死锁

这是Oracle特定的,当从多个连接发生对相同数据(数据库中的行)的更新时会发生这种情况。每当Oracle(实际上几乎任何数据库)更新一行时,它会在更新期间锁定它。如果在锁定时在同一行上尝试另一次更新,则会发生此错误。 以下是Oracle的官方错误解释:

ORA-00060:在等待资源时检测到死锁

原因:交易在等待资源时相互死锁。

操作:查看跟踪文件以查看涉及的事务和资源。必要时重试。

解决此问题的一种方法是使用版本控制:

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

这会向表中添加一个版本列,该行会在更新行时自动递增。在更新之前,将检查版本,如果版本高于预期版本,则更新甚至不会更新,并且会抛出特定错误,您可以处理。通常,处理涉及从该实体的数据库重新加载信息,将其值重置为您想要的值然后保存。