How to insert UUID into RAW(16) column

13k Views Asked by At

I have the RAW(16) PK column in Oracle, and trying to insert into it using 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);

However, I am getting an exception:

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)

I am getting the same exception when trying to use DbSetup for inserting test data.

Is there a way to make JDBC insert UUIDs into RAW(16) column?

I am using Oracle JDBC 12.2.0.1.0.

4

There are 4 best solutions below

0
On

JdbcTemplate provides different methods for performing a DML operations like insert. Please, consider for instance update.

@MarmiteBomber provides in his/her answer all the necessary information for performing what you need, please, only wrap the code appropriately in the different artifacts defined by Spring.

For example, you can use PreparedStatementCreator, something like:

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

The code can be simplified using lambdas to:

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

If you prefer, you can use PreparedStatementSetter instead:

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

Again, the code can be simplified with lambdas:

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

In both examples you explicitly invoke setBytes in the underlying prepared statement and use the asBytes method from the Marmite answer.

3
On

Oracle has no real UUID datatype and dealing with RAW(16) is really a PITA.

What we do, is to pass the UUID as a string to a SQL statement that uses hextoraw():

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

You must convert the UUID to a byte array. See the method asBytes how to do it.

After it the binding is a s simple as using setBytes.

Example

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()

Here just for case the link doesn't work the conversion method UUID to byte array

  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
On
getJdbcTemplate().update("INSERT INTO abc(abc_id, abc_uuid, "
                                       + "VALUES (?, ?)",
                                          abcId, uuidToBytes(abcUuid))

Here's a helper method to converet UUID type to bytes.

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