【问题标题】:PreparedStatement executeUpdate() updates wrong recordPreparedStatement executeUpdate() 更新错误记录
【发布时间】:2014-06-27 00:55:16
【问题描述】:

我正在使用 Oracle 数据库,一段时间后我收到以下异常:

java.sql.SQLException: ORA-01000: maximum open cursors exceeded

我分析了我的代码,我似乎要关闭所有 ResultSet。此异常仅偶尔发生。由于这个错误,我决定稍微改变我的代码。下面是代码:

public class Audit {

    private Connection connection;
    private PreparedStatement insertAuditPreparedStatementSent;
    private static int counter;
    private static int JDBC_COUNTER;

    public Audit() throws Exception {
        connection = DriverManager.getConnection("url", "username", "password");
    }

public int insertAudit(String message, java.util.Date sent) throws Exception {
     PreparedStatment preparedStatement = prepareStatement(new String("INSERT INTO Audit (message, sent) VALUES (?, ?)");
     if(JDBC_COUNTER == 0) {
            // this is required to be executed so that ORA-08002 SQLException is not thrown
            connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.NEXTVAL FROM DUAL"));
        }
     ResultSet resultSet = connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.CURRVAL FROM DUAL"));
     resultSet.next();
     primaryKey = resultSet.getInt(new String("CURRVAL"));
     resultSet.close();
     return primaryKey;
}

public void executeUpdateAudit(int id, java.util.Date sent) throws Exception {
    if(updateAuditPreparedStatement == null) {
         updateAuditPreparedStatement = connection.prepareStatement(new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = " + id));
    }
    updateAuditPreparedStatement.setTimestamp(1, new java.sql.Timestamp(sent.getDate());
    int i = updateAuditPreparedStatement.executeUpdate();
    connection.commit();   
}

public static void main(String[] args) throws Exception {
     Audit audit = new Audit();
     int primaryKey = audit.insertAudit("message", new java.util.Date());
     audit.executeUpdateAudit(primaryKey, new java.util.Date());
     int primaryKey2 = audit.insertAudit("message2", new java.util.Date());
     audit.executeUpdateAudit(primaryKey2, new java.util.Date());  
}
}

在插入记录 2 和更新记录 2 时,只有 updateAuditPreparedStatement.executeUpdate() 返回 1,但数据库更新第一条记录而不是第二条记录。

更改代码的原因是因为我相信 PreparedStatement 每次都会创建一个新游标。所以,我希望 insertAuditPreparedStatementSent 出现在许多插入上而不关闭。我试过insertAuditPreparedStatementSent.clearBatch()insertAuditPreparedStatementSent.clearParameters()

我不确定它为什么要更新记录 2 的主键上的记录 1。SQL 很好。

有什么想法吗?

【问题讨论】:

    标签: java oracle jdbc prepared-statement


    【解决方案1】:

    请检查oracle参数

    open_cursors

    您可以在企业管理器中或通过执行以下 SQL 找到它:

    select * from v$parameter a
    where a.NAME = 'open_cursors';
    

    如果此参数非常低(例如

    【讨论】:

      【解决方案2】:

      你没有关闭这个:

      if(JDBC_COUNTER == 0) {
          // this is required to be executed so that ORA-08002 SQLException is not thrown
          connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.NEXTVAL FROM DUAL"));
      }
      

      还需要关闭准备好的语句,并且在所有这些方法中都缺少try/catch/finally 以防止资源泄漏。我强烈反对直接使用 JDBC,因为它很难使用。

      【讨论】:

      • 我正在关闭我的结果集。查看方法 insertAudit() 方法。我必须使用 JDBC,因为我的公司不允许使用 JDBC 以外的任何东西。我想停止 java.sql.SQLException: ORA-01000: maximum open cursors exceeded 异常。因此,我决定不关闭 PreparedStatement 并将其重新用于所有插入。这是一个问题吗?您能提出其他解决方案吗?
      • 使用 Java 7 try-with-resources 可以更轻松地正确处理 JDBC 资源。
      【解决方案3】:

      executeUpdateAudit 中,您使用看到的第一个 ID 准备一次声明:

      if(updateAuditPreparedStatement == null) {
           updateAuditPreparedStatement = connection.prepareStatement(
               new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = " + id));
      }
      updateAuditPreparedStatement.setTimestamp(1,
        new java.sql.Timestamp(sent.getDate());
      

      在第二次调用中,仍然使用第一次调用的 ID,因为它实际上是在 SQL 中硬编码的。

      您也应该使用 ID 参数:

      if(updateAuditPreparedStatement == null) {
           updateAuditPreparedStatement = connection.prepareStatement(
               new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = ?"));
      }
      updateAuditPreparedStatement.setTimestamp(1,
          new java.sql.Timestamp(sent.getDate());
      updateAuditPreparedStatement.setInt(2, id);
      

      不知道你为什么在任何地方都明确地使用new String;它会更简单一点:

      if(updateAuditPreparedStatement == null) {
           updateAuditPreparedStatement = connection.prepareStatement(
               "UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = ?");
      }
      updateAuditPreparedStatement.setTimestamp(1,
          new java.sql.Timestamp(sent.getDate());
      updateAuditPreparedStatement.setInt(2, id);
      

      这与你的 ORA-01000 无关,但这似乎是这个问题的主旨——你不应该一次真正地问两件事,特别是如果它们没有直接相关......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-02
        相关资源
        最近更新 更多