Tentando criar bons códigos

estou com um DAO(resumido)

[code]public class Dao {

    private Connection connection = null;
    private PreparedStatement preparedStatement = null;
    private Statement statement = null;

    /**
     * busca e carrega as informações do arquivo properties relacionado ao banco de dados
     * @return <b>Properties</b> com as informações de acesso ao banco de dados
     */
    private Properties carregarPropriedades(){
              ...
              return properties;
    }

    /**
     * método que carrega o driver de conexão do banco de dados.<br>
     * sempre chamar fecharConexao em seguida
     * @return <b>Connection</b> pronto para acessar o banco
     */
    Connection abrirconexao(){
           ....
           carregarPropriedades();
            return connection;
            
    }


    /**
     * fecha as conexões preparedStatement e connection
     */
    void fecharConexao(){
          ...
    }        
    
    /**
     * cria as tabelas do banco
     */
    private void criaBancoTabelas() {
            ...
    }

    /**
     *
     * @return void - verifica se já existe um banco
     */
    public void verificarExistenciaBanco(){
        ...
    }

}[/code]
o que acham… eu deveria sempre fechar as conexões quando um método terminar ou poderia deixar alguma coisa sempre carregada? se deixar algo carregado, imaginei deixar a propriedade/objeto properties, q está lendo os dadosde acesso ao banco num arquivo “banco.properties”.
tento deixar tudo dentro dos métodos, estou certo? se precisar deixar fora do método, seria legal apontar null quando terminar de usar no método?
o método fechar conexão é preguiçoso mesmo, se tiver fecha se n tiver não faz nada, pode ser??
vlw galera.

Penso que neste caso deve-se fechar a conexão. Já aconteceu comigo do Mysql(no meu caso) dar um “too many connections” por não fechar.
Apesar de ser mais tenso para mudar as configurações, procuro deixar os dados de conexão com o banco em programação mesmo. Acho mais seguro.

olá daquinho,

Legal a sua idéia de seguir o pricipio da boa prática.
Mas hoje em dia, não se usa mais criar métodos para abrir e fechar conexão, pois normalmente implementa-se caso o programador queira um método de pool de conexões, que vai fazer um rodizio entre n conexóes abertas para as diversas solicitações ao banco serem respondidas com seus dados.

Na realidade vc poderia criar um método factory(ver padão factory) que te retorne uma connection.
no seu dao no método de gravar os dados é ideal que vc use um bloco try/catch/finnaly para persistir os dados, tratar as exception e em seguida fechar os statements e até memso a conexao com a base caso prefira. Eu normalmente quando uso jdbc, em meu generic DAO prefico criar um método factory.

Veja:


public void getConnection(){

if(con=null)
con=return DriverManager.getConnection(url, username, password);
return con;
}

e no método de gravar os dados no banco:


