PL/SQLcall SpringMVC jdbcTemplate 中的游标泄漏
Posted
技术标签:
【中文标题】PL/SQLcall SpringMVC jdbcTemplate 中的游标泄漏【英文标题】:Cursor leak in PL/SQLcall SpringMVC jdbcTemplate 【发布时间】:2018-04-10 11:32:17 【问题描述】:我创建了一个在 for 中调用 PL/SQL 的函数,它正在创建游标泄漏。PL/SQL 工作正常,它返回所需的数据,但我注意到游标的数量会增加,直到 ora超过 -1000 个最大打开游标并抛出 ORA-00604: error occurred at recursive SQL level 1 错误。为了检查已使用游标的数量,我使用了以下 SQL 语句:
select *
from
(select a.value, s.username, s.sid, s.serial#
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic#
and s.sid=a.sid
and b.name = 'opened cursors current')
where sid = 'mySID';
我调试了我的代码,发现游标是在这里创建的:objectXStructResult.getAttributes()[X]。我试图关闭结果集和 callableStatement 但它不起作用。谁能帮帮我?
function callToDao()
for(Object X:LstObject)
plsqlCall(X);
funcion ObjectX plsqlCall(Object X)
Object salidaX = null;
//Obtención de parámetros de consulta
final String p_x1 = X.getX();
final String p_x2 = X.getX();
final String p_x3 = X.getX();
final String p_x4 = X.getX();
final String p_x5 = X.getX();
final String p_x6 = X.getX();
final String p_x7 = X.getX();
try
CallableStatementCreator csCreator = new CallableStatementCreator()
@Override
public CallableStatement createCallableStatement(Connection con) throws SQLException
CallableStatement cs = null;
try
cs = con.prepareCall("call PK_XXXXX.XXXXXX(?, ?, ?, ?, ?, ?, ?, ?) ");
cs.setInt(1, Integer.parseInt(p_x1));
cs.setString(2, p_x2);
cs.setString(3, p_x3);
cs.setLong(4, Long.parseLong(p_x4));
cs.setDouble(5,Double.parseDouble(p_x5));
cs.setDouble(6, Double.parseDouble(p_x6));
cs.setDouble(7, Double.parseDouble(p_x7));
cs.registerOutParameter(8, OracleTypes.STRUCT,"OBJECT_PLSQL");
catch (SQLException e)
e.printStackTrace();
return cs;
;
CallableStatementCallback csCallback = new CallableStatementCallback()
public Object doInCallableStatement(CallableStatement cs) throws SQLException
ObjectX ret = null;
ResultSet rs = null;
try
cs.execute();
ret = obtainObjectX(cs, rs, objectPos, type);
catch (SQLException e)
e.printStackTrace();
return ret;
;
salida = (ObjectX) this.jdbcTemplate.execute(csCreator,csCallback);
catch (Exception e)
e.printStackTrace();
return salida;
private ObjectX obtainObjectX(CallableStatement cs, ResultSet rs, int objectPos, String p_tipo) throws SQLException
ObjectX objectX= new ObjectX();
List<ObjectX> objectXLst= new ArrayList<ObjectX>();
try
//Obtain exit parameter
STRUCT objectXStructResult = (STRUCT)cs.getObject(objectPos);
//Obtain Struct data
String att1 = (String)objectXStructResult.getAttributes()[1];
String att2 = (String)objectXStructResult.getAttributes()[5];
BigDecimal att3 = (BigDecimal)objectXStructResult.getAttributes()[6];
String att4 = (String)objectXStructResult.getAttributes()[7];
//Control de error en la obtención de datos
if (new BigDecimal("0").equals(att3))
//Obtención de los datos de salida
ARRAY listaDatos = (ARRAY)objectXStructResult.getAttributes()[0];
if (listaDatos!=null)
rs = listaDatos.getResultSet();
int rowNum = 0;
//Recorrido del listado de datos
while (rs.next())
STRUCT dataStruct= (STRUCT) listaDatos.getOracleArray()[rowNum];
ObjectX objXFor= this.mapRow(dataStruct, rowNum);
objectXLst.add(objXFor);
rowNum++;
objectX.setAtt1(att1);
objectX.setAtt2(att2);
objectX.setAtt3(new Long(att3.toString()));
objectX.setAtt4(att4);
objectX.setData(objectXLst);
catch (Exception e)
e.printStackTrace();
return objectX;
private ObjectX mapRow(STRUCT dataStruct, int rowNum) throws SQLException
Object[] objectInfo = dataStruct.getAttributes();
//Obtención de datos de la estructura de base de datos
BigDecimal att1= ((BigDecimal)objectInfo[0]);
BigDecimal att2= (BigDecimal)objectInfo[1];
String att3= (String)objectInfo[2];
String att4= (String)objectInfo[3];
return new ObjectX(att1, att2, null, att3, null, null, att4, null);
更新 1:
我已经设法将光标的创建从 5 个减少到 1 个更改代码,但我仍然找不到关闭该光标的方法。
发件人:
String att1 = (String)objectXStructResult.getAttributes()[1];
String att2 = (String)objectXStructResult.getAttributes()[5];
BigDecimal att3 = (BigDecimal)objectXStructResult.getAttributes()[6];
String att4 = (String)objectXStructResult.getAttributes()[7];
//Control de error en la obtención de datos
if (new BigDecimal("0").equals(att3))
//Obtención de los datos de salida
ARRAY listaDatos = (ARRAY)objectXStructResult.getAttributes()[0];
收件人:
Object[] atributos = objectXStructResult.getAttributes();
String att1 = (String)atributos[1];
String att2 = (String)atributos[5];
BigDecimal att3 = (BigDecimal)atributos[6];
String att4 = (String)atributos[7];
//Control de error en la obtención de datos
if (new BigDecimal("0").equals(att3))
//Obtención de los datos de salida
ARRAY listaDatos = atributos[0];
【问题讨论】:
【参考方案1】:注意:任何打开的结果集都应该在回调实现中的 finally 块中关闭。 Spring 将在回调返回后关闭 Statement 对象,但这并不一定意味着将关闭 ResultSet 资源:Statement 对象可能会被连接池池化,close 调用仅将对象返回到池中而不是物理关闭资源。
请注意,jdbcTemplate 会自动关闭 csCreator 和 csCallback,但您必须始终注意 ResultSet .记得在 finally 块中关闭它们,以确保当异常发生时,你的 close() 会被调用。
例如,您当前的陈述:
ResultSet rs = null;
try
cs.execute();
ret = obtainObjectX(cs, rs, objectPos, type);
catch (SQLException e)
e.printStackTrace();
应替换为:
ResultSet rs = null;
try
cs.execute();
ret = obtainObjectX(cs, rs, objectPos, type);
catch (SQLException e)
e.printStackTrace();
finally
if(rs != null)
rs.close()
更新: 来自oracle 文档:
默认情况下,自动索引未启用。对于 JDBC 应用程序,如果可能通过 getArray 和 getResultSet 方法随机访问数组元素,请为 ARRAY 对象启用自动索引。
所以也许这会减少数据库上的打开游标:
array.setAutoIndexing(true);
顺便说一句,oracle.sql.ARRAY 类已被弃用:
已弃用。使用 java.sql.Array 接口进行声明,而不是使用具体类 oracle.sql.ARRAY。
【讨论】:
感谢您的回答。在阅读您的答案之前,我尝试添加该代码,它仍然为每次调用 plsql 创建一个光标。我也处理了以防万一关闭cs,但是光标仍然在那里。 如果您查看 org.springframework.jdbc.core.JdbcTemplate.query 方法源代码,您会看到调用 - JdbcUtils.closeResultSet(rs);在 finally 块中 - 如果它不起作用,那么它可能是 spring jdbctemplate 或 ojdbc 驱动程序的错误以上是关于PL/SQLcall SpringMVC jdbcTemplate 中的游标泄漏的主要内容,如果未能解决你的问题,请参考以下文章
SSM(MyBatis+Spring+SpringMVC)之MyBatis总结
springmvc+jdbc连接数据库(第一个微商项目,代理注册)