Как выполнить запрос SELECT пользовательской функции PL/pgSQL в jOOQ, который возвращает тип JSON?

У меня проблема с поиском правильного способа преобразования моей пользовательской функции в PL/pgSQL в код jOOQ. Моя пользовательская функция в PL/pgSQL возвращает тип JSON, и мне нужно как-то настроить/привести его в jOOQ. Я гуглил примеры, но не нашел.

На всякий случай вот моя пользовательская функция в PL/pgSQL:

create or replace function public.get_order_by_order_id(o_id bigint) returns json as
$BODY$
DECLARE
    total_oi_price double precision;
    book_price double precision;
    total_price double precision;
    oi_amount integer;
    order_items json;
    item_recs RECORD;
    book_json json;
    single_order_item json;
    found_order "vertx-jooq-cr".public.orders;
    found_user json;
    _item_id bigint;
    item_array json[];
BEGIN
    select * into found_order
    from "vertx-jooq-cr".public.orders
    where order_id = o_id;

    select json_build_object('user_id', "vertx-jooq-cr".public.users.user_id, 'username', "vertx-jooq-cr".public.users.username)
    into found_user
    from "vertx-jooq-cr".public.users
    INNER JOIN "vertx-jooq-cr".public.orders as o USING (user_id)
    WHERE o.order_id = o_id;

    total_price = 0.00;

    FOR item_recs IN SELECT *
        FROM public.order_item AS oi WHERE oi.order_id = o_id
    LOOP
        select public.get_book_by_book_id(item_recs.book_id) into book_json
        from public.order_item
        where public.order_item.order_item_id IN (item_recs.order_item_id);

        select price INTO book_price FROM book AS b WHERE b.book_id = item_recs.book_id;
        select amount INTO oi_amount FROM order_item AS oi WHERE oi.amount = item_recs.amount;
        
        total_oi_price = book_price * oi_amount;

        SELECT json_build_object('order_item_id', item_recs.order_item_id,
        'amount', item_recs.amount,
        'book', book_json,
        'order_id', item_recs.order_id,
        'total_order_item_price', trunc(total_oi_price::double precision::text::numeric, 2)) INTO single_order_item;
        total_price := total_price + total_oi_price;
        item_array = array_append(item_array, single_order_item);
    END LOOP;
    order_items = array_to_json(item_array);

    return (select json_build_object(
        'order_id', found_order.order_id,
        'total_price', trunc(total_price::double precision::text::numeric, 2),
        'order_date', found_order.order_date,
        'user', found_user,
        'order_items', order_items
    ));

end;
$BODY$
LANGUAGE 'plpgsql';

... и еще один, который использует функцию, указанную выше.

CREATE OR REPLACE FUNCTION get_all_orders() RETURNS JSON AS
$BODY$
DECLARE
    single_order RECORD;
    single_order_json json;
    orders_array json[];
BEGIN

    FOR single_order IN SELECT * FROM public.orders ORDER BY order_id
    LOOP
        SELECT get_order_by_order_id(single_order.order_id) INTO single_order_json;
        orders_array = array_append(orders_array, single_order_json);
    END LOOP;

    return (select json_build_object(
        'orders', orders_array
    ));
END;
$BODY$
LANGUAGE 'plpgsql';

Обе функции были успешно сгенерированы кодом в моем проекте Maven, и последняя get_all_orders() необходима для выполнения над ней операции SELECT и возврата объекта JSON в моем коде jOOQ.

Вот класс Routines.java в пакете **.jooq, который генерируется среди Keys.java, DefaultCatalog.java и других классов:

/**
 * Convenience access to all stored procedures and functions in public
 */
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class Routines {

    /**
     * Call <code>public.get_all_orders</code>
     */
    public static JSON getAllOrders(Configuration configuration) {
        GetAllOrders f = new GetAllOrders();

        f.execute(configuration);
        return f.getReturnValue();
    }

    /**
     * Get <code>public.get_all_orders</code> as a field.
     */
    public static Field<JSON> getAllOrders() {
        GetAllOrders f = new GetAllOrders();

        return f.asField();
    }

// other methods left out for code brevity

/**
     * Call <code>public.get_order_by_order_id</code>
     */
    public static JSON getOrderByOrderId(Configuration configuration, Long oId) {
        GetOrderByOrderId f = new GetOrderByOrderId();
        f.setOId(oId);

        f.execute(configuration);
        return f.getReturnValue();
    }

    /**
     * Get <code>public.get_order_by_order_id</code> as a field.
     */
    public static Field<JSON> getOrderByOrderId(Long oId) {
        GetOrderByOrderId f = new GetOrderByOrderId();
        f.setOId(oId);

        return f.asField();
    }

    /**
     * Get <code>public.get_order_by_order_id</code> as a field.
     */
    public static Field<JSON> getOrderByOrderId(Field<Long> oId) {
        GetOrderByOrderId f = new GetOrderByOrderId();
        f.setOId(oId);

        return f.asField();
    }
}