public void gravarDados(){

/ prepared statement para inserção
/*		String sql = "insert into tbl_classematerial (CLM_NOME, CLM_DESCRICAO, CLM_DTCAD, CLM_DTALT, CLM_ATIVO, CLM_OBS) values (?,?,now(),now(),?,?)";
		
               try{ // previno contra lançamento de exception caso ocorra!!
               PreparedStatement stmt = con.prepareStatement(sql); // chama o prepared statment
		// seta os valores

		stmt.setString(1, classeMaterial.getNome());
		stmt.setString(2, classeMaterial.getDescricao());
		stmt.setString(3, classeMaterial.getAtivo());
		stmt.setString(4, classeMaterial.getObservacao());
		// executa
		stmt.execute();
                
                }catch(SQLException e){
                   // trata a exception e gera o log

                 }finally{
                 // Aqui se vc quiser vc pode fechar a conexão e fechar os statments
                  stmt.close();
                  con.close();
                 }

Em anexo segue um exemplo de generic dao, que na época que eu usava jdbc consegui na aula do diego souza do carmo da devmedia. Ele deu uma aula muito interessante de Swing onde ele usava jdbc e boas práticas. È bom pra vc ver como fica mais facil a implementação. Esses daos são parte de um sistema que desenvoli na época para um cliente.

Espero ter ajudado;
Fallow Abraço

	stmt.close();

}

show pessoal…
obrigado.
abaixo o código, o que acham?

package agenda.dao;

import agenda.excessao.ErroAgenda;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class Dao {

        private Connection connection = null;
        private PreparedStatement preparedStatement = null;
        private Statement statement = null;

        /**
         * busca e carrega as informações do arquivo properties relacionado ao banco de dados
         * @return <b>Properties</b> com as informações de acesso ao banco de dados
         */
        private Properties carregarPropriedades(){
                File file;
                Properties properties = new Properties();
                
                try {
                        file = new File("banco.properties");
                        properties.load(new FileInputStream(file));
                }
                catch (FileNotFoundException ex){
                        ErroAgenda.exibirMensagemErro("o Arquivo de propriedades que acessa o banco não pode ser encontrado.\n"
                                + "A aplicação será encerrada.\n"
                                + "código: "+ex.getMessage());
                        System.exit(0);
                }
                catch (IOException ex){
                        ErroAgenda.exibirMensagemErro("O arquivo de configurações não pode ser carregado,\n"
                                + "a aplicação será encerrada.\n"
                                + "código: "+ex.getMessage());
                }

                return properties;
        }

        /**
         * método que carrega o driver de conexão do banco de dados.<br>
         * sempre chamar fecharConexao em seguida
         * @return <b>Connection</b> pronto para acessar o banco
         */
        final Connection getConexao(){        
                String url = carregarPropriedades().getProperty("banco.url");
                String user = carregarPropriedades().getProperty("banco.user");
                String password = carregarPropriedades().getProperty("banco.password");
                String driver = carregarPropriedades().getProperty("banco.driver");
                try {
                        if(connection == null){
                             Class.forName(driver);
                             connection = DriverManager.getConnection(url, user, password);
                       }
                }
                catch (ClassNotFoundException ex) {
                        ErroAgenda.exibirMensagemErro(ex);
                }
                catch (SQLException ex){
                        ErroAgenda.exibirMensagemErro(ex);
                }
                return connection;
                
        }

        /**
         * fecha as conexões preparedStatement, statement e connection
         */
        final void fecharConexao(){
                try {
                    if(!statement.isClosed()) statement.close();
                    if(!preparedStatement.isClosed()) preparedStatement.close();
                    if(!connection.isClosed()) connection.close();
                }
                catch (SQLException ex) {
                    ErroAgenda.exibirMensagemErro(ex);
                    statement = null;
                    preparedStatement = null;
                    connection = null;
                }
        }

        /**
         * cria o banco e as tabelas caso não existam
         */
        public void criaBancoTabelas() {
                DaoCriaTabelasDoBanco daoTabelas = new DaoCriaTabelasDoBanco();

                try {
                        statement = getConexao().createStatement();

                        // não precisa receber nada, sempre retrona um ResultSet, se não tiver
                        // banco, cai na excessão, se tiver banco, mesmo sem tabelas, dá erro e vai para
                        // catch, executando a criação  das tabelas
                        statement.executeQuery(daoTabelas.SQLTesteSeExisteBanco());

                } catch (SQLException exception) {
                        try {
                                statement.executeUpdate(daoTabelas.criaTabelaContato());
                                statement.executeUpdate(daoTabelas.criaTabelaEndereco());
                                statement.executeUpdate(daoTabelas.criaTabelaTelefone());
                                statement.executeUpdate(daoTabelas.criaTabelaEmpresa());
                                statement.executeUpdate(daoTabelas.getFKContatoNaTabelaEmpresa());
                                statement.executeUpdate(daoTabelas.getFKEnderecoNaTabelaEmpresa());
                                statement.executeUpdate(daoTabelas.getFKTelefoneNaTabelaEmpresa());
                        } catch (SQLException sQLException) {
                        }
                }
        }
}

o método que cria as tabelas é public pq lá na classe principal, a primeira coisa a fazer será verificar se existe banco e crias as tabelas caso não exista.
os outros são package pq todas as classes que usarem qualquer acesso ao banco estarão no memso pacote(os beans estão em outro pacote)

Oi daguinho,

faça uma pequena modificação na seguinte linha:

Class.forName(driver);
connection = DriverManager.getConnection(url, user, password); 

mude para:

