java里每次打开一个statement对象jdbc都会在数据库中打开一个游标来执行操作,做完业务后必须马上关闭,否则一旦多次打开且不关闭就会造成ORA-01000: 超出打开游标的最大数错误,如果需要循环建立statement对象最后统一提交,可以在循环里执行完活动马上关闭,关闭后业务处于未提交状态,直到执行connection.commit事件时活动才会提交.
变成表多行数据插入,for循环中的statement是每次一个new对象,这个对象在数据库中会产生一个游标,finanlly最后实际只关闭了其中一个statement。剩下的只能交予jvm垃圾回收机制完成了。如果上上面代码改为:
这样就不会出现这种问题,而且这问题只有在并发操作或一个事务多操作下出现,一般小量会经过java回收,而且数据库游标也够用。
statement = conn.prepareStatement(Insertsql); 在for循环里面同样也是一种错误写法,应该是卸载外面只有一个对象。
oracle数据库中open_cursors默认值为300,正常情况都够用了,出现ORA-01000错误基本都是有程序没关闭游标,多出现于java程序使用连接池连接数据库的情况下,打开了statement没关闭就把连接放回了连接池或者循环打开statement对象而不关闭,请仔细检查自己的程序代码
比如这段代码:
for (int j = 0; j < count; j++) {
DataObject d = dataObjects.get(j);
String sql = d.getSql();
statement = conn.prepareStatement(sql);
List<DataCell> dataCells = d.getDataArray();
if (d.getSheetType() == SheetType.EXT) {
batchExecExtSQL(d, statement, dataCells);
} else {
statement = conn.prepareStatement(sql);
for (int i = 0; i < dataCells.size(); i++) {
DataCell dataCell = dataCells.get(i);
Object val = dataCell.getValue();
setSqlValue(statement, dataCell.getDataType(), val, i + 1);
}
statement.execute();
}
}
} finally {
if (statement != null) {
// statement.clearBatch();
statement.close();
}
变成表多行数据插入,for循环中的statement是每次一个new对象,这个对象在数据库中会产生一个游标,finanlly最后实际只关闭了其中一个statement。剩下的只能交予jvm垃圾回收机制完成了。如果上上面代码改为:
PreparedStatement statement = null;
try {
Connection conn = session.connection();
// session.setFlushMode(FlushMode.ALWAYS);
// 并发测试希望有所提升,加入事务隔离级别(有部分作用),后期更改读操作的事务要素有效
// conn.setTransactionIsolation(TransactionDefinition.ISOLATION_READ_COMMITTED);
int count = dataObjects.size();
for (int j = 0; j < count; j++) {
DataObject d = dataObjects.get(j);
String sql = d.getSql();
statement = conn.prepareStatement(sql);
List<DataCell> dataCells = d.getDataArray();
if (d.getSheetType() == SheetType.EXT) {
batchExecExtSQL(d, statement, dataCells);
} else {
for (int i = 0; i < dataCells.size(); i++) {
DataCell dataCell = dataCells.get(i);
Object val = dataCell.getValue();
setSqlValue(statement, dataCell.getDataType(), val, i + 1);
}
statement.execute();
if (statement != null) {
statement.close();
}
}
}
} finally {
if (statement != null) {
statement.clearBatch();
statement.close();
}
}
这样就不会出现这种问题,而且这问题只有在并发操作或一个事务多操作下出现,一般小量会经过java回收,而且数据库游标也够用。
再看下面一段代码的写法,虽然是批处理,但是不符合Java JDBC操作的规范,同样会导致游标不够的问题。
public void batchExecSqlWithArray(final String Insertsql,
final List<SystemLog> dataObjects) throws SQLException {
this.getHibernateTemplate().execute(new HibernateCallback() {
@SuppressWarnings("deprecation")
public Object doInHibernate(Session session) throws HibernateException, SQLException {
PreparedStatement statement = null;
try {
Connection conn = session.connection();
int count = dataObjects.size();
for (int j = 0; j < count; j++) {
SystemLog log = dataObjects.get(j);
statement = conn.prepareStatement(Insertsql);
UUID uuids = UUID.randomUUID();
String uuid = uuids.toString().replaceAll("-", "");
statement.setString(1, uuid);
statement.setString(2, log.getIPAdress());
statement.setInt(3, log.getLogType().getId());
statement.setString(4, log.getLogdate());
statement.setString(5, log.getMemo());
statement.setString(6, log.getMessage());
statement.setInt(7, log.getModule().getId());
statement.setInt(8, log.getRelease());
statement.setString(9, log.getUserid());
statement.addBatch();
}
dataObjects.clear();
statement.executeBatch();
} finally {
if (statement != null) {
statement.clearBatch();
statement.close();
}
}
return null;
}
});
}
statement = conn.prepareStatement(Insertsql); 在for循环里面同样也是一种错误写法,应该是卸载外面只有一个对象。
数据库相关关闭连接资源比较常见,开发过程中发现的,引以为鉴……