Máximo de cursores aberto excedidos

9 respostas Resolvido
oraclejava
B
Bom dia, minha dúvida é sobre alguns select dentro de um for que esta terminando em máximo de cursores excedidos.
Vi que existe um tópico sobre cursores abertos porém no meu caso é um pouco diferente,
tenho que fazer diversos select até que o for se acabe e então estoura o numero de cursores.


Segue o código:

PreparedStatement stmt = null;
        ResultSet rs = null;
        
        for (int i = 0; i < listaEmail.size(); i++) {
            String idEmail = null;
            boolean isEmail = false;
            boolean isUC = false;
            boolean isBlock = false;
            String ucId = "";
            try {
                stmt = conn.prepareStatement("select id_email from mi_email where email = ?");
                stmt.setString(1, listaEmail.get(i).getEmail());
                rs = stmt.executeQuery();
                if(rs.next()) {
                    idEmail = rs.getString("id_email");
                    isEmail = true;
                }

                stmt = conn.prepareStatement("select address_complement_uc, oid_uc from rs_uc where address_complement_uc = ? and oid_system = ?");
                stmt.setString(1, listaEmail.get(i).getAddressCompUc());
                stmt.setString(2, oidSystem);
                rs = stmt.executeQuery();
                if(rs.next()) {
                    isUC = true;
                    ucId = rs.getString("oid_uc");
                }
                
                stmt = conn.prepareStatement("select address_block_uc from rs_uc where address_block_uc = ? and oid_system = ?");
                stmt.setString(1, listaEmail.get(i).getBloco());
                stmt.setString(2, oidSystem);
                rs = stmt.executeQuery();
                if(rs.next()) {
                    isBlock = true;
                }
                
            } catch (SQLException ex) {
                
                mensagem.put(idEmail, ex.getMessage());
                ex.printStackTrace();
            } finally {
                try{rs.close();}catch (Throwable e) {}
                try{stmt.close();}catch (Throwable e) {}
            }

            if (isEmail && isUC && isBlock) {
                //verifico se ja nao estao relacionados e faço o relacioanmento
                connectEmailUC(conn, idEmail, listaEmail.get(i).getAddressCompUc()+"", ucId,  listaEmail.get(i).getBloco()+"" , user, oidSystem);

            } else if (isUC && !isEmail) {
                //cadastra email
                registerEmail(conn, listaEmail.get(i).getEmail(), listaEmail.get(i).getAddressCompUc()+"", user, null);

            } else if (!isUC && !isEmail) {
                registerEmail(conn, listaEmail.get(i).getEmail(), null, user, null);
                //Informar que nao existe UC e cadastrar email

            } else if (!isUC && isEmail) {
                // informar que nao existe UC e que email ja cadastrado
                mensagem.put(idEmail, "O e-mail informado já existe, mas a Unidade Consumidora é inválida");
            }

        }
        return mensagem;
    }

Alguém poderia ajudar?

Obrigado!

9 Respostas

M

Porque vc simplesmente não faz o select, coloca o resultado em memória (um array ou qualquer outra coisa parecida) e depois fecha o cursor? Não faz o menor sentido ficar com cursor aberto.

B

Primeiro obrigado pela resposta amigo. Não entendi, ou talvez vc não tenha entendido o código. rsrsr
Não é somente 1 select, são 3 select em cada laço do for…
Não entendi sua colocação se puder descrever no codigo eu agradeço.

R
Solucao aceita

Primeira dica: você não precisa preparar o seu statement toda vez. Cada vez que você faz isso o BD tem que compilar a query e criar um plano de execução, isso é um desperdício de recurso monstruoso. Portanto, prepare os statements uma única vez, fora do bloco for:

PreparedStatement stmt1 = conn.prepareStatement('select id_email from mi_email where email = ?');
PreparedStatement stmt2 = conn.prepareStatement("select address_complement_uc, oid_uc from rs_uc where address_complement_uc = ? and oid_system = ?");
PreparedStatement stmt3 = conn.prepareStatement("select address_block_uc from rs_uc where address_block_uc = ? and oid_system = ?");
ResultSet rs = null;

for (int i = 0; i < listaEmail.size(); i++) {

A segunda dica é fechar os cursores logo após ler os dados do BD. Para ser bem claro cursor = ResultSet. Diferente de um POJO, que é recolhido pelo GC, quando você abre um ResultSet pelo JDBC esse recurso fica alocado no banco de dados, mesmo que você não o referencie mais através de uma variável. Portanto, é essencial que você feche o ResultSet logo depois de utilizá-lo. Assim, dentro do laço for você tem que chamar o método close logo após cada leitura:

rs = stmt.executeQuery();
if(rs.next()) {
    idEmail = rs.getString("id_email");
    isEmail = true;
}
rs.close()

Essas duas dicas resolvem seu problema imediato, mas eu particularmente faria uma única query, fazendo um join entre as tabelas mi_email, rs_uc e passando a lista de emails como parâmetro … mas dev acha que só dba tem que saber SQL :disappointed_relieved:

I

Pq que vc nao faz select com in? ao inves de fazer um select pra cada email.

Faz o for pra juntar todos os dados que vc precisar no select, e depois executa o select 1x só

select id_email from mi_email where email IN (‘email1’, ‘email2’, ‘email3’);

B

Obrigado pela resposta Igor, mas sua solução não funciona. São diferentes posições da lista por exemplo.
No primeiro laço posição 1, ele tem email, nome, endereço, ainda no primeiro laço eu preciso fazer o select em na tabela de email, de nome e de endereço. Algo mais ou menos assim…
Então é necessário ter os selects dentro do for.

J

Desculpa, mas a mão da gambiarra chega a tremer kk

B

Agradeço a resposta de todos.
Encontrei a solução.
Um pouco da resposta do rmendes08 e um pouco do que encontrei pesquisando no google.

Sua ideia de fechar os resultsets estava correta porém tem que ser fechado assim:

rs = stmt.executeQuery();

if(rs.next()) {

idEmail = rs.getString(id_email);

isEmail = true;

}

rs.getStatement().close();

sem esse getStatement().close() não funciona.

Como sua resposta foi a que chegou mais perto da solução vou dar como a resposta correta ok!?

Vlw!

R

Isso ainda está errado! Dessa maneira você vai fechar o Statement subjacente, e vai precisar ficar recompilando a query a cada passada no laço.

B

Deu certo amigo. Nenhum cursor aberto.
Funcionou perfeitamente.
Agradeço.

Criado 17 de maio de 2017
Ultima resposta 17 de mai. de 2017
Respostas 9
Participantes 5