JPA репозитории и блокировка ввода / вывода - PullRequest
3 голосов
/ 24 мая 2019

У меня возникла проблема, когда мне нужно выполнить несколько медленных HTTP-запросов в отдельном потоке после записи в базу данных с использованием JpaRepository. Проблема в том, что doActualJob() блокируется в ожидании разрешения серии фьючерсов. Это, по-видимому, препятствует закрытию основного сеанса Hibernate, что приводит к тому, что у приложения заканчиваются соединения вскоре после этого.

Как мне написать эту функцию, чтобы соединение с базой данных не оставалось открытым во время блокирующего ввода-вывода? Возможно ли даже использование JpaRepositories или мне нужно использовать API более низкого уровня, например EntityManager / SessionFactory?

@Service
class SomeJobRunner {

    private final SomeJobRepository mSomeJobRepository; //extends JpaRepository

    @AutoWired
    public SomeJobRunner(final SomeJobRepository someJobRepository) {
        mSomeJobRepository = someJobRepository;
    }

    @Async
    public void doSlowJob(final long someJobId) {
        SomeJob someJob = mSomeJobRepository.findOne(someJobId);
        someJob.setJobStarted(Instant.now());
        mSomeJobRepository.saveAndFlush(someJob);

        doActualjob(); // Synchronous job doing several requests using Unirest in series

        someJob = mSomeJobRepository.findOne(someJobId);
        someJob.setJobEnded(Instant.now());
        mSomeJobRepository.saveAndFlush(someJob);
}

Ответы [ 2 ]

3 голосов
/ 24 мая 2019

Что ж, неблокируемый ввод-вывод в базу данных невозможен в мире Java / JDBC стандартным образом. Проще говоря, в вашем хранилище данных Spring в конечном итоге будет использоваться реализация JPA ORM (например, Hibernate), которая, в свою очередь, будет использовать JDBC. взаимодействовать с базой данных, которая по сути является блокирующей по своей природе. В настоящее время Oracle проделывает работу над этим ( Асинхронный доступ к базе данных API ), чтобы обеспечить API, аналогичный JDBC, но не блокирующий. Они намерены предложить это в качестве стандарта. Кроме того, ребята из Spring прилагают увлекательные и параллельные усилия в этом направлении, а именно R2DBC - Reactive Relational Database Connectivity . Они фактически интегрировали это и с данными Spring ( link ), что может помочь вам интегрироваться в ваше решение. Хороший урок от Spring можно найти здесь здесь .

0 голосов
/ 24 мая 2019

Я бы предложил записать в базу данных, используя отдельную JTA транзакцию .Сделайте так, определите метод как

@Transactional(Transactional.TxType.REQUIRES_NEW)
public void saveJobStart(final long someJobId) {
    SomeJob someJob = mSomeJobRepository.findOne(someJobId);
    someJob.setJobStarted(Instant.now());
    mSomeJobRepository.saveAndFlush(someJob);
}

Конечно, это не совсем то же самое.Если произойдет сбой doActualjob(), в вашем случае база данных не сохранит дату начала.В моем предложении это будет сохраняться.Чтобы компенсировать это, вам нужно удалить дату начала в блоке catch в doSlowJob в новой транзакции, а затем повторно throw исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...