А вот мой рутинный класс GetAllOrders.java, расположенный в пакете **.jooq.routines

/**
 * This class is generated by jOOQ.
 */
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class GetAllOrders extends AbstractRoutine<JSON> {

    private static final long serialVersionUID = 917599810;

    /**
     * The parameter <code>public.get_all_orders.RETURN_VALUE</code>.
     */
    public static final Parameter<JSON> RETURN_VALUE = Internal.createParameter("RETURN_VALUE", org.jooq.impl.SQLDataType.JSON, false, false);

    /**
     * Create a new routine call instance
     */
    public GetAllOrders() {
        super("get_all_orders", Public.PUBLIC, org.jooq.impl.SQLDataType.JSON);

        setReturnParameter(RETURN_VALUE);
    }
}

Наконец, это мой код jOOQ для выполнения запроса SELECT в jOOQ:

Future<JsonObject> ordersFuture = queryExecutor.transaction(qe -> qe
                .query(dsl -> dsl
                    .select(new Routines().getAllOrders())
            )); 

... определение метода qe.query() приведено ниже:

@Override
    public <R extends Record> Future<QueryResult> query(Function<DSLContext, ? extends ResultQuery<R>> queryFunction) {
        return executeAny(queryFunction).map(ReactiveQueryResult::new);
    }

Созданные проблемы:

Несоответствие типов: невозможно преобразовать Future ‹ Object › в Future ‹ JsonObject ›

Несоответствие типов: невозможно преобразовать Future ‹ QueryResult › в Future ‹ Object ›

Кстати, я должен упомянуть, что это vertx-jooq, которая использует jOOQ 3.13.1.
Любая помощь/предложение приветствуется.

ОБНОВЛЕНИЕ:
В соответствии с запросом здесь указаны отсутствующие типы и подписи 1st для метода transaction() (дополнительная информация ее )

/**
     * Convenience method to perform multiple calls on a transactional QueryExecutor, committing the transaction and
     * returning a result.
     * @param transaction your code using a transactional QueryExecutor.
     *                    <pre>
     *                    {@code
     *                    ReactiveClassicGenericQueryExecutor nonTransactionalQueryExecutor...;
     *                    Future<QueryResult> resultOfTransaction = nonTransactionalQueryExecutor.transaction(transactionalQueryExecutor ->
     *                      {
     *                          //make all calls on the provided QueryExecutor that runs all code in a transaction
     *                          return transactionalQueryExecutor.execute(dslContext -> dslContext.insertInto(Tables.XYZ)...)
     *                              .compose(i -> transactionalQueryExecutor.query(dslContext -> dslContext.selectFrom(Tables.XYZ).where(Tables.XYZ.SOME_VALUE.eq("FOO")));
     *                      }
     *                    );
     *                    }
     *                    </pre>
     * @param <U> the return type.
     * @return the result of the transaction.
     */
    public <U> Future<U> transaction(Function<ReactiveClassicGenericQueryExecutor, Future<U>> transaction){
        return beginTransaction()
                .compose(queryExecutor -> transaction.apply(queryExecutor) //perform user tasks
                        .compose(res -> queryExecutor.commit() //commit the transaction
                                .map(v -> res))); //and return the result
    }

... и executeAny() (дополнительная информация доступна здесь):

 /**
     * Executes the given queryFunction and returns a <code>RowSet</code>
     * @param queryFunction the query to execute
     * @return the results, never null
     */
    public Future<RowSet<Row>> executeAny(Function<DSLContext, ? extends Query> queryFunction) {
        Query query = createQuery(queryFunction);
        log(query);
        Promise<RowSet<Row>> rowPromise = Promise.promise();
        delegate.preparedQuery(toPreparedQuery(query)).execute(getBindValues(query),rowPromise);
        return rowPromise.future();
    }

... и здесь для ReactiveQueryResult

ОБНОВЛЕНИЕ2:
Это мой результат функции get_all_orders(), созданной в PL/pgSQL типа JSON:

