2011-02-28 91 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