2012-06-30 2 views
11

Есть ли какое-нибудь простое решение для сохранения данных в базу данных с использованием JPA в новом потоке?Вызов методов @Transactional из другого потока (Runnable)

Мое приложение на основе Spring позволяет пользователю управлять запланированными задачами. Во время выполнения он может создавать и запускать новые экземпляры предопределенных задач. Я использую Spring TaskScheduler, и все работает хорошо.

Но мне нужно сохранить логический результат каждой заданной задачи в базу данных. Как я могу это сделать?

EDIT: Я должен обобщать свой вопрос: мне нужно вызвать метод из моего класса @Service из задач. Потому что результат задачи должен быть «обработан» перед сохранением в базе данных.

EDIT 2: Упрощенная версия моего проблемного кода приходит сюда. Когда saveTaskResult() вызывается из планировщика, сообщение распечатывается, но ничего не сохраняется в db. Но всякий раз, когда я вызываю saveTaskResult() из контроллера, запись корректно сохраняется в базе данных.

@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

В чем же проблема? Вы начинаете поток, вы называете свои службы Spring именно так, как вы бы это сделали, если бы вы не начали нить, и все должно быть в порядке. –

+0

Проблема заключается в целевом бизнес-методе @Transactional. Когда я вызываю этот метод в run(), данные не сохраняются. (Я обновил заголовок вопроса) –

+0

Транзакционному перехватчику все равно, является ли поток, вызывающий метод, потоком, созданным вашим контейнером сервлетов, или потоком, созданным вами. Он должен работать. Покажите нам какой-то код. –

ответ

14

Проблема с кодом заключается в том, что вы ожидаете, что транзакция начнется, когда вы вызовете saveTaskResult(). Этого не произойдет, потому что Spring использует AOP для запуска и прекращения транзакций.

Если вы получаете экземпляр транзакционного весеннего боба с фабрики bean или через инъекцию зависимости, то вы получаете на самом деле прокси-сервер вокруг bean-компонента. Этот прокси запускает транзакцию перед вызовом фактического метода и совершает или откатывает транзакцию после завершения метода.

В этом случае вы вызываете локальный метод компонента, не пропуская транзакционного прокси. Поместите метод saveTaskResult() (аннотированный @Transactional) в другой весенний боб. Внесите этот другой компонент Spring в DemoService и вызовите другой компонент Spring из DemoService, и все будет хорошо.

+0

Отлично, он работает. Это именно то, что мне нужно было знать. Большое спасибо –

+0

Спасибо миллион. это решение работает с CompletableFuture как шарм. –

2

Сделки хранятся в местном хранилище потоков.
Если ваш другой метод работает с потоком с аннотацией @Transactional.
По умолчанию установлено значение REQUIRED, и это означает, что если вы запустите метод, аннотированный @Transacitonal из другого потока, у вас будет новая транзакция (так как в локальном хранилище этого потока нет транзакции, хранящейся в этом потоке).

Смежные вопросы