W końcu udało mi się to zrobić, ale z pewnymi modyfikacjami. Pomysł polega na użyciu LockModeType.PESSIMISTIC_FORCE_INCREMENT zamiast PESSIMISTIC_WRITE. Używając tego trybu blokady, zadania Cron zachowują się w następujący sposób:
- Kiedy pierwsze zadanie wybiera aktualizację, wszystko idzie zgodnie z oczekiwaniami, ale zmienia się wersja obiektu.
- Jeśli inne zadanie próbuje dokonać tego samego wyboru, podczas gdy pierwsze jest nadal w swojej transakcji, JPA uruchamia wyjątek OptimisticLockException, więc jeśli przechwycisz ten wyjątek, możesz być pewien, że został on rzucony z powodu blokady odczytu.
To rozwiązanie ma różne odpowiedniki:
- SynchronizedCronJobTask musi mieć pole wersji i być pod kontrolą wersji za pomocą @Version
- Musisz obsłużyć OptimisticLockException i powinien być on przechwycony poza metodą usługi transakcyjnej, aby dokonać wycofania, gdy nastąpi odblokowanie.
- IMHO to nieeleganckie rozwiązanie, znacznie gorsze niż zwykła blokada, w której zadania Cron czekają na zakończenie poprzednich zadań.