嵌入模式下的h2并发更新

时间:2017-07-12 15:06:21

标签: java hibernate h2

我正在使用Hibernate 1.4.191和Hibernate 4.3.1。我的h2连接网址是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();

当然,在后台有许多其他数据库写入/读取在不同的线程上,但都使用相同的连接。

调用代码的上述部分时,错误仍然发生。即使重新启动应用程序也无济于事 - 下一次调用代码会导致同样的异常。只有放弃数据库才有帮助。

1 个答案:

答案 0 :(得分:0)

在我的代码库中看了一下之后,我发现了问题的可能来源。

我就是创建一个LockRequest并将收据锁定在收据表中。如果永远不会放弃此锁(因为事务以无限循环结束),那么之后对收据表的任何更新都将导致上面的堆栈跟踪。

我通过删除LockRequest解决了这个问题(我找不到无限循环的来源)。这是可能的,因为我们最近在应用程序级别上锁定了该收据,并且因为我们的应用程序在乐观锁定时没有问题。

解决此问题的另一个方法是向锁,事务或会话添加超时。但是,我的环境似乎并不支持任何这些 - 至少在我的测试中没有一个工作。