PreparedStatement + Выбрать для обновления + Oracle 12c + ORA-01461 в столбце первичного ключа

У меня странная проблема при попытке выполнить оператор select for update, а затем выполнить вставку или обновление. Я получаю исключение ORA-01461. Это происходит только при использовании последней версии драйвера ojdbc ( 12.1.0.2), тогда как в старшей версии он работает нормально ( 12.1.0.1).

В частности, последний драйвер, похоже, имеет какое-то ограничение на длину символа первичного ключа (ограничено 32 символами), хотя в соответствующем столбце объявлено более 32 символов. Пример кода для воспроизведения проблемы следующий:

CREATE TABLE "TEST_TABLE" (
 "TEST_ID" VARCHAR2(40 CHAR) NOT NULL ENABLE,
 "TEST_COMMENT" VARCHAR2(200 CHAR),
 CONSTRAINT "TEST_TABLE_PK" PRIMARY KEY ("TEST_ID")
);

И немного Java:

public class DemoUpdatableResultSet {

    private static final String DB_URL = "jdbc:oracle:thin:@xxxx:1521/xxxxx";
    private static final String DB_USER = "xxx";
    private static final String DB_PASS = "xxx";

  public static Connection getConnection() throws Exception {
    Class.forName("oracle.jdbc.driver.OracleDriver");
    Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    return conn;
  }

  public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString();
    ResultSet rs = null;
    Connection conn = null;
    PreparedStatement pstmt = null;
    try {
      conn = getConnection();
      String query = "SELECT t.* FROM TEST_TABLE t WHERE t.TEST_ID=? FOR UPDATE";
      pstmt = conn.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE,
          ResultSet.CONCUR_UPDATABLE);
      pstmt.setString(1, uuid); // set input values
      rs = pstmt.executeQuery(); // create an updatable ResultSet
                                               // insert column values into the insert row.
      rs.moveToInsertRow();                    // moves cursor to the insert row
      rs.updateString("TEST_ID", uuid);           // updates the 2nd column
      rs.updateString("TEST_COMMENT", "Comment for: " + uuid);
      rs.insertRow();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      try {
        rs.close();
        pstmt.close();
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
}

Первая строка основного метода создает UUID

 UUID.randomUUID().toString();

который составляет 36 символов в длину. Выполнение этого примера класса приведет к ошибке ORA-01461, но изменение строки, упомянутой выше, на

 UUID.randomUUID().toString().replaceAll("-", "");

который приводит к 32 символам строки будет работать правильно и вставить строку в базу данных. Обратите внимание, что столбец "TEST_ID", в котором сохранена вышеупомянутая строка, является VARCHAR2(40 CHAR) и может содержать строки как 32, так и 36 символов. Увеличение длины столбца до еще больших чисел ничего не изменит.

Я надеюсь, что мой пример кода легко читать и понимать, и я с нетерпением жду решения / объяснения этой проблемы.

Спасибо!

Информация о базе данных:

Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
"CORE 12.1.0.2.0 Production"
TNS for Linux: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

Немного изменен, чтобы запустить оператор вставки с теми же данными, чтобы показать, что эта проблема более странная, чем кажется (относительно длины строки uuid). Следующий пример кода выполняется правильно с новейшим драйвером oracle:

  public static void main(String[] args) {
    final String uuid = UUID.randomUUID().toString();
    ResultSet rs = null;
    Connection conn = null;
    PreparedStatement pstmt = null;
    try {
      conn = getConnection();
      String query = "INSERT INTO TEST_TABLE (TEST_ID, TEST_COMMENT) VALUES (?, ?)";
      pstmt = conn.prepareStatement(query);
      pstmt.setString(1, uuid); // set input values
      pstmt.setString(2, "Comment for: " + uuid); // set input values
      rs = pstmt.executeQuery();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      try {
        rs.close();
        pstmt.close();
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }

2 ответа

Решение

Открыл SR для оракула, так как это ошибка драйвера jdbc 12.1.0.2.0, и для ее устранения потребуется патч.

Эта ошибка возникает при попытке использовать переменную varchar длиной более 4000 байтов в инструкции SQL (ограничение в 4000 байтов).

Вы должны проверить, что uuid Переменная содержит внимание к возможной кодировке.

В любом случае хранить UUID как тип данных VARCHAR не очень хорошая идея.

Обходной путь, чтобы заставить это работать, мог бы изменить TEST_ID тип данных из VARCHAR2 в CLOB (удаление PK, как вы сказали в комментарии ниже), но это не решение.

Другие вопросы по тегам