我正在读取包含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();
}
}
}
}
答案 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