Problemas nos cadastros com os objetos DAO

Pelas suas perguntas me parece que ainda não lhe está claro o que é uma classe e o que é um objeto.

Você já reparou que o método getInstance() da classe BD é estático?
E ele inicializa um atributo estático do tipo BD somente se esse atributo ainda não foi inicializado.
Você pode ter trocentas variáveis bd espalhadas pelas suas classes, como eles são inicializadas através do método BD.getInstance(), elas vão referenciar o mesmo objeto, o mesmo endereço de memória, entende?

Tanto o endEntClientes quanto clientes possuem um atributo bd, porque você optou em fazer assim, só você pode responder.
Minha sugestão é usar sempre as variáveis do tipo BD como sendo variáveis locais.

Você não precisa acessar um atributo do endEntClientes é só você fazer isso:

BD.getInstance().close();

O atributo bd tanto do endEntClientes quanto do clientes referência o mesmo objeto retornado pelo BD.getInstance() então é indiferente qual bd você vai usar, eles são o mesmo objeto.

Recomendo você apagar o atributo bd dessas classes e utilizar somente variáveis locais.

Acho que você ainda não entendeu o que o getInstance() faz, sugiro que coloque os seguintes prints:

public static synchronized BD getInstance() {
    if (instance == null) {
        System.out.println("Criando objeto da classe BD");
        instance = new BD();
    }
    System.out.println("Retornando objeto existente da classe BD");
    return instance;
}
1 curtida

Gente, eu gostaria de adicionar uma questão.

Pelo que pesquisei, o que o @anyblueangel3 tá fazendo é uma ideia muito ruim.

Criar uma conexão no inicio do programa e mantê-la aberta até o usuário fechar pode causar varios problemas. Por exemplo: a conexão poderia fechar devido ao tempo ocioso por devido a alguma falha.

Do que eu entendi nas minhas pesquisas, o correto seria vc abrir e fechar a conexão a cada operação. Ou seja, se for inserir algum dado, vc abre a conexão, insere o dado e fecha; se for pegar uma lista de entidades, vc abre a conexão, faz o select e fecha.

O problema óbvio desta abordagem é que criar conexões é custoso pro sistema e é por isso que para minimizar os impactos negativos na performance do programa, nós podemos usar connections pools. Basicamente nós criamos uma certa quantidade de conexões e as deixamos em espera, então, sempre que precisamos nos conectar pegamos uma destas conexões ociosas, então, quando invocamos o método close, na verdade não estamos fechando, mas sim devolvendo ela para a pool, ou seja, para a lista de conexões ociosas.

Há bibliotecas de terceiros que servem para gerenciar este pool, mas alguns drivers JDBC já implementam este conceito sem precisar de terceiros. Parece que o driver do MySQL tem esta funcionalidade, eu só não sei como usar, na documentação até tem um exemplo, mas é usando algum application server, se quiser olhar:

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-usagenotes-j2ee-concepts-connection-pooling.html

Eu estudei um pouco usando MariaDB e na página do driver mostra um exemplo mais facil de aplicar, se tiver curiosidade de ver como é neste banco de dados:

Mas que que vcs acham, é isso mesmo que eu entendi?

Cordiais saudações, wldomiciano,

Entendi seu ponto de vista, mas a conexão não vai ficar aberta todo tempo. Por enquanto estou mexendo com cadastros então cada vez que entre em uma classe (programa) de um cadastro do banco de dados eu abro a conexão, cada vez que saio deste programa fecho a conexão.

Atenciosamente,
Ronaldo

1 curtida

Fiz a modificação de colocar BD.getInstance().close() e abrir conforme você me orientou Staroski, mas agora tem surgido uns probleminhas, parece que ele fecha a conexão e não abre de novo quando eu preciso dela. Ele tem respondido que clientes que estão cadastrados não estão. Veja algumas mensagens de tempo de execução:

run:
Conectou! 
Desconectou...
Desconectou...
erro: java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.select * from clientes where id_cgc_cpf = ?17561408803
erro: java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.select * from clientes where id_cgc_cpf = ?17561408803
erro: java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.select * from clientes where id_cgc_cpf = ?1
erro: java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.select * from clientes where id_cgc_cpf = ?17561408803
Desconectou...
BUILD SUCCESSFUL (total time: 2 minutes 51 seconds)

Repare que ele desconectou duas vezes e conectou uma só. Parece que nossa instancia não esta sendo criada cada vez que uso o comando BD.getInstance().close().

O que faço agora?

Atenciosamente,
Ronaldo

Então você está chamando o close() em mais de um lugar.
Chame o close() somente onde você faz o System.exit(0).

Staroski, o arquivo não pode ser fechado apenas quando ocorre System.exit(0), pelo menos no meu ponto de vista. Se você discorda pode mandar mensagem.

A saída do sistema System.exit(0) só acontece no programa de menu principal, mas o banco de dados é fechado em toda saída de programa. Então (acho que resolvi esta questão) cada vez que chamo o método close() declaro instance = null e da próxima vez que tentar entrar no cadastro ele cria uma nova conexão. Pronto.

Se tiver ideia melhor pode falar. Eu não sei se isto está certo mas agora não tem mais problema de fechar o arquivo mais vezes que abre e o sistema voltou a funcionar (não 100 por cento, mas melhorzinho).

Veja como ficou o close() da classe DB, eu acrescentei instance = null.

public void close() {
    try {
        connection.close();
        instance = null;
        System.out.println("Desconectou...");
    } catch(Throwable t) {
        throw new RuntimeException("Erro ao fechar BD", t);
    }
}

Não entendi, pra você sair de um programa, você tem que fazer System.exit.