Class.forName(driver);
if(connection==null) //caso seja nula,não existe uma conexao, por isso é necessário chamar o DriverManager novamente e pedir outra conexao.
connection = DriverManager.getConnection(url, user, password); 
return connection // se não for nula retorna a conexao existente

Outra dica. Se vc está querendo aprender corretamente. Não perca muito tempo implementando em JDBC. Faça um esforço para aprender Hibernate, JPA…vai valer mais a pena!

VC vai comprovar isso! :smiley:

Fallow

já estou interado, com certeza o uso de framework acelera o desenvolvimento e ajuda a eliminar vários erros, mas por enquanto e o tamanho da aplicação não pedem o uso deles, obrigado.
vou acrescentar a sugestão ao código.

mais uma…
boa prática na importação de classes e descrever classe por classe ao invés de usar o asterisco, isso quando não se usa uma enorme quantidade de classes do mesmo pacote.
Pensei em trocar as importações pelo caminho completo da classe em sua declaração, será que isso dá desempenho?
obs: o Netbeans faz uso disso.

Uma boa sugestão é deixar estes problemas por conta de uma boa framework.
Se for usar JDBC puro assim, vale a pena usar um JDBCTemplate com Spring e deixar pra se preocupar com problemas mais nobres como a lógica de negócio.
Sem falar que vc ganha o benefício da injeção de dependências de brinde. Daí é só implementar tudo usando interfaces e vai ficar com um baixo acoplamento tb.

[]s

Daquinho, ao postar códigos, use a tag code, não a tag quote, como você vem usando.
O botão code fica ao lado direito do botão quote.

a minha idéia é saber independentemente de uso de framework.
e quanto a troca de code por cote, foi mal o descuido, já sabia mas não tinha observado q estava fazendo uso errado.(corrigido).

oi pessoal, por exemplo…
tenho classes DAO que geram erros, até onde passo adiante com o thows? ate´onde passo essa tarefa?
em cada método q aparece um erro para tratar, eu o trato ali memso, por exemplo o DAO q é a camada mais longe da visão(ou quse sei lá), e esses erros eu sempre notifico com um JOptionPane, sugeriram que eu usasse thows para exibir essas mensagens de erro quando forem usadas por alguém na casse de visão ou quase isso, n lembro corretamente.
o q quero é uma sugestão mais exata de até onde eu levo o erro ou se devo continuar como faço, sempre usando o try-catch onde aparece o erro e exibir com JoptionPane.

[quote=daquinho]oi pessoal, por exemplo…
tenho classes DAO que geram erros, até onde passo adiante com o thows? ate´onde passo essa tarefa?
em cada método q aparece um erro para tratar, eu o trato ali memso, por exemplo o DAO q é a camada mais longe da visão(ou quse sei lá), e esses erros eu sempre notifico com um JOptionPane, sugeriram que eu usasse thows para exibir essas mensagens de erro quando forem usadas por alguém na casse de visão ou quase isso, n lembro corretamente.
o q quero é uma sugestão mais exata de até onde eu levo o erro ou se devo continuar como faço, sempre usando o try-catch onde aparece o erro e exibir com JoptionPane.[/quote]

daquinho,

Este lance de tratamento de exceptions é meio controverso. Existem basicamente duas linhas de pensamento. Os que defendem as Checked Exceptions e os que defenderm as UncheckedExceptions.
Vou dar algumas sugestões da forma que eu gosto de tratar a coisa:

Gosto de usar Checked Exceptions para tudo que possa ou deva ser tratado e deixo as RuntimeExceptions apenas para situações que não poderiam ser solucionadas programaticamente.
Costumo criar uma Família de Exceptions para cada camada da aplicação sempre tendo uma exception pai por camada. Ex.:

DaoException => SelectException, InsertException, etc.
ServiceException => UserExistsException, NoBudgetException, etc.

Cada camada só deve deixar passar para camada superior, as exceptions próprias, assim se um método de uma classe da camada de serviço usa um método de um DAO e este lança uma exception esta exception deve ser uma DaoException, ou seja, se acontecer algo que me lançar uma SQLException, eu não passo isto para a camada de cima mas ao contrário, encapsulo numa Exception da camada DAO. Ex.:


		try{
			// codigo de acesso jdbc aqui
		}catch(SQLException e){
			throw new DaoException("Ocorreu um erro X", e);
		}