{
  "orders": [
    {
      "order_id": 1,
      "total_price": 29.99,
      "order_date": "2019-08-22T10:06:33",
      "user": {
        "user_id": 1,
        "username": "test"
      },
      "order_items": [
        {
          "order_item_id": 1,
          "amount": 1,
          "book": {
            "book_id": 1,
            "title": "Harry Potter and the Philosopher's Stone",
            "price": 29.99,
            "amount": 400,
            "is_deleted": false,
            "authors": [
              {
                "author_id": 4,
                "first_name": "JK",
                "last_name": "Rowling"
              }
            ],
            "categories": [
              {
                "category_id": 2,
                "name": "Lyric",
                "is_deleted": false
              }
            ]
          },
          "order_id": 1,
          "total_order_item_price": 29.99
        }
      ]
    },
    {
      "order_id": 2,
      "total_price": 29.99,
      "order_date": "2019-08-22T10:10:13",
      "user": {
        "user_id": 1,
        "username": "test"
      },
      "order_items": [
        {
          "order_item_id": 2,
          "amount": 1,
          "book": {
            "book_id": 1,
            "title": "Harry Potter and the Philosopher's Stone",
            "price": 29.99,
            "amount": 400,
            "is_deleted": false,
            "authors": [
              {
                "author_id": 4,
                "first_name": "JK",
                "last_name": "Rowling"
              }
            ],
            "categories": [
              {
                "category_id": 2,
                "name": "Lyric",
                "is_deleted": false
              }
            ]
          },
          "order_id": 2,
          "total_order_item_price": 29.99
        }
      ]
    }
  ]
}

person NikolaS    schedule 23.06.2020    source источник
comment
Можете ли вы показать код jOOQ, который вы использовали, и проблемы, которые он вызывал?   -  person Lukas Eder    schedule 23.06.2020
comment
Привет Лукас, я обновил свой вопрос. Пожалуйста, взгляните.   -  person NikolaS    schedule 23.06.2020
comment
Хорошо, так что это не связано с JSON. Можете ли вы привести полностью автономный пример? В текущем вопросе по-прежнему отсутствуют типы и подписи, такие как queryExecutor.transaction, executeAny(...), ReactiveQueryResult,...   -  person Lukas Eder    schedule 23.06.2020
comment
Я обновил свой вопрос, добавив сигнатуры методов (включая их тела), а также ссылки на репозиторий GitHub, где можно увидеть исходный код. Кстати, возможно ли, что что-то вроде этот вопрос должен быть сделано ... может быть, специальный конвертер?   -  person NikolaS    schedule 23.06.2020


Ответы (1)


Из середины вашего вопроса:

Future<JsonObject> ordersFuture = queryExecutor.transaction(qe -> qe.query(...)); 

// Where
<R extends Record> Future<QueryResult> query(
  Function<DSLContext, ? extends ResultQuery<R>> queryFunction
) { ... }

Компилятор Java не может преобразовать ваш Future<QueryResult> в Future<JsonObject>. Могут быть некоторые дополнительные проблемы с выводом типа, которые приводят к запутанному сообщению об ошибке, которое не говорит об этом как есть, но это то, что я вижу.

Вы должны каким-то образом явно сопоставить Future<QueryResult> с Future<JsonObject>. В любом случае метод transaction() не выполняет это преобразование:

<U> Future<U> transaction(
  Function<ReactiveClassicGenericQueryExecutor, Future<U>> transaction
) { ... }

Здесь тип <U> привязывается к QueryResult из вашего метода query().

person Lukas Eder    schedule 24.06.2020
comment
Привет Лукас и спасибо за быстрый ответ. Итак, то, что вы говорите, не похоже на этот выпуск, где метод sql() (из класса который реализует интерфейс Binding<Object, JsonObject>) необходимо отредактировать, чтобы использовать тип json вместо типа jsonb (я ссылаюсь на ваш ответ в упомянутой проблеме)? - person NikolaS; 25.06.2020
comment
Я обновил свой вопрос, добавив результат SELECT get_all_orders(); (т.е. функцию PL/pgSQL) - person NikolaS; 25.06.2020
comment
Кроме того, я прислушался к вашему совету и заменил тип JsonObject на тип QueryResult в переменной Future<JsonObject> ordersFuture, и ошибки исчезли! :) Теперь, о чем я спрашивал в своем предыдущем комментарии, нужно ли использовать класс Converter или Binding (этим страница Я полагаю, мне нужно использовать конвертер, верно)? - person NikolaS; 25.06.2020
comment
@NikolaS: Меня все больше и больше смущает этот вопрос :) Видите ли, я не сижу с вами за вашим компьютером, и я не знаю, что вы за это время выяснили, и какие у вас есть открытые вопросы. может еще есть. Ваш вопрос в том виде, в котором он сейчас отредактирован, объединяет всю вашу историю прогресса в этой ... сессии, но если кто-то посетит этот вопрос в будущем, он не поймет, что это был за вопрос или ответ. Могу я попросить вас задавать более короткие и лаконичные вопросы об отдельных проблемах? Вот полезный ресурс для этого: stackoverflow.com/help/how-to-ask - person Lukas Eder; 25.06.2020
comment
Вы правы, и я прошу прощения, я создам еще один вопрос, потому что мои вопросы в комментариях к вашему ответу сейчас выходят за рамки. :) - person NikolaS; 25.06.2020
comment
Вот ссылка на другой созданный вопрос, который (надеюсь) поясняет, чего я пытаюсь достичь. Пожалуйста, взгляните. - person NikolaS; 25.06.2020