Как настроить JOOQ для передачи шестнадцатеричных строк вместо восьмеричных для вставки столбца байтов в PostgreSQL?

Мы используем JOOQ для вставки записей в таблицу postgresql со столбцом bytea.

Отправляемый нами массив байтов имеет размер около 150 МБ, и мы получаем ошибку от сервера Postgresql:

ОШИБКА: неверный размер запроса на выделение памяти 1073741824.

Покопавшись в коде и запросе, который генерирует JOOQ, мы заметили, что byteArray преобразуется в восьмеричный (org.jooq.util.postgres.PostgresUtils#toPGString(byte[])) И в итоге запрос будет выглядеть примерно так:

INSERT INTO test_table (my_data) VALUES (E'\\042\\145\\171...\\042'::bytea);

Тестирование с меньшими массивами байтов показывает, что после вставки данных их размер действительно равен размеру массива байтов, а не размеру восьмеричной строки (которая намного длиннее).

Проблема, по-видимому, заключается в том, что восьмеричная строка настолько длинная, что сервер postgresql не может преобразовать ее в bytea даже до попытки сохранить ее в столбце.

Есть ли способ настроить JOOQ для передачи строки hex в качестве значения вместо octal?

Мы используем версию JOOQ 3.16.4 и postgresql 13.9

Уточнение редактирования:

JOOQ настроен по умолчанию DefaultConfiguration. Никаких существенных изменений не происходит.

Приведенный выше sql взят из созданного исключения: org.jooq.impl.Tools#translate(java.lang.String, java.lang.RuntimeException)

И sql, который он печатает, выглядит так:

org.jooq.exception.DataAccessException: SQL [insert into "public"."table_name" ("id", "name", "version", "description", "archived", "other_id", "jws_data") values (829720463911690240, 'v-0', 'rb-0-6HW7', null, 0, 829720463899107328, E'\\042\\145\\171\\112\\162\\141\\127\\....\135\\175'::bytea) returning "public"."table_name"."id"]; Unspecified RuntimeException

Мы не генерируем вставку SQL, вместо этого мы используем org.jooq.impl.DAOImpl#insert(P) и передаем сгенерированный JOOQ объект Record, который содержит byteArray в качестве одного из членов (jws_data). это имя также соответствует имени столбца в таблице.

Примечание. Я немного изменил SQL, чтобы скрыть точные имена столбцов, поэтому игнорируйте имена и несоответствия с другими SQL.

Трассировки стека:

    toPGString:631, PostgresUtils (org.jooq.util.postgres)
    sqlInline0:2044, DefaultBinding$DefaultBytesBinding (org.jooq.impl)
    sqlInline0:1941, DefaultBinding$DefaultBytesBinding (org.jooq.impl)
    sql:937, DefaultBinding$AbstractBinding (org.jooq.impl)
    sql:929, DefaultBinding$AbstractBinding (org.jooq.impl)
    accept:186, Val (org.jooq.impl)
    visit0:720, DefaultRenderContext (org.jooq.impl)
    visit:295, AbstractContext (org.jooq.impl)
    toSQL92Values:326, FieldMapsForInsert (org.jooq.impl)
    toSQL92Values:278, FieldMapsForInsert (org.jooq.impl)
    accept:137, FieldMapsForInsert (org.jooq.impl)
    visit0:720, DefaultRenderContext (org.jooq.impl)
    visit:295, AbstractContext (org.jooq.impl)
    toSQLInsert:739, InsertQueryImpl (org.jooq.impl)
    accept0:624, InsertQueryImpl (org.jooq.impl)
    accept:642, AbstractDMLQuery (org.jooq.impl)
    visit0:720, DefaultRenderContext (org.jooq.impl)
    visit:295, AbstractContext (org.jooq.impl)
    getSQL0:484, AbstractQuery (org.jooq.impl)
    execute:287, AbstractQuery (org.jooq.impl)
    storeInsert0:191, TableRecordImpl (org.jooq.impl)
    lambda$storeInsert$0:157, TableRecordImpl (org.jooq.impl)
    apply:-1, TableRecordImpl$$Lambda$2120/0x0000000801a5bef8 (org.jooq.impl)
    operate:143, RecordDelegate (org.jooq.impl)
    storeInsert:156, TableRecordImpl (org.jooq.impl)
    insert:144, TableRecordImpl (org.jooq.impl)
    insert:139, TableRecordImpl (org.jooq.impl)
    insert:180, DAOImpl (org.jooq.impl)
    insert:156, DAOImpl (org.jooq.impl)
    save:80, RDBDao (org.jfrog.bintray.distribution.rdb)

То же поведение можно воспроизвести, вызвав:

dslContext.insertInto(TEST_TABLE)
          .set(MY_DATA, jws_data_byte_array)
          .execute();

Или

dslContext.execute("insert into test_table (my_data) values (?)", jws_data_byte_array);

Срабатывает тот же toPGString:631, PostgresUtils (org.jooq.util.postgres).

Почему вы встраиваете значения привязки?

Lukas Eder 11.04.2024 18:08

Наверняка вам стоит использовать PreparedStatement::setBinaryStream?

g00se 11.04.2024 18:10

Предоставленный мной запрос является лишь примером того, как выглядит запрос. Мы используем DAOImpl.insert() с сгенерированными объектами Record.

Eli Skoran 11.04.2024 19:13

Отправленный «настоящий» запрос немного длиннее и имеет гораздо больше параметров: insert into "public"."our_table" ("id", "name", "version", "description", "archived", "other_id", "jws_data") values (829720463911690240, 'v-0', 'rb-0-6HW7', null, 0, 829720463899107328, E'\\042\\145\\171\\112\\162\\141\\127\\121\\151\\117\\1...)

Eli Skoran 11.04.2024 19:18

@EliSkoran: jOOQ всегда генерирует PreparedStatement со значениями привязки по умолчанию. Почему ваше значение привязки становится встроенным? Как вы настроили jOOQ и почему?

Lukas Eder 12.04.2024 10:09

@LukasEder Спасибо за ответ, пожалуйста, ознакомьтесь с поясняющей поправкой к сообщению.

Eli Skoran 12.04.2024 12:23

@EliSkoran: но мой вопрос все еще остается: почему ваши значения привязки встраиваются? Можете ли вы опубликовать стек вызовов, который ведет к (org.jooq.util.postgres.PostgresUtils#toPGString(byte[]))?

Lukas Eder 12.04.2024 17:59

@LukasEder Добавлена ​​трассировка стека.

Eli Skoran 12.04.2024 18:19

Кажется, вы используете старую версию jOOQ, но это, вероятно, не имеет значения. Ваш dslContext, скорее всего, настроен на встроенные значения привязки. Пожалуйста, расследуйте это. Нет веской причины, по которой этот стек вызовов был бы выбран в противном случае (от DefaultBinding$AbstractBinding.sql() до DefaultBinding$DefaultBytesBinding.sqlInline0()). Вы можете установить там точку останова или подняться вверх по стеку вызовов, чтобы увидеть, где, например, Settings.statementType установлено в STATIC_STATEMENT или Settings.paramType установлено в INLINED (там может быть могут быть и другие причины). Возможно, это делает ваша третья сторона RDBDao?

Lukas Eder 19.04.2024 10:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
9
140
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как упомянул @Lukas Eder, Мы нашли место, где параметр StatementType был изменен на STATIC_STATEMENT вместо значения по умолчанию PREPARED_STATEMENT.

После удаления этой конфигурации JOOQ перестал создавать длинные восьмеричные строки, и вставка работает отлично.

Другие вопросы по теме