乐观锁定的重试机制(spring data + JPA)

时间:2014-02-10 08:44:08

标签: java spring hibernate jpa optimistic-locking

我们决定在我们的Web应用程序中使用乐观锁定,以增加并发性,而不使用悲观锁定。

我们现在正在寻找重试解决方案。

我们希望尽可能减少对当前代码库的影响。

我们在网上看到的解决方案之一是使用带注释的重试拦截器将方法标记为可重试。

问题是我们想要注释对它们有@Transactional注释的方法,但拦截器由于某种原因无法重试它们。 (拦截器完美地重试非事务性方法。)

所以:

1)重试的替代方案对我们的代码影响最小吗?

2)该解决方案是否有任何文档\教程?

3)是否可以重试@Transactional带注释的方法?

干杯!

3 个答案:

答案 0 :(得分:2)

Ad 3。

当版本号或时间戳检查失败时(发生乐观锁定),您可以使用Spring Retry重试事务处理方法。

配置

@Configuration
@EnableRetry
public class RetryConfig {

}

用法

@Retryable(StaleStateException.class)
@Transactional
public void doSomethingWithFoo(Long fooId){
    // read your entity again before changes!
    Foo foo = fooRepository.findOne(fooId);

    foo.setStatus(REJECTED)  // <- sample foo modification

} // commit on method end

使用@Transational(propagation = REQUIRED_NEW)仅重试带注释方法的代码。

答案 1 :(得分:1)

您有两种方法可以实现此目的,如下所示

<强> Recovering from hibernate optimistic locking exception

<强> Using Spring AOP to Retry Failed Idempotent Concurrent Operations

希望这会对你有帮助..!

答案 2 :(得分:0)

重试无法进行的原因是@Transactional优先级高于@Aspect。

您应通过在TryAgainAspect类中实施订购的来将@Aspect的优先级提高

拦截器类:

@Aspect
@Component
public class TryAgainAspect implements Ordered {

private int maxRetries;
private int order = 1;

public void setMaxRetries(int maxRetries) {
    this.maxRetries = maxRetries;
}

public int getOrder() {
    return this.order;
}

@Pointcut("@annotation(IsTryAgain)")
public void retryOnOptFailure() {
}

@Around("retryOnOptFailure()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
    MethodSignature msig = (MethodSignature) pjp.getSignature();
    Object target = pjp.getTarget();
    Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
    IsTryAgain annotation = currentMethod.getAnnotation(IsTryAgain.class);
    this.setMaxRetries(annotation.tryTimes());

    int numAttempts = 0;
    do {
        numAttempts++;
        try {
            return pjp.proceed();
        } catch (ObjectOptimisticLockingFailureException | StaleObjectStateException exception) {
            exception.printStackTrace();
            if (numAttempts > maxRetries) {
                throw new NoMoreTryException("System error, all retry failed");
            } else {
                System.out.println("0 === retry ===" + numAttempts + "times");
            }
        }
    } while (numAttempts <= this.maxRetries);

    return null;
}

}

IsTryAgain:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IsTryAgain {
    int tryTimes() default 5;
}

您的服务类方法应添加注释@IsTryAgain和@Transactional

@IsTryAgain
@Transactional(rollbackFor = Exception.class)
public Product buyProduct(Product product) {
// your business logic 
}