Resolvendo ORA-01000: maximum open cursors exceeded
Junto com o clássico OutOfMemoryError, este é um dos erros que mais aparece em sistemas que usam Oracle. Resolver esse problema não é difícil, como vou explicar aqui.
Este erro é causado por Connections, Statements e ResultSets deixados abertos. Normalmente temos algo como:
Connection con = //obtem conexao;
PreparedStatement ps = con.prepareStatement("SELECT ...");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
// ...
}
rs.close();
ps.close();
con.close();
Note que eu estou sendo otimista, supondo que PreparedStatements sejam usados ao invés de concatenar Strings para executar consultas e que o método close() nas três instâncias esteja pelo menos no código. Se você não fez nem isso, corra atrás.
O bug é que, quando ocorre uma exceção, durante o processamento do select, nenhum dos três objetos é fechado. Então, a primeira solução é colocar o fechamento num bloco try/finally, como mostrado abaixo:
Connection con = null;
PreparedStatement ps = null;
ResultSet rs =null;
try {
con = //obtem conexao;
ps = con.prepareStatement("SELECT ...");
rs = ps.executeQuery();
while (rs.next()) {
// ...
}
} finally {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (con != null) {
con.close();
}
}
Tudo certo, né? Errado. Se ocorrer uma exceção ao fechar rs, ps e con nunca serão fechados. Então a melhor solução seria:
Connection con = null;
PreparedStatement ps = null;
ResultSet rs =null;
try {
con = //obtem conexao;
ps = con.prepareStatement("SELECT ...");
rs = ps.executeQuery();
while (rs.next()) {
// ...
}
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException sqe) {
// logar excecao
} catch (RuntimeException re) {
// logar excecao
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException sqe) {
// logar excecao
} catch (RuntimeException re) {
// logar excecao
}
}
if (con != null) {
try {
con.close();
} catch (SQLException sqe) {
// logar excecao
} catch (RuntimeException re) {
// logar excecao
}
}
}
Ok, você vai me dizer, muito legal isso. Mas meu projeto tem milhares de classes e eu não sei onde eu esqueci de fazer isso. Ou, ainda, eu uso um framework O/RM ou JDBC que deveria resolver este problema pra mim. Bem, nesse caso, você tem como descobrir qual a instrução SQL do(s) cursor(es) que está(ão) abertos. Primeiro, você precisa do SID das sessões abertas pela sua aplicação, que você pode obter assim:
select o.sid, osuser, machine, count(*) num_curs from v$open_cursor o, v$session s where o.sid=s.sid group by o.sid, osuser, machine order by num_curs;
Se estiver usando connection pool e um máquina só, pode-se usar estes dados para filtrar os resultados:
select o.sid, osuser, machine, count(*) num_curs from v$open_cursor o, v$session s where o.sid=s.sid and user_name = 'USUARIO_BANCO' and machine = 'NOME_MAQUINA_APLICACAO' group by o.sid, osuser, machine order by o.sid;
Utilize os SIDs na seguinte query:
select q.sql_text, count(*) from v$open_cursor o, v$sql q where q.hash_value=o.hash_value and o.sid IN (<SIDs>) group by q.sql_text order by 2;
Se o framework ou servidor de aplicações que você utiliza faz cache de statements (Hibernate e JBoss fazem, por default), é normal que exista um certo número de cursores com o mesmo SQL em aberto. Suspeite daquelas instruções que tenham um número de cursores muito maior que as outras e investigue de onde elas vêm. E se sua aplicação usa PL/SQL, saiba que as instruções contidas nas suas procedures/functions são contadas individualmente como cursores. Espero ter ajudado
9 de Julho de 2006 @ 00:50
pq serah que voce postou isso heim?? por que serah?!?!
aheuaUAEHuHEUhauaHEUhaEUheauhea
10 de Julho de 2006 @ 09:52
Digamos que foi uma homenagem a um amigo
Agora, falando sério, a razão de eu ter postado é porque esse erro é muito freqüente e achar no Google um guia de como diagnosticar e resolver o problema não é exatamente muito fácil.
20 de Julho de 2006 @ 17:33
Valeu pela dica, já tive o memso problema e esta dica me ajudou a ir direto no problema.
12 de Janeiro de 2007 @ 13:11
Estava com este problema e consegui contorná-lo, valeu!!!
16 de Janeiro de 2007 @ 16:13
realmente nao é facil encontrar dicas sobre isso no google.. vc me ajudou bastante.
31 de Julho de 2007 @ 17:01
Boa parabens! me ajudou mto .
um abraço
26 de Novembro de 2007 @ 12:44
Cara! Show, estava atraz disso mesmo! []s
3 de Dezembro de 2007 @ 07:39
Realmente muito boa a dica !
Bem explicada, prática e muito útil !
10 de Dezembro de 2007 @ 14:17
Parabéns pela dica. Realmente muito útil.
César
31 de Julho de 2008 @ 15:01
Muito boa dica!!! … Estava com esse problema e lendo aqui consegui resolve-lo …
Vlw
8 de Agosto de 2008 @ 10:09
Muito boa dica. Descobri este problema somente após mandar a plicação pra homologação, pois usavamos jboss no desenvolvimento, e websphere na homologação. o Jboss fechava as conexões abertas após um tempo, o websphere não.
15 de Setembro de 2008 @ 19:11
Cara, show!!!!!
vlw!!!!
14 de Agosto de 2009 @ 10:09
Caraca, funcionou mesmo, nunca iria imaginar que fazendo ajustes no try catch poderia resolver esse problema.
Obrigadão Mestre, me salvou de um pepino aqui na empresa.
Um grande abraço.
26 de Outubro de 2009 @ 13:17
Tenho uma duvida…
quando ocorre o erro, tem como resolver direto no banco?
sem ter que alterar a aplicaçao?
ex.: O cliente possui uma nota com mais de 100 itens, quando vai gravar da o erro. Tem como resolver sem que o cliente feche a aplicaçao ou cancele a operaçao?
25 de Março de 2010 @ 12:48
Muito bom, agora eu já descobri qual sql/cursor não foi fechado, mas…. e agora? Como fecha-los?