Spring乐观锁定:如何重试事务方法直到提交成功 [英] Spring Optimistic Locking:How to retry transactional method till commit is successful
问题描述
我使用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屋!