4. Consultando Dados

Faremos uma consulta simples no banco de dados. Será consultado todos os clientes da tabela TBCliente usando o componente JTable. Abaixo está listado o programa MostrarTabela.java que descreve a conexão como banco de dados, a consulta ao banco de dados e a exibição dos resultados. A figura 4.1 mostra o resultado da execução deste programa e a em segue está a listagem do programa ExecutarMostrarTabela.java que executa a classe MostrarTabela.java.

1 import java.sql.*; // Acesso a Banco de Dados relacionais
2 import javax.swing.*; // JTable
3 import java.util.*;
4 import java.awt.*;
5
6 public class MostrarTabela extends JFrame {
7 private Connection connection;
8 // pacote java.sql : gerencia a conexão entre o java e o BD
9 // fornece suporte para SQL
10 private JTable table;
11
12 public MostrarTabela()
13 {
14 // The URL specifying the database to which
15 // this program connects using JDBC to connect to a
16 // Microsoft ODBC database.
17 String url = "jdbc:odbc:Empresa";
18 // Definido Painel de Controle; Fontes de dados ODBC (32 bits)
19 // URL - Uniform Resource Locator
20 // protocolo:sub-protocolo:Banco_de_dados
21 String username = "anonimo"; // login e senha pré-definidos
22 String password = "convidado";
23
24 // Load the driver to allow connection to the database
25 try {
26 Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
27 //drive : classe do pacote sun.jdbc.odbc
28 //http://java.sun.com/products/jdbc
29 connection = DriverManager.getConnection(
30 url, username, password );
31 // Método static : tentar uma conexão
32
33 }
34 catch ( ClassNotFoundException cnfex ) { // Classe não encontrada
35 System.err.println(
36 "Falha ao carregar JDBC/ODBC driver." );
37 cnfex.printStackTrace();
38 System.exit( 1 ); // terminate program
39 }
40 catch ( SQLException sqlex ) { // Conexão não estabelecida
41 System.err.println( "Conexao nao efetetivada" );
42 sqlex.printStackTrace();
43 }
44
45 getTable();
46
47 setSize( 450, 150 );
48 show();
49 }
50
51 private void getTable()
52 {
53 Statement statement; // Comando SQL, pacote java.sql
54 ResultSet resultSet; // Resultado da consulta
55
56 try {
57 String query = "SELECT CLNCODG AS Codigo,"+
58 " CLCDESC AS Descricao,CLYREND AS Renda FROM Tbcliente";
59
60 statement = connection.createStatement(); // Obtem o objeto (não há instancia aqui)
61 resultSet = statement.executeQuery( query ); // Executa o comando SQL
62 displayResultSet( resultSet );
63 statement.close();
64 }
65 catch ( SQLException sqlex ) {
66 sqlex.printStackTrace();
67 }
68 }
69
70 private void displayResultSet( ResultSet rs )
71 throws SQLException
72 {
73 // position to first record
74 boolean moreRecords = rs.next(); // Retorna se há registro (Não EOF (End Of File))
75 // É posicionado antes do primeiro registro válido
76
77 // If there are no records, display a message
78 if ( ! moreRecords ) { // Se não há registros
79 JOptionPane.showMessageDialog( this,
80 "ResultSet nao contem registros" );
81 setTitle( "Nenhum registro para exibir" );
82 return;
83 }
84
85 setTitle( "Tabela Clientes da Empresa" );
86
87 Vector columnHeads = new Vector();
88 Vector rows = new Vector(); // Vetor de Vector : Matriz (Campos de cada Registro)
89
90 try {
91 // get column heads
92 ResultSetMetaData rsmd = rs.getMetaData(); // pacote java.sql
93 // Informações do registro
94
95 for ( int i = 1; i <= rsmd.getColumnCount(); ++i ) // Quantidade de Colunas (campos)
96 columnHeads.addElement( rsmd.getColumnName( i ) ); // Nome da coluna
97
98 // get row data
99 do {
100 rows.addElement( getNextRow( rs, rsmd ) ); // Formata a cada registro
101 } while ( rs.next() );
102
103 // display table with ResultSet contents
104 table = new JTable( rows, columnHeads ); // Dados e Nome das colunas
105 JScrollPane scroller = new JScrollPane( table );
106 getContentPane().add(
107 scroller, BorderLayout.CENTER );
108 validate();
109 }
110 catch ( SQLException sqlex ) {
111 sqlex.printStackTrace();
112 }
113 }
114
115 private Vector getNextRow( ResultSet rs,
116 ResultSetMetaData rsmd )
117 throws SQLException
118 {
119 Vector currentRow = new Vector();
120
121 for ( int i = 1; i <= rsmd.getColumnCount(); ++i )
122 switch( rsmd.getColumnType( i ) ) { // Tipo da coluna (campo)
123 case Types.VARCHAR: // pacote java.sql
124 currentRow.addElement( rs.getString( i ) ); // Obtem o dado String
125 break;
126 case Types.INTEGER:
127 currentRow.addElement(
128 new Long( rs.getLong( i ) ) ); // Obtem o dado Long
129 break;
130 default:
131 System.out.println( "Type was: " +
132 rsmd.getColumnTypeName( i ) );
133 }
134
135 return currentRow;
136 }
137
138 public void shutDown()
139 {
140 try {
141 connection.close(); // Fecha a conexão
142 }
143 catch ( SQLException sqlex ) {
144 System.err.println( "Desconexao nao efetivada" );
145 sqlex.printStackTrace();
146 }
147 }
148 }

