【问题标题】:PreparedStatement + Select for update + Oracle 12c + ORA-01461 in primary key columnPreparedStatement + Select for update + Oracle 12c + ORA-01461 在主键列
【发布时间】:2026-01-27 15:45:02
【问题描述】:

我在尝试执行 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();
      }
    }
  }
}

main方法的第一行创建一个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();
      }
    }
  }

【问题讨论】:

  • 嗨,编辑了我的第一篇文章并添加了数据库信息。谢谢!
  • 您能否尝试将连字符替换为另一个字符(例如0a)而不是空字符串?只是为了了解问题是否真的是大小......会发生什么?
  • 当然,刚刚试了一下,出现同样的错误 ORA-01461,请看我的编辑,使用新的 main 函数进行插入。
  • 您使用的是 12c 的 JDBC 驱动程序吗?

标签: java oracle jdbc prepared-statement ora-01461


【解决方案1】:

尝试在 SQL 语句中使用超过 4000 字节的 varchar 变量时发生此错误(4000 字节是限制)。

您应该检查uuid 变量包含的内容,注意可能的编码。

无论如何,像 VARCHAR 数据类型一样存储 UUID 并不是一个好主意。

使其工作的解决方法可能是将TEST_ID 数据类型从VARCHAR2 更改为CLOB(删除您在下面评论中所说的PK)但这不是解决方案。

【讨论】:

  • 您的解决方案无效。你真的试过了吗?
  • @yannisf 不,因为我现在在移动设备上,但过去我遇到了同样的问题。您是否已经尝试更改 VARBINARY(36) 中的数据类型?结果是一样的吗?
  • @Satoshi Kouno:您的回答是最明显的,但很抱歉并非如此。 uuid 的长度正好是 36 个字符,如果您在 SQL Developer 中使用相同的数据运行插入语句,则插入的行不会出错。在我的第一篇文章中,我提到如果你从 uuid 中修剪 4 个字符,那么示例代码会正确插入行。
  • @elefasGR 就像我说的,CLOB 是一种变通方法,而不是解决方案。无论如何,对我来说,使用 VARCHAR 存储 UUID 是错误的,因为这会影响性能。
【解决方案2】:

向 oracle 打开 SR,因为这是 12.1.0.2.0 jdbc 驱动程序的错误,需要补丁来解决。