Java 使用乐观锁定时的Hibernate/MySQL LockTimeoutException

Java 使用乐观锁定时的Hibernate/MySQL LockTimeoutException,java,mysql,hibernate,jpa,Java,Mysql,Hibernate,Jpa,我们正在运行Hibernate 4和MySQL的最新版本。我们所有的Hibernate实体都有一个用@version注释的版本字段,因为我们全面使用乐观锁定。在应用程序中,我们没有使用悲观锁定。我的理解是,当使用悲观锁定时,LockTimeoutExceptions可能会发生,然而,即使我们不使用这种锁定策略,我们也经常会遇到这种情况 在这个特定场景中,我们有一个执行器服务,其线程池大小为1。当用户执行某些操作时,我们会为其他用户创建通知。我们将此通知创建委托给executor服务。创建这些通知

我们正在运行Hibernate 4和MySQL的最新版本。我们所有的Hibernate实体都有一个用@version注释的版本字段,因为我们全面使用乐观锁定。在应用程序中,我们没有使用悲观锁定。我的理解是,当使用悲观锁定时,
LockTimeoutExceptions
可能会发生,然而,即使我们不使用这种锁定策略,我们也经常会遇到这种情况

在这个特定场景中,我们有一个执行器服务,其线程池大小为1。当用户执行某些操作时,我们会为其他用户创建通知。我们将此通知创建委托给executor服务。创建这些通知可能需要一些时间,因此我们采用这种方式,这样用户就不必坐着等待通知创建完成。我们使用单一大小的线程池,这样我们就不会多次处理通知等。我们想要的是让这些操作排队并按顺序运行。每个线程启动一个事务,创建所有必要的通知,提交所述事务并关闭实体管理器

下面是我们得到的异常的堆栈跟踪。你知道为什么我们会在上面这样的场景中得到一个锁吗?我不明白

Lock wait timeout exceeded; try restarting transaction
    2015-10-08 01:27:52,195 ERROR [NotificationPublishingRunnable] : could not execute statement
    javax.persistence.LockTimeoutException: could not execute statement
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.wrapLockException(AbstractEntityManagerImpl.java:1812)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1715)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1206)
        at za.co.bsg.ems.server.DB.merge(171bcc9:315)
        at za.co.bsg.ems.server.repositories.AbstractRepository.merge(171bcc9 ---------------:158)
        at za.co.bsg.ems.server.services.notification.EmployeeNotificationCRUDServiceSupport.createEmployeeNotification(171bcc9 remotes/origin/11_9 ---------------:97)
        at za.co.bsg.ems.server.services.notification.EmployeeNotificationCRUDServiceSupport.createEmployeeNotifications(171bcc9 remotes/origin/11_9 ---------------:75)
        at za.co.bsg.ems.server.services.notification.EmployeeNotificationCreatorSupport.createEmployeeNotifications(171bcc9 remotes/origin/11_9 -----------:80)
        at za.co.bsg.ems.server.notification.handler.NotificationHandlerManagerSupport.createNotificationsFromRequest(171bcc9 remotes/origin/11_9 ----------:233)
        at za.co.bsg.ems.server.runnable.notification.NotificationPublishingRunnable.run(171bcc9 remotes/origin/11_9 -------:64)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: org.hibernate.exception.LockTimeoutException: could not execute statement
        at org.hibernate.dialect.MySQLDialect$1.convert(MySQLDialect.java:447)
        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:211)
        at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:96)
        at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:58)
        at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3032)
        at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3558)
        at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:98)
        at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:492)
        at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:197)
        at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:181)
        at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:216)
        at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:324)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
        at org.hibernate.jpa.event.internal.core.JpaMergeEventListener.saveWithGeneratedId(JpaMergeEventListener.java:73)
        at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:271)
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:251)
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:189)
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
        ... 10 more
    Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3823)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2530)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1907)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2141)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2077)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2062)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208

使用@Version注释所有实体:这意味着您希望使用乐观锁定。乐观锁定由EntityManger、DAO或存储库管理


在这里,您使用preparedstatement来执行数据库请求。因此,您使用的是悲观锁定,这是某些数据库的默认特性。这就是您拥有LockTimeoutException的原因。

我遇到了相同的问题,这是因为我开始了一个事务,但没有提交它,然后又开始了另一个事务


提交第一笔交易解决了这个问题。

这个答案毫无帮助,它主要重复了我在问题中已经说过的内容。此外,乐观锁定在整个应用程序中都被使用,它不会因为您建议的“数据库默认值”而退回到悲观锁定。我之所以知道这一点,是因为我们在其他情况下也会收到同一实体的乐观锁定例外情况。布伦特777我不知道我是否理解你的问题。你说(在你的标题中)在使用乐观锁定时有一个LockTimeOutException。我的回答是,您使用的不是乐观锁定,而是悲观锁定,因为您使用的是PreparedStatement。如果不是,你的问题是什么?我的问题是如何在乐观的锁定环境中获得LockTimeoutException。正如我在前面的评论中所说,我不认为悲观锁定仅仅因为涉及到准备好的语句就被使用。那么为什么我会得到同一实体的OptimisticLockException?当我的所有查询都以完全相同的方式编写并通过实体管理器作为命名查询执行时,为什么有时是乐观的,有时是悲观的?即使我到处都有特定的版本字段,您对使用悲观锁定的准备语句的引用是什么?请提供发出请求的代码好吗?[@brent777]您有没有找到解决此问题的方法?我们遇到了完全相同的问题。@AndrewKew不幸的是,没有。