Desta forma, os métodos da camada de serviço, só precisam tratar a DaoException que é da camada imediatamente abaixo. A mesma coisa nesta camada, só deixo passar para a camada de cima, as exception desta camada.


		try{
			// codigo de acesso DAO aqui
		}catch(DaoException e){
			throw new ServiceException("Ocorreu um erro na camada DAO", e);
		}

Assim, na camada de visualização (view), eu posso simplesmente tratar as exception de serviço e exibir msg de erro:


		try{
			user = UserService.getUser(id);
			// codigo que faz uso de user
		}catch(ServiceException e){
			log.error("Erro xyz", e);
			errorMessage.setText("Ocorreu um erro ao buscar o usuário de id {1} na base: " + e.getMessage(), id);
		}

Considerando que todas as exceptions de serviço são filhas de ServiceException, basta que eu trate esta única exception e caso eu queira tratar algum erro mais especializado, posso acrescentar uma clausula catch mais específica, por ex.:


		try{
			// codigo que faz uso de camada service
		}catch(NoBudgetException e){
			log.error("Erro xyz", e);
			errorMessage.setText("Ocorreu o erro xyz: " + e.getMessage());
		}catch(ServiceException e){
			log.error("Erro k", e);
			errorMessage.setText("Ocorreu o seguinte erro: " + e.getMessage());
		}

Enfim, esta é a forma que gosto de trabalhar.

Segue, para enriquecer o assunto, um texto interessante sobre o uso de checked ou unchecked exceptions (o autor tende para as unchecked exceptions mas ele tem alguns argumentos interessantes. Vale a pena ler e pensar a respeito.):

http://tutorials.jenkov.com/java-exception-handling/checked-or-unchecked-exceptions.html

[]s

para mim isso não é novidade mas me parece estranho, já que na própria filosofia java diz não precisar reinventar.

quem chama o método trata a excessão.

isso significa q vou passando as excessões até chegar na view?

jaja vou ler esse artigo q me indicou

Oigi, meu inglês não é muito bom, a leitura ficou muito cansativa e confusa, vou tentar nos foruns pegar o uso de excessões.

Reinventar o quê??? Não entendi esta colocação já que criar uma exception é procedimento normal e não tem nada a ver com reinventar já que a exception é nova e com propósito novo.

Sim, trata, encapsula em exception da própria camada e relança para a camada seguinte.

Exato, sempre vai relançando as exceptions devidamente encapsuladas nas exeptions das camadas em que está tratando, a menos que seja coisa que tenha mesmo que ser tratada e morrer naquela camada. Se não tem o que fazer, relança encapsulada na exception da camada. Assim, exceptions que porventura chegaram na view é porque vc não teve como resolver o problema e vai ter que avisar ao usuário de que algo está errado. Aí é só providenciar uma msg amigável.
Lembrando que não se deve deixar a stacktrace explodir na cara do usuário.
Outra coisa que gosto de fazer é… peguei uma exception, enfio no log.
Se estiver usando o commons logging com log4j, use sempre os métodos com dois parâmetros que possuem a assinatura que recebe throwable para que a stacktrace vá parar no arquivo de log, senão vc perde informação pois se usar o método que recebe apenar um parâmetro, ele vai dar um toString na exception e vai colocar apenas a msg sem a stacktrace no log, deixando mais difícil localizar o erro. Ex.:

log.error(e, e); //no primeiro parâmetro, se não tiver msg pra escrever, deixe apenas a exception que vai pegar a msg dela

ao invés de:

log.error(e);

[]s

Procure dar uma estudada em Inglês. É importantíssimo. As informações mais recentes sempre estão em inglês. Leva algum tempo pra alguém passar isto pro português.
Dê uma forçada.
Eu fiz isto na época da facu sob recomendação de um professor. Ele disse:
“Vc tem que forçar. Pegue coisas que vc gosta, textos sobre carros, tecnologia, etc. Tudo em inglês e comece a ler na cara e na coragem… Filmes com legende em inglês.etc… Com o tempo vc vai ficar bom na leitura.”

Assim eu fiz. E funcionou.

[]s

mas eu tentei, essa leitura ficou ruim e difícil e deixei para depois.
muito obrigado.