Spring乐观锁定:如何重试事务方法直到提交成功 [英] Spring Optimistic Locking:How to retry transactional method till commit is successful

查看:138
本文介绍了Spring乐观锁定:如何重试事务方法直到提交成功的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Spring 2.5和Hibernate JPA实现Java和容器管理的事务。

I use Spring 2.5 and Hibernate JPA implementation with Java and "container" managed Transactions.

我有一个用户提交后方法,可以在后台更新数据,无论 ConcurrencyFailureException 都需要提交或 StaleObjectStateException 异常,因为它永远不会显示给客户端。换句话说,需要将乐观锁定变为悲观。 (如果方法执行需要更长的时间并且有人在其他交易中更改数据,可能会发生)

I have a "after user commit" method that updates data in background and need to be committed regardless of ConcurrencyFailureException or StaleObjectStateException exception, because it will never be shown to client. In other words, need to make Optimistic Lock to Pessimistic. (Could happen if methods execution will take little bit longer and someone changed data in other transaction)

我读了很多关于幂等的东西,如果搜索DEFAULT_MAX_RETRIES 6.2.7。示例第14.5章。重试。我还在stackoverflow中找到了这里和< a href =http://www.springframework.net/doc-latest/reference/html/dao.html =nofollow noreferrer>这里。

I read a a lot about idempotent stuff, retry if exception in search for DEFAULT_MAX_RETRIES or 6.2.7. Example or chapter 14.5. Retry. I also found in stackoverflow here and here.

我试过这个:

public aspect RetryOnConcurrencyExceptionAspect {

    private static final int DEFAULT_MAX_RETRIES = 20;
    private int maxRetries = DEFAULT_MAX_RETRIES;

    Object around(): execution( * * (..) ) && @annotation(RetryOnConcurrencyException) && @annotation(Transactional) {

        int numAttempts = 0;
          RuntimeException failureException = null;
          do {
                numAttempts++;
                try {
                    return proceed(); 
                } 
                catch( OptimisticLockingFailureException ex ) {
                    failureException = ex;
                }
                catch(ConcurrencyFailureException ex) {
                    failureException = ex;
                }
                catch( StaleObjectStateException ex) {
                    failureException = ex;
                }
          } while( numAttempts <= this.maxRetries );
          throw failureException;

    }
}

RetryOnConcurrencyException 是我的Annotation,用于标记需要重试的方法(如果发生异常)。没用......我也试过几种方法,比如 SELECT ... FOR UPDATE EntityManager.lock(...)

RetryOnConcurrencyException is my Annotation to mark methods that need to be retried, if a exception occurrs. Didn't work... I also tried several ways like SELECT ... FOR UPDATE, EntityManager.lock(...)

什么是避免过时数据,脏读等的最佳方法?重试?,同步?,JPA锁定?,隔离?,选择...进行更新?我无法让它工作,我真的很高兴任何帮助。

What is the best way to avoid stale data, dirty reads etc. such a strategy with Spring? Retry?, synchronized?, JPA lock?, isolation?, select ... for update? I could not get it to work and I really happy about any help.

这是我喜欢的一些伪代码执行:

Here is some pseudo code what I like to do:

void doSomething(itemId) {
    select something into A;
    select anotherthing into B;

    // XXX
    item = getItemFormDB( itemId ); // takes long for one user and for other concurrent user it could take less time
    item.setA(A);
    item.setB(B);

    // YYYY
    update item; 
}

在// XXX和// YYY之间另一个会话可以修改项目,然后抛出StaleObjectStateException。

Between // XXX and // YYY another session could modify the item, then the StaleObjectStateException gets thrown.

推荐答案

我得到了一个解决方案,但我觉得它很难看。我捕获所有RuntimeException,它只适用于新事务。你知道怎么做得更好吗?你看到有什么问题吗?

I got a solution but I think it's ugly. I catch all RuntimeException and it only works for new transactions. Do you know how to make it better? Do you see any problems?

首先,我做了一个注释:

First, I made an Annotation:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryingTransaction {
     int repeatCount() default 20;
}

然后我做了一个这样的拦截器:

Then I made a interceptor like this:

    public class RetryingTransactionInterceptor implements Ordered {
      private static final int DEFAULT_MAX_RETRIES = 20;
      private int maxRetries = DEFAULT_MAX_RETRIES;
      private int order = 1;

      @Resource
      private PlatformTransactionManager transactionManager;

      public void setMaxRetries(int maxRetries) {
          this.maxRetries = maxRetries;
      }
      public int getOrder() {
          return this.order;
      }
      public void setOrder(int order) {
          this.order = order;
      }

      public Object retryOperation(ProceedingJoinPoint pjp) throws Throwable {
          int numAttempts = 0;
          Exception failureException = null;
          do {
                numAttempts++;
                try {
                    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                    TransactionStatus status = transactionManager.getTransaction(def);

                    Object obj = pjp.proceed();

                    transactionManager.commit(status);      

                    return obj;
                } 
                catch( RuntimeException re ) {
                    failureException = re;
                }
          } while( numAttempts <= this.maxRetries );
          throw failureException;
      }
}

Spring applicationConfig.xml:

Spring applicationConfig.xml:

<tx:annotation-driven transaction-manager="transactionManager" order="10" />

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionSynchronizationName">
        <value>SYNCHRONIZATION_ALWAYS</value>
    </property>
</bean>

<bean id="retryingTransactionInterceptor" class="com.x.y.z.transaction.RetryingTransactionInterceptor">
    <property name="order" value="1" />
</bean>

<aop:config>
    <aop:aspect id="retryingTransactionAspect" ref="retryingTransactionInterceptor">
        <aop:pointcut 
            id="servicesWithRetryingTransactionAnnotation" 
            expression="execution( * com.x.y.z.service..*.*(..) ) and @annotation(com.x.y.z.annotation.RetryingTransaction)"/>
        <aop:around method="retryOperation" pointcut-ref="servicesWithRetryingTransactionAnnotation"/>
    </aop:aspect>
</aop:config>

这样的方法注释如下:

@RetryingTransaction
public Entity doSomethingInBackground(params)...

这篇关于Spring乐观锁定:如何重试事务方法直到提交成功的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