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 отправляет это как bytea:

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);
        }
    }

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

Ответ 1

ТЛ; др

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

подробности

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

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

(b) См. мои сообщения в блоге. Значения 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." );
}

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

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

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

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

Как говорится в документе JpBC, подготовленном PreparedStatement:

Если требуются преобразования параметров произвольного типа, метод 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, я бы это сделал. Но, по-видимому, этот комитет более скрыт, чем советское Политбюро и медленнее ледника.

Ответ 2

Я использовал следующий подход для добавления 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.

Ответ 3

попробуйте

.setParameter("uuid", uuid, PostgresUUIDType.INSTANCE);

Ответ 4

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

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

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