Programando de forma simplificado com JDBC – Parte 3

 

Agora na prática

Chega de introdução, chegou a hora de ver a implementação e alguns exemplos. Veremos como o pacote pode ser usado para executar operações básicas select/insert/update/delete e também para formatar e validar dados. A Figura 1 apresenta o diagrama de classes da UML para as classes responsáveis por facilitar o uso do JDBC.

 

img1java.JPG

Figura 1. Diagrama UML das classes básicas.

 

A classe Database faz duas coisas: mantém um mapa de Formatter e implementa métodos para selecionar, inserir, atualizar e apagar linhas. Também há um método validate(), sobre o qual falarei brevemente. Formatter é uma classe abstrata que define três métodos: parse(), format() e validate(). Como mencionado antes, a visão de dados dos usuários está na forma de strings, assim os formatters são responsáveis por converter tipos de banco de dados de/para strings. Sempre é possível a inserção de dados inválidos, por exemplo, caracteres não numéricos em um campo numérico, data formatada incorretamente ou strings que não casam com um padrão. Para cada um dos dados de coluna esperados, existe uma subclasse Formatter.

Vejamos um exemplo simples de seleção de dados na Listagem 1.

 

   Connection con = getConnection();
   myDB = new Database(false);
   myDB.loadDefaultFormatters(con, "roles");
   String[] params = {"roles.id", "99"};
   Results rs = myDB.select(con, "select id,name,role from roles where id = ?", params, 10);
   System.out.println("Name is " + rs.getString(0, 1));
   System.out.println("Password is " + rs.getString(0, "roles.role"));

Listagem 1. Exemplo de seleção de dados.

 

A primeira linha obtém uma conexão com o banco de dados. Assumi que existem meios para obter a conexão, a maioria das aplicações o faz. Normalmente, provém de uma fonte de dados predefinida ou de uma chamada simples a DriverManager. O construtor do banco de dados possui um argumento booleano que diz à instância para fechar automaticamente a conexão depois de uma operação. Como provavelmente você deve saber, fechar conexões é um processo crítico e propenso a erros, portanto isto pode ser automatizado caso assim o prefira. O método loadDefaultFormatters() é um modo prático de carregar o formatter apropriado para uma determinada tabela. Este depende do método getColumnClass() em ResultSetMetaData. Possivelmente, isto poderia não funcionar, mas em minha experiência, este método parece funcionar com todos os bancos de dados que testei, inclusive com o Access, SQL Server, MySQL, Oracle e DB2. Alternativamente, podemos configurar o formatter para colunas específicas por nome. Provavelmente você desejará fazer isto em produção, já que o formatter padrão não possui muitas restrições de validação.

Este exemplo é de um simples select. Perceba que o SQL não é nada além do que você iria prover ao PreparedStatement. Na realidade, no fundo é exatamente isto, permitindo a utilização de qualquer SQL executado pelo seu banco de dados. O argumento params é um array de Strings que contem pares de nome de coluna e valor. A ordem dos parâmetros equivale a um HashMap ordenado, porém mais fácil de criar e inicializar. O nome das colunas é necessário, pois desta maneira a análise gramatical dos valores dos parâmetros poderá ser realizada corretamente.

O método select() retorna um objeto Results que equivale a um ResultSet, mas formata os resultados baseado em nomes de colunas. Se o nome de coluna não estiver disponível, tentará utilizar o tipo da coluna. Valores para uma determinada linha ou coluna podem ser recuperados em qualquer ordem. Perceba que o nome de coluna ou o numero da coluna pode ser especificado. O método select() levantará uma SQLException se algo der errado.

Agora analisaremos um exemplo de inserção na Listagem 2.

 

   String[] values = {
      "roles.id", "99",
      "roles.name", "ahab",
      "roles.role", "captain"
   };
   String[] errors = myDB.validate(values);
   if (errors != null) {
      for (int i=0; i