Prática 2: Acesso ao Banco de Dados
Nesta prática iremos acessar o banco de dados criado na prática 1 utilizando a ferramenta nativa do NetBeans. Mas antes de iniciar esta prática, se certifique de que o gerenciador de banco de dados MySQL está em execução e também de que o banco de dados pedido, da prática 1 foi criado corretamente.
Adicionalmente, nesta prática criaremos uma nova aplicação e conectaremos no banco de dados, onde poderemos inserir, consultar, alterar e excluir os registros através de uma conexão usando driver JDBC para o banco de dados MySQL.
2.1. Acessando o servidor MySQL através do NetBeans
1. Com o NetBeans aberto, acione o comando de menu Janela > Serviços (Ctrl + 5);
2. Clique no item Banco de Dados, com o botão direito do mouse, acionando o comando Nova Conexão...; 3. Selecione o Driver MySQL (Connector/J driver) e clique no botão Próximo;
4. Altere o conteúdo do campo Banco de dados para pedido (caso tenha definido uma senha, esta deve ser informada) e clique no botão Testar Conexão;
5. Clique no botão Próximo;
6. Clique no botão Próximo novamente;
7. Altere o nome da conexão para pedido e clique no botão Finalizar para fechar a janela;
8. Clique com o botão direito do mouse sobre a nova conexão que acabamos de criar (pedido) e acione o comando
Conectar;
9. Na janela de diálogo que solicita o usuário e a senha, deixe os valores em branco (a não ser que você tenha definido uma senha) e clique no botão OK;
10. Navegue na estrutura em árvore e verifique que os objetos de banco de dados criados na prática 1 estão disponíveis;
11. Clique com o botão direito do mouse novamente sobre a conexão criada (pedido) e acione o comando Executar
Comando..., onde será aberta uma nova janela para inserção de comandos SQL;
12. Execute o comando abaixo para listar os bancos de dados existentes e verificar que a conexão está funcionando adequadamente:
show databases
13. Execute os outros comandos aprendidos na prática 1, e verifique que funcionam nesta interface gráfica também; 14. Insira, altere, exclua e consulte os dados nas tabelas criadas na prática 1, usando essa janela de execução de
comandos SQL do NetBeans.
2.2. Criando um novo projeto Java no NetBeans
1. No NetBeans, acione o comando de menu Janela > Projetos (Ctrl + 1); 2. Acione o comando Arquivo > Novo Projeto...;
3. Na janela Novo Projeto, selecione a categoria Java e o tipo de projeto Aplicação Java; 4. Clique no botão Próximo;
5. Informe pedido como o nome do projeto;
6. Informe um local para salvar o projeto (é recomendável salvar em seu pen drive); 7. Marque a opção Usar Pasta Dedicada para Armazenar Bibliotecas;
8. Desmarque a opção Criar Classe Principal; 9. Clique no botão Finalizar;
10. Navegue na estrutura do projeto recém-criado, pedido.
2.3. Classe utilitária para a conexão com o banco de dados
Para centralizar a conexão com o banco de dados, se faz necessário criar uma classe utilitária para gerenciar a conexão com o banco de dados. Para um reaproveitamento melhor das implementações, recomenda-se criar um projeto específico para as classes utilitárias que podem ser reutilizadas e reaproveitadas em outros projetos. Na sequência de práticas a serem executadas, todos os arquivos serão mantidos em um único projeto.
A seguir, vamos implementar uma classe de conexão com o banco de dados e também começaremos a criar as classes de teste unitário para verificarmos se a implementação está funcionando de fato.
1. Crie um novo pacote chamado br.edu.univag.util.sql, para armazenarmos a nossa classe utilitária para gerenciar as conexões com o banco de dados;
2. No pacote acima, crie uma nova classe Java chamada ConexaoBD;
3. Para conectarmos com um banco de dados, a aplicação precisa conhecer qual é o driver de conexão a ser usado, a URL com o endereço do banco de dados, o usuário de banco de dados e a senha para efetuar o login. Então, acrescente os atributos necessários para armazenar estes dados:
private final String driver; private final String url; private final String usuario; private final String senha;
4. Precisamos também de um atributo para referenciarmos a conexão criada e outro para o situação (conectado ou não):
private Connection con; private boolean conectado;
5. Acrescente a importação da classe java.sql.Connection, utilizando o atalho de teclado
<CtrL>+<Shift>+<i>;
6. Crie um construtor, utilizando o atalho de teclado <Ctrl> + <Espaço>, de forma que os atributos driver, url, usuario e senha possam ser preenchidos. No caso, o NetBeans oferece auxílio apenas para o construtor default ou para o construtor contendo todos os atributos, então selecione o segundo e remova o código que esteja sobrando:
public ConexaoBD(String driver, String url, String usuario, String senha) { this.driver = driver;
this.url = url;
this.usuario = usuario; this.senha = senha; }
7. Inclua, através do atalho de teclado <Ctrl>+<Espaço>, o método acessor get para o atributo conectado (note que não é oferecido o método getConectado e sim isConectado, essa é uma característica específica para os atributos do tipo boolean, pois possui uma semântica melhor em inglês);
8. Agora será necessário criar os métodos para se conectar e desconectar no banco de dados: public void conectar() throws SQLException {
try {
this.conectado = false; Class.forName(driver);
con = DriverManager.getConnection(url, usuario, senha); con.setAutoCommit(false);
this.conectado = true;
} catch (ClassNotFoundException ex) { throw new SQLException(ex);
} }
public void desconectar() throws SQLException { if (isConectado() == true) {
con.rollback(); con.close();
} }
9. Adicione as importações necessárias (<Ctrl>+<Shift>+<i>);
10. Para podermos executar comandos SQL no banco de dados, precisamos criar objetos do tipo java.sql.PreparedStatement. Adicionalmente, como as chaves primárias das nossas tabelas são geradas automaticamente, será necessário informar quando iremos ou não precisar obter as chaves geradas pelo banco de dados. Então, teremos dois métodos para executarmos os comandos SQL e um terceiro para obter o valor da chave gerada pelo banco de dados:
public PreparedStatement prepararSQL(String sql) throws SQLException { return prepararSQL(sql, false);
}
public PreparedStatement prepararSQL(String sql, boolean gerarChaves) throws SQLException { if (isConectado()) { if (gerarChaves) { return con.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return con.prepareStatement(sql, PreparedStatement.NO_GENERATED_KEYS); } } else {
throw new SQLException("Não conectado ao banco de dados!"); }
}
public long getChave(PreparedStatement ps) throws SQLException { long resposta = 0;
try (ResultSet rs = ps.getGeneratedKeys()) { if (rs.next()) { resposta = rs.getLong(1); } } return resposta; }
11. Adicione as importações necessárias (<Ctrl>+<Shift>+<i>);
12. No código-fonte acima, obtemos o valor da chave gerada através do método getGeneratedKeys(), da classe java.sql.PreparedStatement e, adicionalmente precisamos da interface java.sql.ResultSet para tratarmos a resposta deste método;
13. Para finalizarmos, precisamos de um método para formatar o resultado de maneira que possa ser visualizado em uma tabela (javax.swing.JTable), quando construirmos a interface visual de uma consulta ao banco de dados:
//import javax.swing.table.DefaultTableModel;
public DefaultTableModel getTabela(ResultSet rs) throws SQLException { DefaultTableModel tabela = new DefaultTableModel();
//Obtém os meta dados
ResultSetMetaData rsmd = rs.getMetaData(); //Pega o número de colunas
int numColunas = rsmd.getColumnCount();
//Cria um vetor para guardar o título das colunas String[] nomesDasColunas = new String[numColunas]; //Alimenta o vetor com os títulos das colunas for (int i = 0; i < numColunas; i++) {
}
//Ajusta os títulos das colunas da tabela em função dos //dados do vetor
tabela.setColumnIdentifiers(nomesDasColunas); while (rs.next()) {
Object[] linha = new Object[numColunas]; for (int i = 0; i < numColunas; i++) { linha[i] = rs.getObject(i + 1); } tabela.addRow(linha); } return tabela; }
14. Adicione as importações necessárias (<Ctrl>+<Shift>+<i>); 15. Pronto, agora temos uma classe utilitária para banco de dados. 2.3.2. Driver JDBC para a conexão com o banco de dados
Para se conectar com o banco de dados MySQL, será necessário incluir a biblioteca de conexão conhecida como
MySQL Connector/J:
1. Clique com o botão direito em Bibliotecas, do seu projeto pedido, no NetBeans, acionando o comando Adicionar
JAR/Pasta...;
2. Selecione o arquivo C:\Program Files\NetBeans 8.1\ide\modules\ext\mysql-connector-java-5.1.23-bin.jar; 3. Marque a opção Copiar para a Pasta de Bibliotecas;
4. Clique no botão Abrir;
5. Desta forma, podemos levar o nosso projeto completo, incluindo a biblioteca de conexão JDBC para o banco de dados MySQL.
2.3.3. Teste de Unidade da classe ConexaoBD
Bom, até agora não testamos as classes que criamos. É sempre uma boa prática testarmos os códigos que criamos para termos a certeza de que estão funcionando, com o objetivo de aumentar a qualidade dos softwares desenvolvidos:
1. Acione o comando de menu Arquivo > Novo Arquivo...;
2. Na janela Novo Arquivo, vá para o campo Filtro, digitando teste; 3. Selecione o tipo de arquivo Teste para a Classe Existente; 4. Clique no botão Próximo;
5. Na nova janela, Selecionar a Classe, clique no botão Procurar;
6. Selecione a classe br.edu.univag.util.sql.ConexaoBD no campo Classe a Testar; 7. Clique no botão OK;
8. Clique no botão Finalizar;
9. Se for perguntado a versão do JUnit a ser utilizado, selecione a opção JUnit 4.x e confirme;
10. Note que agora o seu projeto possui mais dois elementos: Pacotes de Teste e Bibliotecas de Testes;
11. A classe de teste possui três seções: o construtor, as configurações para serem executadas antes e depois tanto da classe como de cada método, os testes para cada um dos métodos a ser avaliado;
12. Execute o teste (<Ctrl>+<F6>);
13. Note que ocorreram erros (diferente de falhar) nos testes, pois ainda precisam ser implementados para testarem cada um dos cenários adequadamente;
14. Primeiramente, crie um atributo estático para referenciar um objeto de ConexaoBD: private static ConexaoBD conBD;
15. O método setUpClass é chamado apenas uma vez durante o tempo de vida da classe de teste, então é o local ideal para criarmos uma referência a um objeto estático:
@BeforeClass
public static void setUpClass() { System.out.println("setUpClass");
String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost/pedido"; String usuario = "root";
String senha = "";
conBD = new ConexaoBD(driver, url, usuario, senha); }
16. Agora, podemos implementar o primeiro método de teste: @Test
public void testIsConectado() throws SQLException { System.out.println("isConectado");
ConexaoBD instance = ConexaoBDTest.conBD; boolean expResult1 = false;
boolean result1 = instance.isConectado(); assertEquals(expResult1, result1);
instance.conectar();
boolean expResult2 = true;
boolean result2 = instance.isConectado(); assertEquals(expResult2, result2);
instance.desconectar(); boolean expResult3 = false;
boolean result3 = instance.isConectado(); assertEquals(expResult3, result3);
}
17. Execute a classe de teste novamente e confira na janela Resultados do Teste que existe um teste aprovado; 18. Crie um teste para o método conectar:
@Test
public void testConectar() throws SQLException { System.out.println("conectar");
ConexaoBD instance = ConexaoBDTest.conBD; instance.conectar();
boolean expResult = true;
boolean result = instance.isConectado(); assertEquals(expResult, result);
}
19. Para cada método da classe ConexaoDB, implemente um método de teste excute o teste, verificando se cada um deles está passando:
@Test
public void testDesconectar() throws SQLException { System.out.println("desconectar");
ConexaoBD instance = ConexaoBDTest.conBD; instance.desconectar();
boolean expResult = false;
} @Test
public void testPrepararSQL_String() throws SQLException { System.out.println("prepararSQL");
String sql = "show tables"; ConexaoBD instance = conBD; instance.conectar();
try (PreparedStatement result = instance.prepararSQL(sql)) { assertNotNull(result);
}
instance.desconectar(); }
@Test
public void testPrepararSQL_String_boolean() throws SQLException { System.out.println("prepararSQL");
String sql = "show tables"; boolean gerarChaves = false; ConexaoBD instance = conBD; instance.conectar();
try (PreparedStatement result = instance.prepararSQL(sql, gerarChaves)) { assertNotNull(result);
}
instance.desconectar(); }
@Test
public void testGetChave() throws SQLException { System.out.println("getChave");
String sql = "show tables"; boolean gerarChaves = true; ConexaoBD instance = conBD; instance.conectar();
try (PreparedStatement ps = instance.prepararSQL(sql, gerarChaves)) { long expResult = 0L;
long result = instance.getChave(ps); assertEquals(expResult, result); }
instance.desconectar(); }
@Test
public void testGetTabela() throws SQLException { System.out.println("getTabela");
String sql = "show tables"; ConexaoBD instance = conBD; instance.conectar();
try (PreparedStatement ps = instance.prepararSQL(sql); ResultSet rs = ps.executeQuery()) {
DefaultTableModel result = instance.getTabela(rs); assertNotNull(result);
}
instance.desconectar(); }
20. É lógico que alguns desses testes acima precisa melhorar em razão de não refletirem exatamente os cenários reais e não testarem adequadamente as situações que possam ocorrer.
2.3.4. Utilizando um arquivo de configuração (propriedades)
Em vez de incluirmos os dados para o acesso ao banco de dados no código-fonte, mesmo que em uma classe utilitária única, é melhor incluir esses detalhes em uma configuração externa. Assim, é possível que utilizemos um banco de dados enquanto estamos desenvolvendo a aplicação e utilizemos outro banco de dados, com diferentes dados para ser acessado. E, além disso, não precisamos ficar recompilando toda vez que muda algum detalhe, como o driver, URL, usuário ou senha do banco de dados.
1. Acione o comando de menu Arquivo > Novo Arquivo...;
2. Na janela Novo Arquivo, selecione a categoria Outros e o tipo de arquivo Arquivo de Propriedades; 3. Clique no botão Próximo;
4. Informe conexao como o Nome do Arquivo;
5. Selecione a pasta src, usando o botão Procurar (um arquivo nomeado conexao.properties será criado no pacote default do projeto NetBeans);
6. Clique no Botão Finalizar;
7. Um arquivo de propriedades possui sempre um par contendo um identificador (chave) e um valor associado, portanto inclua os dados necessários no seu conteúdo:
jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost/pedido jdbc.usuario = root
jdbc.senha =
8. Agora, precisamos ler o conteúdo do arquivo acima. Para isso crie uma nova classe utilitária Java chamada PropriedadesBD no pacote br.edu.univag.util.sql, removendo o modificador public, para que somente classes do mesmo pacote tenham acesso à esta classe;
9. Adicione os seguintes atributos constantes: public static final String DRIVER; public static final String URL; public static final String USUARIO; public static final String SENHA;
10. Note que os atributos não foram inicializados. É uma boa prática carregarmos apenas uma vez as propriedades sobre os dados da conexão, para evitarmos leituras demasiadas em um arquivo que quase não se altera. Caso o arquivo de propriedades seja alterado, podemos simplesmente reiniciar a aplicação.
11. Na classe PropriedadesBD, adicione um bloco de código estático, após os atributos estáticos, para podermos carregar as constantes apenas uma única vez quando a classe for carregada:
static {
Properties props = new Properties(); try {
props.load(Properties.class.getResourceAsStream("/conexao.properties")); } catch (IOException ex) {
Logger.getLogger(PropriedadesBD.class.getName()).log(Level.SEVERE, null, ex); } DRIVER = props.getProperty("jdbc.driver"); URL = props.getProperty("jdbc.url"); USUARIO = props.getProperty("jdbc.usuario");
}
12. Adicione as importações necessárias (<Ctrl>+<Shift>+<i>);
13. Altere a classe ConexaoBD, inserindo um novo construtor sem parâmetros (default) e carregando os dados através da classe PropriedadesBD:
public ConexaoBD() { this.driver = PropriedadesBD.DRIVER; this.url = PropriedadesBD.URL; this.usuario = PropriedadesBD.USUARIO; this.senha = PropriedadesBD.SENHA; }
14. Desta forma, temos uma maneira mais fácil de manter e atualizar os dados de conexão com o nosso banco de dados.
2.4. Exercícios
1. Por que os atributos driver, url, usuario e senha da classe ConexaoBD foram marcados com o modificador final?
2. Qual a finalidade da instrução con.setAutoCommit(false), encontrada no método conectar()da classe ConexaoBD?
3. Qual a finalidade da instrução con.rollback(), encontrada no método desconectar()da classe ConexaoBD?
4. Implemente o método commit() na classe ConexaoBD.
5. Pesquise sobre como efetuar testes usando o JUnit. Existem muitos artigos e tutoriais nas mais renomadas fontes. Uma rápida pesquisa no Google permite um bom aprofundamento, seguindo os exemplos encontrados.
6. Altere a classe ConexaoBDTest, adicionando um teste para o construtor default da classe ConexaoBD, que utiliza o arquivo de propriedades para obter a conexão com o banco de dados.