Вопрос по runnable, spring, java – Вызов методов @Transactional из другого потока (Runnable)

13

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

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

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

РЕДАКТИРОВАТЬ: Я должен обобщить свой вопрос: мне нужно вызвать метод из моего класса @Service из задач. Потому что результат задачи должен быть «обработан» перед сохранением в базу данных.

РЕДАКТИРОВАТЬ 2: Упрощенная версия моего проблемного кода приведена здесь. Когда saveTaskResult () вызывается из планировщика, сообщение распечатывается, но ничего не сохраняется в БД. Но всякий раз, когда я вызываю 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);
    }

}
Какой менеджер транзакций Spring вы используете? Philippe Marschall
Пример кода добавлен в исходный вопрос Ondřej Míchal
Проблема в том, что целевой бизнес-метод - @Transactional. Когда я вызываю этот метод в run (), данные не сохраняются. (Я обновил название вопроса) Ondřej Míchal
Транзакционный перехватчик не заботится, является ли поток, вызывающий метод, потоком, созданным вашим контейнером сервлета, или потоком, созданным вами. Он должен работать. Покажите нам код. JB Nizet
в чем именно проблема?: Вы запускаете поток, вызываете сервисы Spring точно так же, как если бы вы не запускали поток, и все должно быть в порядке. JB Nizet

Ваш Ответ

2   ответа
19

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

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

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

Бесконечно благодарен. это решение работает с CompletableFuture как очарование.
Отлично, это работает. Это именно то, что мне нужно было знать. большое спасибо Ondřej Míchal
3

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

Это на самом деле очень правильно, это не ответ, хотя лучше будет комментировать. В данном примере это не имеет значения, но если scheduleNewTask уже был @Transactional, то действительно, saveTaskResult запустит новую транзакцию, даже если для нее установлено значение REQUIRED, которая присоединится к существующей транзакции, если она существует.
Here вы найдете хитрость для этого НО, иногда в этом нет никакого смысла. F.I. все общедоступные методы в соединении Oracle синхронизированыOracle Docs Этот подход будет использовать то же соединение. Вы ничего не выиграете, даже если это сработает (не проверял это сам, так что не могу быть уверен). Вид уведомления, проверьте документацию вашего подключения.

Похожие вопросы