如何将UUID插入到RAW(16)列中

14

我有一个在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。


1
你不能保存id.toString()吗? - user7294900
1
在我看来,您需要传递类似于byte[]的东西给JDBC驱动程序,以允许存储RAW数据类型。 - ibre5041
1
id.toString()?转换成RAW(16)?但是怎么做呢? - Serge Iroshnikov
1
@ibre5041,我可以将其写成字节,但我需要让DbSetup正常工作。Hibernate运行良好。 - Serge Iroshnikov
1
使用setBytes()代替setObject()怎么样? - ibre5041
setBytes() 可以使用,但不适用于 DbSetup。 - Serge Iroshnikov
4个回答

11

您需要将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();
  }

2
@Serge,欢迎;不过根据我的经验,我只会在有两个充分的理由无法使用“序列”并且哈希码相对于数字主键带来了真正的优势时,才使用/允许使用哈希码作为主键。祝好运! - Marmite Bomber
我使用它是因为需要将中间服务器上的数据合并到主服务器上。我认为这种方式比为每个服务器维护单独的序列值范围更好。 - Serge Iroshnikov
我正在使用jbdcTemplate,而不是preparedStatement/Con,那我该怎么办呢?在调用as Bytes函数后,我需要以某种方式setBytes(2),如果有人知道,请告诉我,谢谢! - mattsmith5
如何使用JDBC模板将UUID插入Raw16列 - mattsmith5

4

Oracle没有真正的UUID数据类型,处理RAW(16)非常麻烦。

我们所做的是将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("-", ""));

在测试中,它没有遵循此页面转换 https://robobunny.com/cgi-bin/guid。 - mattsmith5
是的,我正在尝试使用Oracle进行测试,等等。 - mattsmith5
有什么办法可以只获取字符串值吗?https://stackoverflow.com/questions/68886687/java-guid-convert-into-oracle-raw16-string - mattsmith5

2

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

使用lambda表达式可以简化代码:

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方法。


2
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)) - mattsmith5
这是不正确的,需要设置bytes方法(在测试后添加2)。 - mattsmith5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接