figura 4.1

1 import java.awt.*;
2 import java.awt.event.*;
3
4 class ExecutarMostrarTabela
5 {
6 public static void main( String args[] )
7 {
8 final MostrarTabela app = new MostrarTabela();
9
10 app.addWindowListener(
11 new WindowAdapter() {
12 public void windowClosing( WindowEvent e )
13 {
14 app.shutDown();
15 System.exit( 0 );
16 }
17 }
18 );
19 }
20 }

Estudando MostrarTabela.java

A linha 1 importa o pacote java.sql que contém as classes e interfaces para manipular os bancos de dados relacionais.

A linha 7 declara uma referência Connection (pacote java.sql) chamada connection. Isso irá referenciar um objeto que implementa a inteface Conection. Um objeto Connection gerencia a conexão entre o programa Java e o Banco de Dados. Também fornece suporte para executar instruções de SQL para fins de manipulação do banco de dados e o processamento de transações.

O construtor para a classe MostrarTabela (linha 12) tenta a conexão como o banco de dados e, se bem-sucedida, consulta o banco de dados e exibe os resultados chamando o métodoo utilitário getTable (definido na linha 51). As linhas 14 a 22

17 String url = "jdbc:odbc:Empresa";
21 String username = "anonimo"; // login e senha pré-definidos
22 String password = "convidado";

especificam o URL do banco de dados que faz com que o programa localize o banco de dados, o nome do usuário para efetuar o logon e a senha para efetuar a conexão com o banco de dados. O URL especifica o protocolo para comunicação (jdbc), o subprotocolo para comunicação (odbc) e o nome do banco de dados (Empresa). O subprotocolo odbc indica que o programa está utilizando jdbc para conectar-se com uma fonte de dados Microsoft ODBC. O J2SDK fornece o driver de banco de dados de fonte JDBC para ODBC a fim de permitir que qualquer programa Java acesse qualquer fonte de dados ODBC. O driver é definido pela classe JdbcOdbcDriver no pacote sun.jdbc.odbc.

A definição de classe parao driver de banco de dados deve ser carregada antes do programa se conectar ao banco de dados. A linha 26

26 Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );

utiliza o método static forName da classe Class (pacote java.lang) para carregar a definição de classe para o driver de banco de dados (essa linha dispara uma java.lang.ClassNotFoundExcepton se a classe não conseguir ser localizada). Observe que a instrução especifica o nome completo do pacote e o nome da classe - jdbc.odbc.JdbcOdbcDriver.

A maioria dos fornecedores de banco de dados importantes fornece seus próprios drivers de banco de dados de JDBC e muitos fornecedores independentes também fornecem drivers JDBC. Para mais informações sobre drivers JDBC e banco de daos suportados visite o site JDBC da Sun Microsystems : http://java.sun.com/products/jdbc/.

As linhas 29 e 30

29 connection = DriverManager.getConnection(
30 url, username, password );

utilizam o método static getConnection da classe DriverManager (pacote java.sql) para tentar uma conexão com o banco de dados especificado pelo url. Os argumentos username e password são passados aqui porque intencionalmente configuramos a fonte de dados para exigir que o usuário efetue login. Nosso banco de dados é configurado com um nome de usuário - anônimo - e uma senha - convidado - para propósitos de demonstração. Se o DriveManager não se conectar ao banco de dados, o método getConnection dispara uma java.sql.SQLException. Se a tentativa de conexão é bem-sucedida, a linha 45 chama o método utilitário getTable (definido na linha 51) para recuperar os dados da tabela TBcliente.

