获取java.sql.SQLException: - ORA-01000:DML超出最大打开游标数(更新语句)

时间:2018-04-20 10:04:08

标签: java oracle jdbc

我正在读取包含30000行的excel文件,并尝试根据某些逻辑更新Oracle dB表字段。我的Java应用程序错误输出“java.sql.SQLException: - ORA-01000:超出最大打开游标数”,当它在表中写入大约700条记录时。需要帮助优化代码以避免此错误。

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.ResourceBundle;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import oracle.jdbc.driver.OracleDriver;

public class UpdateTest {
    private static Connection conn = null;
    static ResourceBundle bundle = ResourceBundle.getBundle("PropertiesFile");
    public static void main(String[] args) {

    String filename = bundle.getString("FILEPATH") + bundle.getString("FILENAME");
    FileInputStream fileInputStream = null;
    String input = null;
    PreparedStatement preparedStatement = null;
    Integer result = null;
    int counter = 0;

    try {
        DriverManager.registerDriver(new OracleDriver());
        conn = DriverManager.getConnection(
                bundle.getString("DATABASE_URL"), 
                bundle.getString("DATABASE_USERNAME"),
                bundle.getString("DATABASE_PASSWORD"));
        conn.setAutoCommit(false);

        fileInputStream = new FileInputStream(filename);
        XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
        XSSFSheet sheet = workbook.getSheetAt(0);
        System.out.println("Number of records to be updated: " + (sheet.getPhysicalNumberOfRows() - 1));

        Iterator i = sheet.iterator();
        while (i.hasNext()) {
            XSSFRow row = (XSSFRow) i.next();
            input = row.getCell(0).toString();
            preparedStatement = conn.prepareStatement("update table1 set column1='value' where input=?");
            preparedStatement.setString(1, input);
            result = preparedStatement.executeUpdate();
        }

        if (preparedStatement != null) {
            preparedStatement.close();
        }
        conn.commit();
        conn.close();

    } catch (Exception e) {
        if (conn != null) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
        e.printStackTrace();
    } finally {
        try {
            if (conn != null && !conn.isClosed()) {
                if (!conn.getAutoCommit()) {
                    conn.commit();
                    conn.setAutoCommit(true);
                }
                conn.close();
                conn = null;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

}

2 个答案:

答案 0 :(得分:5)

每次调用prepareStatement()都会在Oracle服务器中创建一个未在循环中关闭的新游标。

正确的解决方案,以避免过多的开放游标"只是通过在循环之前仅一次准备语句来创建一个游标。

preparedStatement = conn.prepareStatement("update table1 set column1='value' where input=?");

Iterator i = sheet.iterator();
while (i.hasNext()) {
    XSSFRow row = (XSSFRow) i.next();
    input = row.getCell(0).toString();
    preparedStatement.setString(1, input);
    result = preparedStatement.executeUpdate();
 }

然后在finally块中循环后关闭它。

循环调用prepareStatement()会破坏PreparedStatement的目的和意图。

答案 1 :(得分:1)

preparedStatement.close()移到while内:

preparedStatement = conn.prepareStatement("update table1 set column1='value' where input=?");
while (i.hasNext()) {
    XSSFRow row = (XSSFRow) i.next();
    input = row.getCell(0).toString();

    preparedStatement.clearParameters();
    preparedStatement.setString(1, input);
    result = preparedStatement.executeUpdate();
}

if (preparedStatement != null) {
    preparedStatement.close();
}

当你准备一个新的prepareStatement时,你正在丢失引用,只有它正在关闭最后一个preparedStatement。

如果您在代码的其他部分使用ResultSet,请记住,如果您正在进行循环,请将其关闭。

编辑:重用预准备语句,可以在循环外关闭它。更多详情here