Вызов методов @Transactional из другого потока (Runnable)

Есть ли какое-нибудь простое решение для сохранения данных в базу данных с использованием JPA в новом потоке?

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

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

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

ИЗМЕНИТЬ 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);
    }

}

Ответ 1

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

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

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

Ответ 2

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