Автор оригинала: Vlad Mihalcea.
Вступление
Это третья часть серии оптимистичных блокировок, и я расскажу, как мы можем реализовать механизм автоматической повторной попытки при работе с репозиториями JPA.
Вы можете найти вводную часть здесь и реализацию MongoDB здесь .
Повторить попытку
JPA требует выполнения кода контекста сохранения внутри транзакции, и если наш менеджер транзакций поймает Исключение RuntimeException
, оно инициирует процесс отката. Это делает контекст сохранения непригодным для использования, так как мы должны отбросить его вместе с откатной транзакцией.
Поэтому безопаснее повторить операцию бизнес-логики, когда мы не находимся в рамках текущей транзакции.
Для этого мы изменили нашу @Retry
аннотацию следующим образом:
public @interface Retry { Class extends Exception>[] on(); int times() default 1; boolean failInTransaction() default true; }
Здесь мы добавили свойство сбой в транзакции
, для которого по умолчанию установлено значение true.
Аспект также был изменен с учетом нового свойства аннотации.
private Object proceed( ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable { int times = retryAnnotation.times(); Class extends Throwable>[] retryOn = retryAnnotation.on(); Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!"); Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!"); if (retryAnnotation.failInTransaction() && TransactionSynchronizationManager.isActualTransactionActive()) { throw new IllegalTransactionStateException( "You shouldn't retry an operation from withing an existing Transaction." + "This is because we can't retry if the current Transaction was already rollbacked!"); } LOGGER.info( "Proceed with {} retries on {}", times, Arrays.toString(retryOn) ); return tryProceeding(pjp, times, retryOn); }
Если мы выполняем внутри транзакции и не отключаем безопасную проверку по умолчанию затем мы создаем исключение IllegalTransactionStateException, уведомляя вызывающего абонента, что безопаснее повторить попытку, если транзакция содержится в продолжающемся вызове службы, без вложения нашего аспекта перехвата.
Эта утилита является частью моего проекта db-util вместе с механизмом повтора MongoDB оптимистичного управления параллелизмом.
Поскольку он уже доступен в Центральном репозитории Maven, вы можете легко использовать его, просто добавив эту зависимость в свой pom.xml:
com.vladmihalcea db-util 0.0.1
Вы можете настроить оптимистичную повторную попытку так же просто, как это:
@Retry(times = 10, on = OptimisticLockException.class) public Product updateName( final Long id, final String name) { return transactionTemplate.execute( new TransactionCallback() { @Override public Product doInTransaction( TransactionStatus status) { Product product = entityManager.find(Product.class, id); product.setName(name); LOGGER.info("Updating product {} name to {}", product, name); return product; } } ); }
Запуск теста JUnit, который планирует 10 потоков для обновления одной и той же сущности, создаст оптимистичные исключения блокировки, и это то, что выводит тест.
Line 102: INFO [Thread-3]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 103: INFO [Thread-12]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 104: INFO [Thread-9]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 105: INFO [Thread-6]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 109: INFO [Thread-9]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 110: INFO [Thread-7]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 114: INFO [Thread-3]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 115: INFO [Thread-5]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 117: INFO [Thread-11]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 9 remaining retries on [class javax.persistence.OptimisticLockException] Line 118: INFO [Thread-6]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 123: INFO [Thread-7]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 124: INFO [Thread-5]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 126: INFO [Thread-11]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 8 remaining retries on [class javax.persistence.OptimisticLockException] Line 129: INFO [Thread-5]: v.c.a.OptimisticConcurrencyControlAspect - Optimistic locking detected, 7 remaining retries on [class javax.persistence.OptimisticLockException]
Вывод
Поэтому мы повторно использовали ту же оптимистичную логику повторных попыток блокировки, которую мы впервые реализовали для наших пакетных процессоров MongoDB, что доказывает, что мы можем легко реализовать такое поведение даже для репозиториев JPA.
Код доступен на GitHub .