Java PreparedStatement executeUpdate()更新错误记录

Java PreparedStatement executeUpdate()更新错误记录,java,oracle,jdbc,prepared-statement,Java,Oracle,Jdbc,Prepared Statement,我正在使用Oracle数据库,过了一段时间后,我收到以下异常: java.sql.SQLException: ORA-01000: maximum open cursors exceeded 我分析了我的代码,似乎要关闭所有的结果集。这种例外只是偶尔发生。由于这个错误,我决定稍微修改一下代码。代码如下: public class Audit { private Connection connection; private PreparedStatement insertAud

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

java.sql.SQLException: ORA-01000: maximum open cursors exceeded
我分析了我的代码,似乎要关闭所有的结果集。这种例外只是偶尔发生。由于这个错误,我决定稍微修改一下代码。代码如下:

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每次都会创建一个新的游标。因此,我希望InsertAuditPreparedStatement发送到许多插入中而不关闭。我已经尝试了
insertAuditPreparedStatementSent.clearBatch()
insertAuditPreparedStatementSent.clearParameters()

我不知道它为什么要在记录2的主密钥上更新记录1。SQL很好


有什么想法吗?

请检查oracle参数

打开游标

您可以在enterprise manager中或通过执行以下SQL来找到这一点:

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

如果此参数非常低(例如<300),并且有许多进程/用户同时工作,则可能发生此错误。

您没有关闭此:

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,因为它很难使用。

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);
不确定为什么在任何地方都显式地使用
新字符串
;更简单的是:

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无关,但这似乎是这个问题的主旨-您不应该同时询问两件事,特别是如果它们没有直接关系的话…

我正在关闭我的结果集。请查看insertAudit()方法。我必须使用JDBC,因为我的公司不允许除JDBC之外的任何东西。我想停止java.sql.SQLException:ORA-01000:超出最大打开游标数异常。因此,我决定不关闭PreparedStatement并在所有插入中重复使用它。这是个问题吗?您能推荐另一种解决方案吗?使用Java7,可以更轻松地正确处理JDBC资源。