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

Posted

技术标签:

【中文标题】乐观锁定的重试机制(spring data + JPA)【英文标题】:Retry mechanism for optimistic locking (spring data + JPA) 【发布时间】:2014-03-07 12:03:11 【问题描述】:

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

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

我们希望对我们当前的代码库产生尽可能小的影响。

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

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

所以:

1) 是否有任何对我们的代码影响最小的重试替代方案?

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

3) 甚至可以重试@Transactional 注释的方法吗?

干杯!

【问题讨论】:

【参考方案1】:

重试失败的原因是@Transactional 优先级高于@Aspect。

您应该通过在 TryAgainAspect 类中实现 Ordered 来提高 @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 

【讨论】:

【参考方案2】:

广告 3。

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

配置

@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

使用@Transactional (propagation = Propagation.REQUIRES_NEW) 仅重试注释方法中的代码。

【讨论】:

“SQLException:语句已关闭。”退休时发生的。 @xiemeilong 我在使用 Hibernate 5.0.x 时遇到了与您相同的错误。它在 5.2.14.Final 中消失了。我花了将近一天的时间才弄清楚这一点。 :( @Jean-FrançoisBeauchef 非常感谢,我稍后再试试。【参考方案3】:

您有以下两种方法来实现这一点

Recovering from hibernate optimistic locking exception

Using Spring AOP to Retry Failed Idempotent Concurrent Operations

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

【讨论】:

Ashish,我们已经尝试过第二种解决方案,显然当您在事务方法上使用它时它不起作用,您知道为什么吗? 我完全按照josiahgore.blogspot.in/2011/02/… 中所写的内容,然后我用@RetryConcurrentOperation(exception = HibernateOptimisticLockingFailureException.class, retries = 12) 注释了一个服务方法,因为该方法是用@Transactional 注释的它不起作用,如果我注释它,它会在非事务方法上给出一个常规的乐观锁异常 - 它起作用。

以上是关于乐观锁定的重试机制(spring data + JPA)的主要内容,如果未能解决你的问题,请参考以下文章

ribbon的重试机制

Spring Cloud Feign 重试机制-如何实现请求重试

如何在 Spring Data MongoDB 中使用乐观锁定?

一文详解Spring Cloud Feign重试机制

spring-retry重试机制使用

使用JPA和Hibernate重试乐观锁定异常