Можно ли написать конвертер 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());
В целом решение кажется хакерским, и мне нужно написать монстра модульного теста, чтобы убедиться, что он не сломается при обновлении любого из соответствующих пакетов, но, по крайней мере, я могу читать и написать в мою базу данных.