2012-06-30 13 views
11

Czy istnieje proste rozwiązanie do zapisywania danych w bazie danych przy użyciu WZP w nowym wątku?Wywoływanie metod @Transactional z innego wątku (Runnable)

Aplikacja internetowa oparta na mojej wiosce pozwala użytkownikowi zarządzać zaplanowanymi zadaniami. W czasie wykonywania może tworzyć i uruchamiać nowe instancje predefiniowanych zadań. Używam Spring TaskScheduler i wszystko działa dobrze.

Ale muszę zapisać wynik boolean każdego wypalonego zadania do bazy danych. Jak mogę to zrobić?

EDYTOWANIE: Muszę uogólnić moje pytanie: muszę wywołać metodę na mojej klasie @Service z zadań. Ponieważ wynik zadania musi zostać "przetworzony" przed zapisaniem w bazie danych.

EDIT 2: Uproszczona wersja mojego problematycznego kodu przychodzi tutaj. Kiedy funkcja saveTaskResult() jest wywoływana z programu planującego, komunikat jest drukowany, ale nic nie jest zapisywane w db. Ale za każdym razem, gdy wywoływam saveTaskResult() ze sterownika, rekord jest poprawnie zapisywany w bazie danych.

@Service 
public class DemoService { 

    @Autowired 
    private TaskResultDao taskResultDao; 

    @Autowired 
    private TaskScheduler scheduler; 

    public void scheduleNewTask() { 
     scheduler.scheduleWithFixedDelay(new Runnable() { 

      public void run() { 
       // do some action here 
       saveTaskResult(new TaskResult("result")); 
      } 

     }, 1000L); 
    } 

    @Transactional 
    public void saveTaskResult(TaskResult result) { 
     System.out.println("saving task result"); 
     taskResultDao.persist(result); 
    } 

} 
+0

Jaki jest dokładnie problem? Zaczynasz wątek, nazywasz swoje usługi wiosenne dokładnie tak, jak zrobiłbyś, gdybyś nie rozpoczął wątku, i wszystko powinno być w porządku. –

+0

Problemem jest metoda biznesowa celu @Transactional. Kiedy wywołasz tę metodę w run(), dane nie zostaną utracone. (Mam zaktualizowany tytuł pytania) –

+0

Przechwytywacz transakcyjny nie dba o to, czy wątek wywołujący metodę jest wątkiem utworzonym przez twój kontener serwletu, czy wątkiem utworzonym przez ciebie. Powinno działać. Pokaż nam jakiś kod. –

Odpowiedz

14

Problem z kodem jest to, że można się spodziewać transakcji należy rozpocząć, gdy dzwonisz saveTaskResult(). Nie stanie się tak, ponieważ Spring używa AOP do uruchamiania i zatrzymywania transakcji.

Jeśli masz instancję transakcyjnej wiosennym fasoli z fabryki świętojańskiego lub poprzez wstrzyknięcie zależności, co masz jest w rzeczywistości proxy wokół fasoli. Ten serwer proxy rozpoczyna transakcję przed wywołaniem właściwej metody i zatwierdza lub wycofuje transakcję po zakończeniu metody.

W takim przypadku należy wywołać lokalną metodę komponentu bean, bez przechodzenia przez proxy transakcyjne. Umieść metodę saveTaskResult() (opisaną jako @Transactional) w innym komponencie Spring bean. Wrzuć tę inną Spring bean do DemoService i wywołaj inną Spring bean z DemoService, a wszystko będzie dobrze.

+0

Świetnie, działa. Dokładnie to musiałem wiedzieć. Wielkie dzięki –

+0

Dzięki za milion. to rozwiązanie działa z urokiem podobnym do CompletableFuture. –

2

Transakcje są przechowywane w lokalnym magazynie wątków.
Jeśli w innej metodzie jest uruchomiony wątek z adnotacją @Transactional.
Domyślnym ustawieniem jest REQUIRED, co oznacza, że ​​jeśli uruchomisz metodę opatrzoną komentarzem @Transacitonal z innego wątku, otrzymasz nową transakcję (ponieważ nie ma żadnej transakcji w magazynie lokalnym wątku tego wątku).

Powiązane problemy