我正在使用 Hibernate 1.4.191 和 Hibernate 4.3.1。我的 h2 连接 URL 是 dbc:h2:file:./h2/myDB
- 这是一个未启用 MULTI_THREADED
的嵌入式数据库。我正在运行 MVCC,因为这是 1.4.191 的默认设置。
在我的应用程序中使用此配置,我在尝试执行更新 HQL 语句时获得以下堆栈跟踪:
2017-07-11 19:38:48 SEVERE Could not set rounding style. org.hibernate.PessimisticLockException: could not execute statement
at org.hibernate.dialect.H2Dialect$2.convert(H2Dialect.java:342)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:190)
at org.hibernate.hql.internal.ast.exec.BasicExecutor.doExecute(BasicExecutor.java:109)
at org.hibernate.hql.internal.ast.exec.BasicExecutor.execute(BasicExecutor.java:78)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.executeUpdate(QueryTranslatorImpl.java:445)
at org.hibernate.engine.query.spi.HQLQueryPlan.performExecuteUpdate(HQLQueryPlan.java:347)
at org.hibernate.internal.SessionImpl.executeUpdate(SessionImpl.java:1282)
at org.hibernate.internal.QueryImpl.executeUpdate(QueryImpl.java:118)
at de.oktopos.dataBase.receipts.converter.ReceiptConverter$29.runOperation(ReceiptConverter.java:1214)
at de.oktopos.dataBase.receipts.converter.ReceiptConverter$29.runOperation(ReceiptConverter.java:1210)
at de.oktopos.dataBase.tools.DatabaseOperationRunner.run(DatabaseOperationRunner.java:71)
at de.oktopos.dataBase.receipts.converter.ReceiptConverter.setRoundingStyle(ReceiptConverter.java:1226)
at de.oktopos.dataBase.receipts.converter.ReceiptCacheWriter.setRoundingStyle(ReceiptCacheWriter.java:99)
at de.oktopos.oktoDeskService.remote.OktoDeskModel.setRoundingStyle(OktoDeskModel.java:841)
at net.oktopos.cashdesk.DeskModelProcessor.setRoundingStyle(DeskModelProcessor.java:1648)
at sun.reflect.GeneratedMethodAccessor37.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.h2.jdbc.JdbcSQLException: Zeitüberschreitung beim Versuch die Tabelle zu sperren
Timeout trying to lock table ; SQL statement:
update Receipt set turnoverType=? [50200-191]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:168)
at org.h2.command.Command.filterConcurrentUpdate(Command.java:307)
at org.h2.command.Command.executeUpdate(Command.java:260)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:160)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:146)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:187)
... 29 more
Caused by: org.h2.jdbc.JdbcSQLException: Gleichzeitige Änderung in Tabelle "RECEIPT": eine andere Transaktion hat den gleichen Datensatz geändert oder gelöscht
Concurrent update in table "RECEIPT": another transaction has updated or deleted the same row [90131-191]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:168)
at org.h2.mvstore.db.MVTable.convertException(MVTable.java:898)
at org.h2.mvstore.db.MVSecondaryIndex.remove(MVSecondaryIndex.java:247)
at org.h2.mvstore.db.MVTable.removeRow(MVTable.java:677)
at org.h2.table.Table.updateRows(Table.java:487)
at org.h2.command.dml.Update.update(Update.java:145)
at org.h2.command.CommandContainer.update(CommandContainer.java:98)
at org.h2.command.Command.executeUpdate(Command.java:258)
... 32 more
Caused by: java.lang.IllegalStateException: Entry is locked [1.4.191/101]
at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:773)
at org.h2.mvstore.db.TransactionStore$TransactionMap.set(TransactionStore.java:1031)
at org.h2.mvstore.db.TransactionStore$TransactionMap.remove(TransactionStore.java:989)
at org.h2.mvstore.db.MVSecondaryIndex.remove(MVSecondaryIndex.java:241)
... 37 more
我试图理解这是怎么可能的。堆栈跟踪似乎说锁 could not be acquired because of another connection (50200) 。它特别指出 another connection concurrently updated the table (90131) 。但由于数据库是嵌入式的,因此只能有一个连接。我在这里遗漏了什么吗?
HQL 查询基本上执行如下(减去错误处理样板,DBReceipt 映射到收据):
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.createQuery("UPDATE DBReceipt SET turnovertype = :turnovertype")
.setParameter("turnovertype", turnovertype)
.executeUpdate();
transaction.commit();
session.close();
当然,在不同线程的后台还有许多其他数据库写入/读取,但都使用相同的连接。
调用上述部分代码时,不断发生错误。即使重新启动应用程序也无济于事 - 代码的下一次调用将导致相同的异常。只有删除数据库才有帮助。
最佳答案
在我的代码库中查看了一下之后,我发现了问题的可能根源。
我正在创建一个LockRequest
并锁定收据表中的收据。如果这个锁永远不会被释放(因为交易最终陷入无限循环),那么之后对收据表的任何更新都会导致上面的堆栈跟踪。
我通过删除 LockRequest
解决了这个问题(我找不到无限循环的来源)。这是可能的,因为我们最近转向在应用程序级别锁定该收据,而且还因为乐观锁定在我们的应用程序中不会出现问题。
解决此问题的另一个方法是为锁、事务或 session 添加超时。然而,我的环境似乎不支持任何这些 - 至少它们在我的测试中都不起作用。
关于java - 嵌入模式下的 h2 并发更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45061433/