Java-超出打开游标的最大数

java里每次打开一个statement对象jdbc都会在数据库中打开一个游标来执行操作,做完业务后必须马上关闭,否则一旦多次打开且不关闭就会造成ORA-01000: 超出打开游标的最大数错误,如果需要循环建立statement对象最后统一提交,可以在循环里执行完活动马上关闭,关闭后业务处于未提交状态,直到执行connection.commit事件时活动才会提交. 

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 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 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 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循环里面同样也是一种错误写法,应该是卸载外面只有一个对象。



数据库相关关闭连接资源比较常见,开发过程中发现的,引以为鉴……

你可能感兴趣的:(Java)