【问题标题】:How to insert UUID into RAW(16) column如何将 UUID 插入 RAW(16) 列
【发布时间】:2018-06-25 12:43:20
【问题描述】:

我在 Oracle 中有 RAW(16) PK 列,并尝试使用 JDBC 将其插入:

        PreparedStatement stmt = connection.prepareStatement("insert into COUNTRY (id, state, version, code, name, nationality, issuing_entity, country) values (?, ?, ?, ?, ?, ?, ?, ?)");
        UUID id = UUID.randomUUID();
        stmt.setObject(1, id, Types.BINARY);

但是,我遇到了一个异常:

java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8494)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:7995)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:8559)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:225)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setObject(HikariProxyPreparedStatement.java)
at rw.gov.dgie.framework.test.AbstractTestCaseWithDB.tryToInsertCountry(AbstractTestCaseWithDB.java:78)
at rw.gov.dgie.framework.test.AbstractTestCaseWithDB.dbSetup(AbstractTestCaseWithDB.java:62)
at test.rw.gov.dgie.bms.terr.service.TestCountryService.init(TestCountryService.java:37)

我在尝试使用 DbSetup 插入测试数据时遇到了同样的异常。

有没有办法让 JDBC 将 UUID 插入 RAW(16) 列?

我使用的是 Oracle JDBC 12.2.0.1.0。

【问题讨论】:

  • 你不能保存id.toString()吗?
  • 恕我直言,您必须将 byte[] 之类的内容传递给 JDBC 驱动程序以允许存储数据类型 RAW。
  • id.toString()?进入RAW(16)?但是怎么做呢?
  • @ibre5041,我可以把它写成字节,但我需要让 DbSetup 工作。休眠工作正常
  • 用 setBytes() 代替 setObject() 怎么样?

标签: java oracle jdbc uuid dbsetup


【解决方案1】:

您必须将 UUID 转换为字节数组。看方法asBytes怎么做。

在它之后,绑定就像使用setBytes 一样简单。

例子

def stmt = con.prepareStatement("insert into TAB_UUID (id, uuid) values (?,?)") 
// bind
stmt.setInt(1,1)
def uuid = UUID.randomUUID()
stmt.setBytes(2,asBytes(uuid)) 
def rowCount = stmt.executeUpdate()

这里只是以防万一链接不起作用 UUID 到字节数组的转换方法

  public static byte[] asBytes(UUID uuid) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return bb.array();
  }

【讨论】:

  • @Serge 不客气;无论如何,根据我的经验,只有在有两个充分理由的情况下,我才会使用/允许将哈希码用作 PK,为什么不能使用 sequence 并且该哈希码对数字 PK 带来了真正的优势。祝你好运!
  • 我使用它是因为需要在主服务器上合并来自中间服务器的数据。我认为这种方式比为每个服务器维护单独的序列值范围更好。
  • 我使用的是jbdcTemplate,而不是preparedStatement/Con,那我该怎么办?我需要以某种方式 setBytes(2,在调用 Bytes 函数后,如果有人知道,那就太好了!
【解决方案2】:

Oracle 没有真正的 UUID 数据类型,处理 RAW(16) 确实是一个 PITA。

我们所做的,是将 UUID 作为字符串传递给使用hextoraw() 的 SQL 语句:

String sql = "insert into foo (id) values (hextoraw(?))";
PreparedStatement pstmt = connection.prepareStatement(sql);
UUID uid = UUID.randomUUID();
pstmt.setString(1, uid.toString().replaceAll("-", ""));

【讨论】:

【解决方案3】:
getJdbcTemplate().update("INSERT INTO abc(abc_id, abc_uuid, "
                                       + "VALUES (?, ?)",
                                          abcId, uuidToBytes(abcUuid))

这是一个将 UUID 类型转换为字节的辅助方法。

private byte[] uuidToBytes(final UUID uuid) {
        if (Objects.isNull(uuid)) {
            return null;
        }

        final byte[] uuidAsBytes = new byte[16];

        ByteBuffer.wrap(uuidAsBytes)
                  .order(ByteOrder.BIG_ENDIAN)
                  .putLong(uuid.getMostSignificantBits())
                  .putLong(uuid.getLeastSignificantBits());

        return uuidAsBytes;
    }

【讨论】:

  • 你需要多一行,见上面的答案 def uuid = UUID.randomUUID() stmt.setBytes(2,asBytes(uuid))
  • 这个不对,需要set bytes方法(2,测试后添加
【解决方案4】:

JdbcTemplate 提供了不同的方法来执行插入等 DML 操作。请考虑例如update

@MarmiteBomber 在他/她的回答中提供了执行您需要的所有必要信息,请仅将代码适当地包装在 Spring 定义的不同工件中。

例如,您可以使用PreparedStatementCreator,例如:

jdbcTemplate.update(new PreparedStatementCreator() {
  @Override
  public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
    PreparedStatement ps = con.prepareStatement("insert into TAB_UUID (id, uuid) values (?,?)");
    ps.setInt(1,1);
    UUID uuid = UUID.randomUUID();
    ps.setBytes(2,asBytes(uuid));
    return ps;
  }
});

可以使用 lambdas 将代码简化为:

jdbcTemplate.update(con -> {
  PreparedStatement ps = con.prepareStatement("insert into TAB_UUID (id, uuid) values (?,?)");
  ps.setInt(1,1);
  UUID uuid = UUID.randomUUID();
  ps.setBytes(2,asBytes(uuid));
  return ps;
});

如果您愿意,可以改用PreparedStatementSetter

jdbcTemplate.update("insert into TAB_UUID (id, uuid) values (?,?)", new PreparedStatementSetter() {
  @Override
  public void setValues(PreparedStatement ps) throws SQLException {
    ps.setInt(1,1);
    UUID uuid = UUID.randomUUID();
    ps.setBytes(2, asBytes(uuid));
  }
});

同样,可以使用 lambda 简化代码:

jdbcTemplate.update("insert into TAB_UUID (id, uuid) values (?,?)", ps -> {
  ps.setInt(1,1);
  UUID uuid = UUID.randomUUID();
  ps.setBytes(2, asBytes(uuid));
});

在这两个示例中,您在底层准备好的语句中显式调用 setBytes 并使用 Marmite 答案中的 asBytes 方法。

【讨论】:

    猜你喜欢
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    相关资源
    最近更新 更多