Можно ли написать конвертер jOOQ для применения к массиву?

У меня есть база данных postgres с некоторыми столбцами, которые имеют тип varchar[], jOOQ и pgjdbc-ng не очень хорошо играют друг с другом; DefaultBindContext в jOOQ имеет что-то вроде:

protected final BindContext bindValue0(Object value, Field<?> field) throws SQLException {
    SQLDialect dialect = configuration.dialect();

    // [#650] [#3108] Use the Field's Converter before actually binding any value
    Converter<?, ?> converter = field.getConverter();
    Class<?> type = converter.fromType();
    value = ((Converter) converter).to(value);

    //...

    else if (type.isArray()) {
        switch (dialect) {
            case POSTGRES: {
                stmt.setString(nextIndex(), toPGArrayString((Object[]) value));
                break;
            }

Что устанавливает переменную оператора в "{\"value1\", \"value2\", \"etc\"}", как вы бы указали массив в запросе. Позже, pgjdbc-ng имеет:

public static Object coerceToArray(Format format, Object val, Type type, Class<?> targetType, Map<String, Class<?>> typeMap, PGConnectionImpl connection) throws SQLException {
  if (val == null) {
    return null;
  }
  else if (val instanceof PGArray) {
    return coerceToArray(format, ((PGArray) val).getValue(), type, targetType, typeMap, connection);
  }
  else if (val.getClass().isArray()) {
    return coerceToArray(format, val, 0, Array.getLength(val), type, targetType, typeMap, connection);
  }

  throw createCoercionException(val.getClass(), targetType);
}

Что предполагает, что значение в операторе будет иметь тип PGArray или фактический массив; он не может привести строковое представление массива к строковому представлению массива.:(

Я пытаюсь написать JOOQ Converter, который будет конвертировать между String[] а также PGArray; в идеале это будет означать, что DefaultBindContext в jOOQ достаточно хорошо оставит преобразованное значение в одиночку, и тогда pgjdbc-ng сможет правильно его обработать.

Однако мне не удалось написать конфигурацию схемы jOOQ, которая позволяет мне это делать. Я пробовал варианты на:

<customType>    
    <customType>
        <name>StringArray</name>
        <type>java.lang.String[]</type>
        <converter>my.package.PGStringArrayConverter</converter>
    </customType>
</customTypes>

<forcedTypes>
    <forcedType>
        <name>StringArray</name>
        <types>varchar\[\]</types>
    </forcedType>
</forcedTypes>

Без всякой удачи; сгенерированные объекты таблицы относятся к String[][], а также varchar[] не совпадает ни на чем. Даже если я сломаю его, чтобы принудительный тип соответствовал любому типу, но с <expression> это соответствует только моему столбцу, и тип конвертера java.lang.StringЯ получаю компилятор Java, который жалуется на невозможность привести Object[] к String[].

Есть ли какой-нибудь свет в конце этого туннеля, или я должен начать искать для переноса моей базы данных?

1 ответ

Таким образом, получается, что ответ "вроде".

Я создавал свой PGStringArrayConverter класс задом наперед; Я создал

public abstract class PGArrayConverter implements Converter<String[], PGArray>

То, что закончило работать, было:

public abstract class PGArrayConverter implements Converter<Object, String[]> {
    @Override
    public String[] from(final Object databaseObject) {
        if (databaseObject == null) {
            return null;
        }
        if (databaseObject.getClass().isArray()) {
            return (String[]) databaseObject;
        }
        return (String[]) ((PGArray)databaseObject).getValue();
    }

    @Override
    public Object to(final String[] userObject) {
        if (userObject == null) {
            return null;
        }
        return new PGArray(null, null, userObject);
    }

    @Override
    public Class<Object> fromType() {
        return Object.class;
    }

    @Override
    public Class<String[]> toType() {
        return String[].class;
    }
}

from() оказалось странным; Я получал массивы Strings, а не PGArrays. Я не думаю, что база данных знала, что она "должна" сериализовать их в PGArrays.

Мне также пришлось изменить сгенерированный код, что немного неприятно. Мой файл schema.xml содержал:

<customType>
    <name>StringArray</name>
    <type>java.lang.String</type>
    <converter>my.package.PGStringArrayConverter</converter>
</customType>

<forcedType>
    <name>StringArray</name>
    <expression>.*tabular_answer_entry.text.*</expression>
    <types>.*</types>
</forcedType>

И мне пришлось изменить сгенерированные файлы таблицы, чтобы удалить .getArrayDataType() вызовите тип данных createField:

public final org.jooq.TableField<my.package.gen.tables.records.TabularAnswerEntryRecord, java.lang.String[]> TEXT = createField("text", org.jooq.impl.DefaultDataType.getDefaultDataType("java.lang.String").getArrayDataType(), this, "", new my.package.PGStringArrayConverter());

чтобы:

public final org.jooq.TableField<my.package.gen.tables.records.TabularAnswerEntryRecord, java.lang.String[]> TEXT = createField("text", org.jooq.impl.DefaultDataType.getDefaultDataType("java.lang.String"), this, "", new my.package.PGStringArrayConverter());

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

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