O método utiitário getTable consulta o banco de dados e, a seguir, chama o método utilitário displayResultSet (definido na linha 70) para criar uma JTable (pacote javax.swing) contendo o resultado da consulta. A linha 53 declara uma referência Statement (pacote java.sql) que irá se referir a um objeto que implementa a interface Statement. Esse objeto submeterá a consulta ao banco de dados. A linha 54 declara uma referência ResultSet (pacote java.sql) que irá se referir a um objeto que implementa a interface ResultSet. Quando uma consulta é realizada em um banco de dados, um objeto ResultSet é retornado contendo o resultado da consulta. Os métodos da interface ResultSet permitem ao programador manipular os resultados da consulta.

A linha 57 define a consulta a realizar. Neste exemplo foi selecionada a tabela TBcliente. A linha 60

60 statement = connection.createStatement();

invoca o método createStatement de Connection para obter um objeto que implementa a interface Statement. Agora podemos utilizar statement para consultar o banco de dados.

A linha

61 resultSet = statement.executeQuery( query );

realiza a consulta chamando o método executeQuery de Statement. Esse método retorna um objeto que implementa ResultSet e contém os resultados da consulta. O ResultSet é passado para o método utilitário displayResultSet (definido na linha 70), assim a instrução é fechada na linha 63 para indicar que terminamos o processamento da consulta.

A linha 74 do método displayResultSet

74 boolean moreRecords = rs.next();

posiciona-se no primeiro registro no ResultSet com o método next de ResultSet. Inicialmente, o ResultSet é posicionado antes do promeiro registro, portanto esse método deve ser chamado antes de você conseguir acessar os resultados. O método next retorna um boolean indicando se foi capaz de posicionar no próximo registro. Se o método retornar false, não há mais registros a processar. Se houver registros, a linha 87 define um Vector para armazenar as linhas de dados do ResultSet. Esses Vectors são utilizados com o construtor JTable para construir uma JTable que exibe os dados do ResultSet.

A linha 92

92 ResultSetMetaData rsmd = rs.getMetaData(); // pacote java.sql

obtém os meta dados para o ResultSet e os atribui a uma referência ResultSetMetaData (pacote java.sql). Os meta dados para o ResultSet descrevem o conteúdo de um ResultSet. Essas informações podem ser utilizadas para obter programaticamente informações sobre os nomes e tipos das colunas ResultSet e podem ajudar o programador a processar um ResultSer dinamicamente quando informações detalhadas sobre o ResultSet não são conhecidas antes da consulta. Utilizamos ResultSetMetaData nas linhas 95 e 96 para retornar o número de colunas no ResultSet e o método getColumnName de ResultSetMetaData retorna o nome da coluna especificada.

As linhas 99 a 101

99 do {
100 rows.addElement( getNextRow( rs, rsmd ) ); // Formata a cada registro
101 } while ( rs.next() );

recuperam cada linha do ResultSet itilizando o método utilitário getNextRow (definido na linha 115). O método getNextRow retorna um Vector contendo os dados de uma linha do ResultSet. Repare na condição rs.next(). Isso move o cursor de ResultSet que monitora o registro atual no ResultSet para o próximo registro no ResultSet. Lembre-se que o método next retorna falso quando não há mais registros no ResultSet. Portanto, o laço terminará quando não houver mais registro.

Depois que todas as linhas são convertidas em Vectors, a linha 104 cria o componente GUI JTable que exibe os registros no ResultSet. O construtor que utilizamos nesse programa recebe dosi Vectors como argumentos. O primeiro argumento é um Vector de Vectors (array bidimensional) que contém todos os dados de linhas. O segundo argumento é um Vector contendo os nomes de coluna para cada coluna. O construtor JTable utiliza esses Vectors para preencher a tabela.

O método getNextRow (linha 115) recebe um ResultSet e seu correspondente ResultSetMetaData como argumentos e cria um Vector contendo uma linha de dados do ResultSet. A estrutura for na linha 121 faz um laço por cada coluna do conjunto de resultados e executa a estrutura switch na linha 115 que determina o tipo de dados da coluna. O método getColumnType de ResultSetMetaData retorna uma constante inteira da classe Types (pacote java.sql) indicando o tipo dos dados. Os únicos tipos de dados em nosso banco de dados são strings e inteiros longos. O tipo de SQL para strings é Type.VARCHAR e para nteiros longos é Type.INTEGER. A linha 124 utiliza o método getString de ResultSet para obter oo String de uma coluna do tipo Types.VARCHAR. As linhas 127 e 128 utilizam o método getLong de ResultSet para obter o inteiro longo de uma coluna do tipo Types.INTEGER.

Os métodos shutdown na linha 138 fecha a conexão para o banco de dados com o método close de Connection (linha 141).