Esta é a última parte de uma série que visa apresentar o essencial que qualquer programador Java necessita saber para desenvolver aplicações corretas, performáticas e seguras usando JDBC. A primeira parte foi focada mais no desenvolvedor iniciante, apresentando como estabelecer conexões a um banco de dados relacional, o papel do driver JDBC (e como escolher o melhor driver para um dado banco de dados) e como executar comandos SQL de consulta e atualização de dados. Nesta parte serão apresentados recursos mais avançados, porém essenciais na maioria das aplicações reais – os metadados de consultas e do próprio banco de dados, o tratamento de erros, o gerenciamento de transações e o uso de comandos preparados (PreparedStatements).
Os exemplos, tanto da parte inicial quando desta, são desenvolvidos utilizando o banco de dados HSQLDB (veja referências), que é um banco de dados leve e 100% Java. Esta foi uma opção didática, para que o leitor não tivesse como pré-requisito para acompanhar o artigo instalar e configurar um servidor de banco de dados mais “parrudo”. Entretanto todos os exemplos devem funcionar em qualquer outro banco de dados, desde que o driver JDBC correto seja configurado no classpath do sistema, e as propriedades de conexão sejam modificadas para indicar o banco desejado. Todos os exemplos das duas partes foram testados com vários outros bancos, mas não seria possível detalhar aqui os detalhes de configuração para cada possibilidade. O quadro “O exemplo em vários bancos” apresenta os procedimentos para rodar os exemplos deste artigo nos dois bancos livres mais populares, MySQL e PostgreSQL.
Para seguir este artigo é recomendado ter lido a primeira parte, ou pelo menos baixar e estudar o seu código de exemplo. Estamos utilizando a mesma base de dados de exemplo da primeira parte, portanto não serão repetidas aqui as instruções sobre como criar esta base e inserir dados. Mas os fontes para download fornecem os scripts SQL necessários, de modo que leitores com algum conhecimento prévio de SQL e bancos de dados relacionais não terão dificuldades na criação do ambiente para a execução dos exemplos.
Informações sobre o resultado de uma consulta
Existem situações onde é necessário gerar consultas dinamicamente, não sendo possível antecipar quantas colunas farão parte de um resultado (ResultSet) [1], ou qual o tipo de dados de cada coluna retornada. Para situações como essa, o JDBC fornece o método getMetaData() da interface ResultSet. Este método retorna um objeto que implementa a interface ResultSetMetaData. Esta interface, por sua vez, descreve todas as características de cada coluna do resultado e é graças a ela que programas como o Database Manager do HSQLDB ou o SQL Explorer para o Eclipse (veja referências) conseguem executar um comando SQL qualquer e exibir seus resultados de forma organizada.
A Listagem 1 apresenta um exemplo de uso dos metadados do resultado. É um programa que lê um único registro de uma tabela qualquer, desde que seja fornecido o nome da tabela e o valor da chave primária do registro desejado. Neste exemplo consideramos que a chave primária é formada por uma única coluna chamada “id”. Mas o JDBC permitiria uma versão mais sofisticada do exemplo, que consulta o banco de dados para saber quais as colunas que formam a chave primária da tabela requisitada.
import java.sql.*;
import java.util.*;
public class ListaRegistro
{
public static void main(String[] args) throws Exception {
//... mesmo código dos exemplos da parte 1 para conectar ao banco
Statement comando = conexao.createStatement();
String tabela = args[0];
String id = args[1];
ResultSet resultado = comando.executeQuery(
"select * from " + tabela + " where id = " + id);
if (resultado.next()) {
ResultSetMetaData metaDados = resultado.getMetaData();
for (int i = 1; i <= metaDados.getColumnCount(); i++) {
System.out.println(metaDados.getColumnName(i) + ": "
+ resultado.getString(i));
}
}
else {
System.out.println("Não foi encontrado nenhum registro com o id"
+ id + ".");
}
//... fecha o resultado, comando e conexão
}
}
Não é preciso fechar os objetos de metadados do JDBC, como o ResultSetMetaData. Mas é preciso fechar os resultados retornados por ele.
Com as informações passadas, o programa monta um comando SQL select e então obtém a descrição do resultado, para saber quantas colunas foram retornadas e quais os seus nomes. Eis um exemplo de execução deste programa:
array0
nbsp;javaListaRegistroproduto4
ID:4
NOME:Travesseiroanti-alérgico
PRECO:49.00
CATEGORIA:CamaeBanho
A maioria dos desenvolvedores estaria interessada em uma informação mais básica: a quantidade de linhas retornadas por uma consulta. Mas o JDBC não oferece um método que retorne esta informação. O fato é que a maioria dos bancos de dados não “sabe” a quantidade de linhas em um resultado até que este resultado seja inteiramente percorrido. Entre outros motivos, este comportamento permite aos bancos de dados responderem mais rapidamente, consumindo menos memória e reduzindo o tráfego de rede.
Então, caso sua aplicação precise indicar a quantidade de registros que retornaram em uma consulta, há duas alternativas: a aplicação deve contar os registros à medida que percorre o resultado, e percorrer todas as linhas do resultado; ou então executar um comando SQL select count(*).
Tratamento de erros de banco
Até este ponto, nenhum dos programas de exemplo realizou qualquer tipo de tratamento de erros. Como é usual em exemplos iniciais, estamos operando em um mundo ideal onde tudo funciona perfeitamente. Mas no mundo real, é claro, muitas coisas podem sair erradas. A rede pode estar com problemas, o servidor de banco de dados pode ter ficado sem espaço em disco, ou o programador pode ter errado a sintaxe de um comando SQL ou digitado incorretamente o nome de uma tabela ou campo.
...