2011-02-28 74 views
3

我在使用JPA-1.0,Hibernate和MySQL 5.0.84(innoDB表)以及Postgres 8.1.11(针对不同客户端的不同数据库)的并发事务中遇到问题。我不知道我是否错过了关于配置的东西,因为我已经阅读了关于JPA事务的规范,并且根据我的问题,我需要为事务注释指定特定的隔离级别。我这样做了,但它只是关闭了所有的事务,所以没有任何东西被持续/更新。JPA并发事务

我在做的是,启动http帖子到一个web服务器(在我的情况下,tomcat),然后尝试产生多个数据库事务作为http请求进来。 每个事务包含1插入和2更新。这个问题似乎总是出现在最终的更新上,尽管这是基于前面的插入。 所以,我插入记录A,然后更新记录B这是一个外键记录A.

以下是我获得在执行单个HTTP请求的日志记录:


org.springframework.orm.jpa.JpaTransactionManager:365 - Creating new transaction with name [biz.cytrus.overlord.v2.core.ExecutionLogAPI.create]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 
org.springframework.orm.jpa.JpaTransactionManager:323 - Opened new EntityManager [[email protected]] for JPA transaction 
org.springframework.orm.jpa.JpaTransactionManager:355 - Exposing JPA transaction as JDBC transaction [org.springframewo[email protected]9f5742] 
org.springframework.orm.jpa.JpaTransactionManager:752 - Initiating transaction commit 
org.springframework.orm.jpa.JpaTransactionManager:462 - Committing JPA transaction on EntityManager [[email protected]] 
org.springframework.orm.jpa.JpaTransactionManager:548 - Closing JPA EntityManager [[email protected]] after transaction 
org.springframework.orm.jpa.EntityManagerFactoryUtils:329 - Closing JPA EntityManager 

以下是记录我获得在同时执行多个HTTP请求:


org.hibernate.util.JDBCExceptionReporter:357 - SQL Error: 1213, SQLState: 40001 
org.hibernate.util.JDBCExceptionReporter:454 - Deadlock found when trying to get lock; try restarting transaction 
org.hibernate.event.def.AbstractFlushingEventListener:532 - 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:105) 
     at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) 
     at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275) 
     at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266) 
     at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168) 
     at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) 
     at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) 
     at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027) 
     at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365) 
     at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137) 
     at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54) 
     at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467) 
     at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
     at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
     at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:375) 
     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
     at $Proxy66.create(Unknown Source) 
     at biz.cytrus.overlord.v2.web.action.task.LogAction.createLogEntry(LogAction.java:84) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:592) 
     at net.sourceforge.stripes.controller.DispatcherHelper$6.intercept(DispatcherHelper.java:442) 
     at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:158) 
     at net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor.intercept(BeforeAfterMethodInterceptor.java:113) 
     at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) 
     at net.sourceforge.stripes.controller.ExecutionContext.wrap(ExecutionContext.java:74) 
     at net.sourceforge.stripes.controller.DispatcherHelper.invokeEventHandler(DispatcherHelper.java:440) 
     at net.sourceforge.stripes.controller.DispatcherServlet.invokeEventHandler(DispatcherServlet.java:278) 
     at net.sourceforge.stripes.controller.DispatcherServlet.service(DispatcherServlet.java:160) 
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at net.sourceforge.stripes.controller.StripesFilter.doFilter(StripesFilter.java:247) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:390) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) 
     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) 
     at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849) 
     at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) 
     at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454) 
     at java.lang.Thread.run(Thread.java:595) 
Caused by: java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transaction 
     at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:657) 
     at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723) 
     at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) 
     at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268) 
     ... 48 more 
org.springframework.orm.jpa.JpaTransactionManager:893 - Initiating transaction rollback after commit exception 
org.springframework.dao.CannotAcquireLockException: Could not execute JDBC batch update; SQL [update application_instances set application_id=?, create_date=?, for_ongoing_task=?, last_log_id=?, last_notified_date=?, name=?, param_string=?, application_status_id=?, status_date=? where id=?]; nested exception is org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update 
     at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:633) 
     at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:97) 
     at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:471) 
     at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
     at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
     at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:375) 
     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
     at $Proxy66.create(Unknown Source) 
     at biz.cytrus.overlord.v2.web.action.task.LogAction.createLogEntry(LogAction.java:84) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:592) 
     at net.sourceforge.stripes.controller.DispatcherHelper$6.intercept(DispatcherHelper.java:442) 
     at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:158) 
     at net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor.intercept(BeforeAfterMethodInterceptor.java:113) 
     at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) 
     at net.sourceforge.stripes.controller.ExecutionContext.wrap(ExecutionContext.java:74) 
     at net.sourceforge.stripes.controller.DispatcherHelper.invokeEventHandler(DispatcherHelper.java:440) 
     at net.sourceforge.stripes.controller.DispatcherServlet.invokeEventHandler(DispatcherServlet.java:278) 
     at net.sourceforge.stripes.controller.DispatcherServlet.service(DispatcherServlet.java:160) 
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at net.sourceforge.stripes.controller.StripesFilter.doFilter(StripesFilter.java:247) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:390) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) 
     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) 
     at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849) 
     at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) 
     at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454) 
     at java.lang.Thread.run(Thread.java:595) 