Em vez de abrir um JFrame para cada programa, o livro que estava estudando cria programas extends JPanel. Deve ter apenas um ou dois JFrame e vários JPanel. Na verdade acho que só uma tela é JFrame e o resto é JPanel.
Só uso System.exit (0) quando vou fechar tudo e voltar para o Windows, no caso para o Apache NetBeans.
Não fui eu que optei por essa estratégia foi um livro didático. Mas se você tem uma sugestão que considera mais profissional, por favor, me ensine. Diga como você abre várias telas e faz um programa mais profissional.
Atenciosamente,
Ronaldo

Exatamente e é nesse momento que você deveria chamar o close() do seu BD, pelo que entendi agora, acho que você está chamando o close() ao sair de cada tela.

Antes você havia dito que chamava o close() a cada “saída de programa” e saída de programa é quando faz System.exit(0).

Em aplicações desktop eu sempre tenho uma classe que estende JFrame, essa classe será responsável por apresentar as telas do sistema e cada classe de tela eu costumo estender JPanel.

1 curtida

Entendi, Staroski. É exatamente isto que estou fazendo. Criar um JFrame e usar vários JPanel. Porém, eu não concordo que o arquivo deva ficar direto aberto. Assim quando eu troco de tela, voltando para o menu principal, eu fecho a conexão. Daí quando eu entro em outra classe se for necessário (como sempre é) eu abro uma nova conexão. Ou seja o banco de dados não vai ficar direto aberto, fica menos tempo aberto.

A grande questão é se meu procedimento pode ser considerado como correto, adequado ou eu posso estar sacrificando o equipamento com essas aberturas e fechamentos. Na verdade não sei se um programador profissional deixa direto o arquivo aberto. Tinha achado que não e aí o que você acha?

Atenciosamente,
Ronaldo

Também gostaria de saber a opinião do Staroski.

Porque, como eu disse, do que entendi das minhas pesquisas, o jeito que vc está fazendo não é o correto.

O correto seria abrir e fechar a cada operação e, para minimizar o consumo de recursos, usaria um pool de conexões.

Veja só, wldomiciano, em mensagem anterior eu expliquei a forma como estou fazendo: cada vez que vou fazer uso do banco de dados crio uma conexão, cada vez que termino os trabalhos com um cadastro fecho a conexão.

A questão de usar um pool de conexões não faço idéia do que seja ou quais comandos utilizar (como implementar). E assim vou caminhando lentamente…

Atenciosamente,
Ronaldo

Em aplicações standalone é perfeitamente normal ter apenas uma conexão sempre aberta com o servidor. Conecta ao abrir a aplicação e desconecta ao fechar. Estar sempre a conectar/desconectar é muito pesado.

Assumindo que não haverão threads de processamento paralelo com acesso a BD, a pool de conexões não seria necessária para nada já que o utilizador estará fazendo uma operação de cada vez. Mesmo que isso acontecesse, então neste caso poderia abrir uma segunda conexão para a thread e fechar no final do processamento.

Pools de conexões são usadas em servidores aplicacionais onde, com poucas conexões, consegues que muitos utilizadores utilizem o sistema simultaneamente.

2 curtidas

@pmlm é mesmo, não tinha pensado que o pool seria desnecessário quando não há multiplas thread operando nele.

Mesmo assim ainda tem uma coisa a se considerar: E se a conexão cai entre uma operação e outra?

Neste caso, mesmo com apenas uma thread acessando o banco por vez, o pool não faria sentido? Porque vc pode configurá-lo para sempre manter um certo número de conexões abertas e se uma cair, ele cria uma nova, se nenhuma cair ele só a reaproveita. E eu pensei que mesmo se uma pool de apenas uma conexão, ainda assim seria útil, por causa desta questão da conexão poder cair no meio do caminho.

O que vc acha?


@anyblueangel3 Me desculpe por te fazer repetir esta informação. Acho que agora sim eu entendi o que vc disse.

Seria assim:

Quando o usuário entra na tela de cadastro, vc já cria uma conexão. Quando o usuário clica no botão de salvar ou de fechar, vc quer fechar a conexão. Correto?

Se for isso mesmo, eu pensei numa coisa: E se vc não abrir a conexão quando a tela abre? Vc poderia abrir a conexão apenas quando o usuário clica no botão de salvar, assim vc não precisaria se preocupar caso o usuário entre na tela de cadastro e feche a tela sem cadastrar nada.

Para uma aplicação desktop standalone, não vejo problema nenhum em abrir a conexão ao logar no sistema e só fechar ao sair dele.

Se fosse uma aplicação com um banco de dados sendo acessado por diferentes clientes, como por exemplo uma aplicação web, aí eu abriria a conexão quando necessário e preferiria utilizar pool de conexões.

Mas pra uma aplicação desktop standalone, que só roda localmente, não vejo problema nenhum.

1 curtida

Faz sentido mesmo.

E neste caso que citei aqui, de usar a pool apenas como segurança pro caso desta única conexão cair?

Faz sentido sim e simplifica a implementação.
Eu apenas nunca utilizei em aplicação standalone pois sempre tinha o banco configurado sem timeout.

Ah, entendi. Obrigado.

Podes, por exemplo, ter um mecanismo de validação no teu método de obter a conexão que valida que a mesma está aberta antes de devolver. Se não estiver, conecta novamente.

Sim, mas ter uma pool de uma conexao para tratar esse caso parece exagerado :slight_smile:

E que tipo de cadastro é esse? Muitas vezes num cadastro tens combo boxes com valores que já terão de ser lidos da BD (país, estado, algum tipo de produto…), logo já terias de ter aberto uma conexao antes para mostrar o formulário, mesmo que depois seja cancelado.

É verdade, não tinha pensado nesta situação dos combo boxes, e realmente, neste caso, dava para fazer só uma checagem simples para ver se a conexão está ativa mesmo. Obrigado.