Я использую реализацию Spring 2.5 и Hibernate JPA с Java и "контейнерными" управляемыми транзакциями.
У меня есть метод «после фиксации пользователем», который обновляет данные в фоновом режиме и должен быть зафиксирован независимо от исключения ConcurrencyFailureException
или StaleObjectStateException
, потому что он никогда не будет показан клиенту. Другими словами, нужно сделать оптимистическую блокировку пессимистичной. (Может произойти, если выполнение методов займет немного больше времени и кто-то изменил данные в другой транзакции)
Я много читал об идемпотентных вещах, повторите попытку, если исключение в ищите DEFAULT_MAX_RETRIES или 6.2.7. Пример или глава 14.5. Повторите попытку. Я также нашел в stackoverflow здесь и здесь.
Я пробовал это:
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
— это моя аннотация для обозначения методов, которые необходимо повторить, если возникает исключение. Не сработало... Я также пробовал несколько способов, таких как SELECT ... FOR UPDATE
, EntityManager.lock(...)
Каков наилучший способ избежать устаревших данных, грязного чтения и т. д. такой стратегии с Spring? Повторить?, синхронизировать?, блокировку JPA?, изоляцию?, выбрать... для обновления? Я не мог заставить его работать, и я очень рад любой помощи.
Вот некоторый псевдокод, который мне нравится делать:
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.