У меня есть столбец RAW (16) PK в Oracle, и я пытаюсь вставить в него с помощью 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.

14
Serge Iroshnikov 25 Июн 2018 в 15:43
1
Вы не можете спасти id.toString()?
 – 
user7294900
25 Июн 2018 в 15:52
1
Imho вам нужно передать что-то вроде byte [] драйверу JDBC, чтобы разрешить хранение типа данных RAW.
 – 
ibre5041
25 Июн 2018 в 15:58
1
Id.toString ()? В RAW (16)? Но как?
 – 
Serge Iroshnikov
25 Июн 2018 в 15:59
1
@ ibre5041, я могу записать это в байтах, но мне нужно, чтобы DbSetup работал. Hibernate отлично работает
 – 
Serge Iroshnikov
25 Июн 2018 в 16:04
1
Как насчет использования setBytes () вместо setObject ()?
 – 
ibre5041
25 Июн 2018 в 16:10

4 ответа

Вы должны преобразовать 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();
  }
10
Marmite Bomber 25 Июн 2018 в 17:28
2
Не за что; в любом случае, исходя из моего опыта, я бы использовал / разрешил использовать хэш-коды в качестве PK, только если есть две веские причины: почему нельзя использовать sequence и этот хеш-код дает реальное преимущество перед числовым PK. Удачи!
 – 
Marmite Bomber
25 Июн 2018 в 18:32
Я использую его из-за необходимости слияния данных с промежуточных серверов на основном. Я думаю, что этот способ лучше, чем сохранение отдельного диапазона значений последовательности для каждого сервера.
 – 
Serge Iroshnikov
26 Июн 2018 в 16:29
Я использую jbdcTemplate, а не prepareStatement / Con, что мне тогда делать? Мне нужно как-то setBytes (2, после вызова функции Bytes, если кто-нибудь знает, было бы здорово!
 – 
mattsmith5
23 Авг 2021 в 06:35
См. вопрос здесь, stackoverflow.com/questions/68895766/…
 – 
mattsmith5
23 Авг 2021 в 19:34

Oracle не имеет реального типа данных UUID, и работа с RAW(16) на самом деле является PITA.

Что мы делаем, так это передаем UUID в виде строки в инструкцию SQL, которая использует hextoraw():

String sql = "insert into foo (id) values (hextoraw(?))";
PreparedStatement pstmt = connection.prepareStatement(sql);
UUID uid = UUID.randomUUID();
pstmt.setString(1, uid.toString().replaceAll("-", ""));
3
a_horse_with_no_name 25 Июн 2018 в 16:32
При тестировании он не следует за этой конверсией страницы robobunny.com/cgi-bin/guid
 – 
mattsmith5
23 Авг 2021 в 23:07
Да, я пытался протестировать с помощью оракула и т. д.
 – 
mattsmith5
23 Авг 2021 в 23:10
Есть идеи, как получить только строковое значение? stackoverflow.com/questions/68886687 /…
 – 
mattsmith5
23 Авг 2021 в 23:39
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;
    }
2
Vijay Nandwana 10 Июн 2019 в 15:00
Вам нужна еще одна строка, см. выше ответ def uuid = UUID.randomUUID () stmt.setBytes (2, asBytes (uuid))
 – 
mattsmith5
23 Авг 2021 в 06:34
Это неверно, необходимо установить метод байтов (2, добавлен после тестирования
 – 
mattsmith5
23 Авг 2021 в 19:03

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

Код можно упростить с помощью лямбда-выражений, чтобы:

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

Опять же, код можно упростить с помощью лямбда-выражений:

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

В обоих примерах вы явно вызываете setBytes в базовом подготовленном операторе и используете метод asBytes из ответа Marmite.

2
jccampanero 28 Авг 2021 в 01:32