quarkus: IllegalStateException: вы попытались выполнить операцию блокировки в потоке ввода-вывода. Это недопустимо, так как блокирование потока ввода-вывода

Есть одна вещь, которую я не могу понять с кваркусом. Я использую JPA с Oracle. Итак, у меня ошибка IllegalStateException: вы попытались выполнить операцию блокировки в потоке ввода-вывода. Это недопустимо, так как блокирование потока ввода-вывода я посмотрел в документации Quarkus, как выполнять вызовы JPA без этой проблемы. Но все примеры и документы используют PostgreSQL или MariaDB с отзывчивыми клиентами. Но для классических клиентов JDBC я не нашел.

Я нашел решение, которое работает частично.

Uni.cretaFrom().Items(() -> MyBlokingIOCall());

На самом деле для меня больше нет исключения. Но метод MyBlokingIOCall может вызывать исключения. Я думаю, что могу использовать Uni onFailure, но мы не можем передавать методы, вызывающие исключение, в

Uni.cretaFrom().Items

На самом деле этот метод имеет в качестве аргумента поставщика, поэтому исключения должны быть зафиксированы. с этим решением невозможно использовать Uni onFailure. Если я использую текущий код в императивном режиме

try {
   Response.ok (myService ());
} catch (throwable e) {
   Response.status (555, e.getMessage ());
}

Я тщетно пытался сделать это в реактивной форме, но решения не нашел. Никакой помощи не нашел. Я представил себе что-то подобное.

Uni.createFrom().Items(() -> myService())
.onItem().apply(data -> Response.ok(data))
.onFailure().apply(err -> Response.status(555, err.getMessage()))

Но это вызывает ошибку компиляции. действительно, метод myService нельзя использовать в Supplier, потому что он генерирует исключение. поэтому он должен быть захвачен. Но тогда мы больше не входим в onFailure Uni и не можем отследить обнаруженную ошибку.

Я думаю, что слишком много думаю о том, чтобы использовать свой императивный код вместо того, чтобы думать иначе.

Но я никак не могу найти документ или пример, который бы выглядел так. Я полагаю, что есть способ делать что-то (если бы не Quarkus, не существовало бы). Я думаю, что когда вы думаете об этом с правильным подходом, вам нужно найти документ, но когда вы не знаете, куда идти, все сложнее. Я не нашел, потому что не знаю, что ищу.

Я предполагаю, что мне нужно инкапсулировать вызов JPA, который создает Uni или Multi, который потребляется процессором, который преобразует Entity в DTO, который затем передается на уровень Rest ресурсов, который преобразует DTO в Response. вызов JPA должен иметь возможность создавать ошибки, как процессор, так и уровень ресурсов тоже

Следовательно, необходимо на каждом этапе фиксировать эти ошибки, чтобы распространять их через Uni или Multi

Но как это сделать?

2 ответа

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

Между тем, это можно просто обойти, реализовав расширение вокруг ожидаемого функционального интерфейса, в вашем случае это будетSupplier<T>, который перехватит любое ** проверенное ** исключение и выдаст непроверенное:

/**
 * A Supplier sub-interface that catches any thrown exception
 * and translates to an unchecked one.
 *
 * <p>This is a functional interface whose functional method is
 * {@link #getChecked()}.
 */
@FunctionalInterface
public interface CheckedSupplier<T> extends Supplier<T> {

    @Override
    default T get() {
        try {
            return getChecked();
        } catch (final Exception e) {
            // Your common exception handling logic here..
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets a result.
     *
     * @return a result
     * @throws Exception
     */
    T getChecked() throws Exception;
}

Обратите внимание, что подпись функционального интерфейса больше не #get(), а #getChecked(). Но это не повлияет на компилятор, который попытается проверить функциональную сигнатуру на основе ожидаемой, а именно:

метод, возвращающий объект типа T

Тогда вы можете просто использовать новый CheckedSupplierинтерфейс, используя только явное приведение везде, гдеSupplier<T> (или подобное) ожидается:

Uni.createFrom().item((CheckedSupplier<DataType>) MutinyTest::findOne)
      .onItem()
      .apply(i-> Response.ok(i))
      .onFailure()
      .apply(e-> Response.status(555, e.getMessage()));

Я изменил свой метод myService, чтобы он возвращал поставщика и обрабатывал исключения.

   public Supplier<List<String>> myService (String arg){
      return () -> {
         try {
            //my code
            return result;
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
      };
   }

и я назвал это так

Uni.createFrom().item(myService("sample")))
.onItem().apply(data -> Response.ok(data))
.onFailure().recoverWithItem(err -> Response.status(600, err.getMessage()))
.onItem().apply(ResponseBuilder::build)
.emitOn(Infrastructure.getDefaultExecutor())
Другие вопросы по тегам