Caused by: org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update 
     at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:105) 
     at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) 
     at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275) 
     at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266) 
     at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168) 
     at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) 
     at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) 
     at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027) 
     at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365) 
     at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137) 
     at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54) 
     at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467) 
     ... 39 more 
Caused by: java.sql.BatchUpdateException: Deadlock found when trying to get lock; try restarting transaction 
     at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:657) 
     at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723) 
     at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) 
     at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268) 
     ... 48 more 
org.springframework.orm.jpa.JpaTransactionManager:488 - Rolling back JPA transaction on EntityManager [[email protected]] 
org.springframework.orm.jpa.JpaTransactionManager:548 - Closing JPA EntityManager [[email protected]] after transaction 
org.springframework.orm.jpa.EntityManagerFactoryUtils:329 - Closing JPA EntityManager 

在试图解决这个问题,我试图设置交易注释隔离级别,但是这导致数据库上没有活动:


org.springframework.orm.jpa.JpaTransactionManager:365 - Creating new transaction with name [biz.cytrus.overlord.v2.core.ExecutionLogAPI.create]: PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ; '' 
org.springframework.orm.jpa.JpaTransactionManager:323 - Opened new EntityManager [[email protected]] for JPA transaction 
org.springframework.orm.jpa.EntityManagerFactoryUtils:329 - Closing JPA EntityManager 

我真的很感谢任何帮助我如何解决这个问题。

下面的代码中的方法的一个片段标有注解@Transactional:


Task task = ongoingTaskAPI.findById(taskId); 
ExecutionLog executionLog = new ExecutionLog(); 
executionLog.setStatusDate(new Date()); 
executionLog.setStatus(status); 

     executionLog.setApplicationInstance(task.getApplicationInstance()); 

executionLog.getApplicationInstance().setLastLog(executionLog); 
em.persist(executionLog); 

实体bean的相关部分如下:


public class ExecutionLog implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Integer id; 

    @ManyToOne 
    @JoinColumn(name="application_instance_id") 
    private ApplicationInstance applicationInstance; 

public class ApplicationInstance implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Integer id; 

    @OneToOne 
    @JoinColumn(name="last_log_id", nullable = true) 
    private ExecutionLog lastLog; 

甲一个在任何时候都存在一对一的关系,特定的日志记录只能链接到特定的应用程序实例记录。

实际上,我在做的是创建日志记录,然后更新应用程序实例,以便将其链接到与之相关的最新日志记录。这是在单个事务处理方法中完成的,但应用程序实例记录更新时似乎发生失败。但是,这在一次调用中可以正常工作,但不适用于并发调用。

希望这可以让我的问题更加清晰。

+0

你能告诉一些代码? – axtavt 2011-02-28 08:20:46

+0

我已经添加了上面的代码片段。根据堆栈跟踪,BatchUpdateException发生在em.persist之前的行上,该行在应用程序实例上设置日志记录。 – Razeen 2011-02-28 09:09:09

回答

4

指向最新日志条目的共享“应用程序实例”行正在寻求锁定问题。在我看来,该序列将是:

从application_instance
  • 选择
  • INSERT INTO execution_log,引用application_instance(发生在application_instance行共享锁)
  • 更新application_instance(将独占锁application_instance行)

因此两个线程都可以在application_instance上拥有共享锁;线程A然后阻塞线程B尝试进行更新,然后线程B上线程A ...

我会通过立即获得应用程序实例与独占锁来解决此问题。在加载实体(session.get或查询时)时,通过指定LockMode.UPGRADE(或LockMode.PESSIMISTIC_WRITE)可以实现直接Hibernate术语。

(我也要重新审视,如果你确实需要更新application_instance在最新的日志条目中的每个时间是一个瓶颈,因为所有的交易需要同步/连载它来点。)

+0

非常感谢你araqnid!这解决了我的问题! :) – Razeen 2011-02-28 12:51:12