微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

java – JPA并发事务

我使用JPA-1.0,Hibernate和MySQL 5.0.84(innoDB表)以及Postgres 8.1.11(不同客户端的不同数据库)的并发事务有问题.我不知道我是否遗漏了有关配置的内容,因为我已阅读有关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 [org.hibernate.ejb.EntityManagerImpl@221f81] for JPA transaction
org.springframework.orm.jpa.JpaTransactionManager:355 - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@9f5742]
org.springframework.orm.jpa.JpaTransactionManager:752 - Initiating transaction commit
org.springframework.orm.jpa.JpaTransactionManager:462 - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@221f81]
org.springframework.orm.jpa.JpaTransactionManager:548 - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@221f81] 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 [org.hibernate.ejb.EntityManagerImpl@7ca9bd]
org.springframework.orm.jpa.JpaTransactionManager:548 - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@7ca9bd] 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 [org.hibernate.ejb.EntityManagerImpl@adddd6] 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;

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

所以从本质上讲,我正在做的是创建日志记录,然后更新应用程序实例,使其链接到与之相关的最新日志记录.这是在单个事务方法中完成的,但是在更新应用程序实例记录时似乎发生了故障.但是,这在单个调用中可以正常工作,但在并发调用时则不行.

希望这为我的问题增加了更多的清晰度.

解决方法:

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

>从application_instance中选择
>插入execution_log,引用application_instance(在application_instance行上获取共享锁)
>更新application_instance(对application_instance行进行独占锁定)

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

我会通过立即获取具有独占锁的应用程序实例来解决这个问题.在直接Hibernate术语中,可以通过在加载实体(session.get或查询)时指定LockMode.UPGRADE(或LockMode.pessimistic_WRITE)来完成.

(我还会重新检查你是否真的需要每次更新application_instance以指向最新的日志条目 – 这是一个瓶颈,因为所有事务都需要同步/序列化它.)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