Postgres UUID JDBC 不工作tl;博士详

2022-09-02 14:26:51

最新的 Java JDBC 驱动程序用于 postgres 声称在本地支持 UUID;与Postgres 9.2(mac)合作。

事实上,当使用ReadyStatement时,我可以单步执行驱动程序代码,甚至可以遍历抽象Jdbc3gStatement中专门的“setUuid”函数.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]

是的,确实,JDBC驱动程序中的setUuid确实将其作为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

tl;博士

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

(a) 向我们出示您的代码。

PreparedStatement::setObject 在传递 java.util.UUID 时确实有效。您的代码中可能存在其他一些问题。

(b) 请参阅我的博客文章 UUID Values From JDBC to 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)我不确定你的意思

用于 postgres 的最新 Java JDBC 驱动程序声称在本机支持 UUID

哪个驱动程序?Postgres至少有两个开源JDBC驱动程序,当前/旧版和新的重写“下一代”驱动程序。还有其他商业驱动因素。

“原生”?你能链接到你阅读的文档吗?SQL 规范没有 UUID 的数据类型(不幸的是☹),因此 JDBC 规范没有 UUID 的数据类型。作为一种解决方法,用于 Postgres 的 JDBC 驱动程序使用 PreparedStatement 上的 and 方法将 UUID 移动到 Java ↔ SQL ↔ Postgres 之间的鸿沟中。请参阅上面的示例代码。setObjectgetObject

正如ReadyStatement JDBC文档所说:

如果需要任意参数类型转换,则方法 setObject 应与目标 SQL 类型一起使用。

也许通过“原生”,您将Postgres对UUID作为数据类型的本机支持与具有UUID数据类型的JDBC混淆了。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。


推荐