Postgres UUID JDBC не работает

Последние драйверы Java JDBC для postgres утверждают, что изначально поддерживают UUID; работает против Postgres 9.2 (Mac).

Действительно, когда используется PreparedStatement, я могу пошагово просмотреть код драйвера и даже пройти через специальную функцию setUuid в AbstractJdbc3gStatement.java. Судя по всему, он должен "просто работать".

Однако это не работает. База данных возвращает ошибку, которую я получаю таким образом:

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: uuid = bytea
  Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
  Position: 139
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157) ~[postgresql-9.2-1002.jdbc4.jar:na]

Да, действительно, setUuid в драйвере JDBC отправляет это как байту:

private void setUuid(int parameterIndex, UUID uuid) throws SQLException {
        if (connection.binaryTransferSend(Oid.UUID)) {
            byte[] val = new byte[16];
            ByteConverter.int8(val, 0, uuid.getMostSignificantBits());
            ByteConverter.int8(val, 8, uuid.getLeastSignificantBits());
            bindBytes(parameterIndex, val, Oid.UUID);
        } else {
            bindLiteral(parameterIndex, uuid.toString(), Oid.UUID);
        }
    }

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

3 ответа

ТЛ; др

myPreparedStatement.setObject( 
    … , 
    java.util.UUID.randomUUID()
)

подробности

(а) Покажите нам свой код.

PreparedStatement::setObject работает при прохождении java.util.UUID, Вероятно, у вас есть другая проблема в вашем коде.

(б) См. мой пост в блоге Значения UUID от JDBC до Postgres для небольшого обсуждения и примера кода.

// Generate or obtain data to store in database.
java.util.UUID uuid = java.util.UUID.randomUUID(); // Generate a random UUID. 
String foodName = "Croissant";
// JDBC Prepared Statement.
PreparedStatement preparedStatement = conn.prepareStatement( "INSERT INTO food_ (pkey_, food_name_  ) VALUES (?,?)" );
int nthPlaceholder = 1; // 1-based counting (not an index).
preparedStatement.setObject( nthPlaceholder++, uuid ); 
preparedStatement.setString( nthPlaceholder++, foodName ); 
// Execute SQL.
if ( !( preparedStatement.executeUpdate() == 1 ) ) { 
  // If the SQL reports other than one row inserted…
  this.logger.error( "Failed to insert row into database." );
}

(с) Я не уверен, что вы подразумеваете под

Последние драйверы Java JDBC для postgres утверждают, что изначально поддерживают UUID

Какой водитель? Существует как минимум два драйвера JDBC с открытым исходным кодом для Postgres: текущий / устаревший и новый переписанный "следующего поколения". Есть и другие коммерческие драйверы.

"Родной"? Можете ли вы дать ссылку на документацию, которую вы прочитали? В спецификации SQL нет типа данных для UUID (к сожалению, ☹), поэтому в спецификации JDBC нет типа данных для UUID. В качестве обходного пути драйвер JDBC для Postgres использует setObject а также getObject методы в PreparedStatement перемещают UUID через пропасть между Java, SQL, Postgres. Смотрите пример кода выше.

Как сказано в документе PreparedStatement JDBC:

Если требуются произвольные преобразования типов параметров, следует использовать метод setObject с целевым типом SQL.

Возможно, "изначально" вы перепутали собственную поддержку Postgres для UUID как типа данных с JDBC, имеющим тип данных UUID. Postgres действительно поддерживает UUID как тип данных, что означает, что значение сохраняется как 128-битное, а не многократно, если оно было сохранено как шестнадцатеричная строка ASCII или Unicode. Быть нативным также означает, что Postgres знает, как построить индекс для столбца этого типа.

Суть моего поста, упомянутого выше, заключалась в том, что я был приятно удивлен тем, насколько просто преодолеть эту пропасть между Java ↔ SQL ↔ Postgres, В моих первых необразованных попытках я работал слишком усердно.


Еще одно замечание о том, что Postgres поддерживает UUID... Postgres знает, как хранить, индексировать и извлекать существующие значения UUID. Чтобы сгенерировать значения UUID, необходимо включить расширение Postgres (плагин) uuid-ossp, Это расширение включает библиотеку, предоставляемую проектом OSSP, для генерации различных типов значений UUID. Смотрите мой блог для получения инструкций.


Кстати…

Если бы я знал, как подать прошение в экспертную группу JDBC или команду JSR, чтобы JDBC знал о UUID, я бы, конечно, знал. Они делают это только для новых типов даты и времени, определенных в JSR 310: API даты и времени.

Точно так же, если бы я знал, как обратиться к комитету по стандартам SQL с просьбой добавить тип данных UUID, я бы сделал это. Но, видимо, этот комитет более скрытный, чем советское Политбюро, и медленнее, чем ледник.

Я использовал следующий подход для добавления UUID и других объектов в postgres:

 PGobject toInsertUUID = new PGobject();
 toInsertUUID.setType("uuid");
 toInsertUUID.setValue(uuid.toString());
 PreparedStmt stmt = conn.prepareStatement(query);
 stmt.setObject(placeHolder,toInsertUUID);
 stmt.execute();

Таким образом, вы будете мешать себе делать приведение типов. Этот кусок кода работал отлично для меня в любое время, например, даже JSON.

Это сработало для меня, используя org.postgresql.postgresql 42.2.5

myPreparedStatement.setObject(4, UUID.randomUUID(),java.sql.Types.OTHER)

Без java.sql.Types.OTHER я получил ошибку

Пытаться

.setParameter("uuid", uuid, PostgresUUIDType.INSTANCE);
Другие вопросы по тегам