6. Gerenciamento de bancos de dados com o API JDBC
6.1. Informações gerais
Existem inúmeros bancos de dados no mercado. Para padronizar o acesso às bases de dados no Windows, a Microsoft desenvolveu uma interface chamada ODBC (Open DataBase Connectivity). Essa camada oculta as particularidades de cada base de dados por meio de uma interface padrão. No Windows, existem diversos drivers que facilitam o acesso aos bancos de dados. Aqui está, por exemplo, uma lista de drivers instalados em um computador com Windows 95:

Um aplicativo que utilize esses drivers pode acessar qualquer um dos bancos de dados acima sem a necessidade de reescrever o código.

Para que os aplicativos Java também possam aproveitar a interface ODBC, a Sun criou a interface JDBC (Java DataBase Connectivity), que se intercalará entre o aplicativo Java e a interface ODBC:

6.2. Etapas importantes na operação de bancos de dados
6.2.1. Introdução
Em um aplicativo JAVA que utiliza um banco de dados com a interface JDBC, geralmente são encontradas as seguintes etapas:
- Conexão com o banco de dados
- Envio de consultas SQL ao banco de dados
- Recebimento e processamento dos resultados dessas consultas
- Encerramento da conexão
As etapas 2 e 3 são realizadas repetidamente, sendo que o encerramento da conexão ocorre somente ao final da consulta ao banco de dados. Trata-se de um esquema relativamente clássico, com o qual você talvez já esteja familiarizado caso já tenha consultado um banco de dados de forma interativa. Vamos detalhar cada uma dessas etapas por meio de um exemplo. Consideremos um banco de dados ACCESS denominado ARTICLES e com a seguinte estrutura:
nome | tipo |
code | código do artigo com 4 caracteres |
nom | nome (sequência de caracteres) |
prix | seu preço (real) |
stock_actu | seu estoque atual (número inteiro) |
stock_mini | o estoque mínimo (número inteiro) abaixo do qual é necessário reabastecer o item |
Esta base ACCESS está definida como fonte de dados “usuário” no gerenciador de bases ODBC:


Suas características são especificadas por meio do botão Configurer da seguinte forma:

Essa configuração consiste essencialmente em associar à base ARTICLES o arquivo Access articles.mdb correspondente a essa base. Feito isso, a base ARTICLES fica acessível às aplicações que utilizam a interface ODBC.
6.2.2. A etapa de conexão
Para utilizar um banco de dados, um aplicativo Java deve primeiro realizar uma etapa de conexão. Isso é feito com o seguinte método de classe:
com
DriverManager: classe Java que contém a lista de drivers disponíveis para o aplicativo
Connection: classe Java que estabelece uma conexão entre a aplicação e o banco de dados, por meio da qual a aplicação enviará consultas SQL ao banco de dados e receberá os resultados
URL: nome que identifica o banco de dados. Esse nome é análogo aos URL da Internet. É por isso que faz parte da classe URL. No entanto, a Internet não intervém de forma alguma aqui. O URL tem o seguinte formato:
jdbc:nom_du_pilote:nom_de_la_source;param=val1;param2=val2
Em nossos exemplos, nos quais utilizaremos exclusivamente drivers ODBC, o driver é chamado odbc. A terceira parte do URL é constituída pelo nome da fonte, acompanhado de eventuais parâmetros. Em nossos exemplos, tratar-se-á de fontes ODBC conhecidas pelo sistema. Assim, o URL da fonte de dados Articles definida anteriormente será
jdbc:odbc:Artigos
id: Identidade do usuário (login)
mdp: senha do usuário (password)
Resumindo, o programa se conecta a um banco de dados:
- identificada por um nome (URL)
- sob a identidade de um usuário (id, senha)
Se esses três parâmetros estiverem corretos e se existir o driver capaz de garantir a conexão do aplicativo Java com o banco de dados especificado, então uma conexão é estabelecida entre o aplicativo Java e o banco de dados. Essa conexão é representada para o programa pelo objeto do tipo Connection retornado pela classe DriverManager. Como essa conexão pode falhar por diversos motivos, ela pode gerar uma exceção. Portanto, escreveremos:
Connection connexion=null;
URL base=...;
String id=...;
String mdp=...;
try{
connexion=DriverManager.getConnection(base,id,mdp);
} catch (Exception e){
// tratar a exceção
}
Para que a conexão com um banco de dados seja possível, é necessário dispor do driver adequado. Em nossos exemplos, trata-se do driver ODBC, capaz de gerenciar o banco de dados solicitado. Além de esse driver estar disponível na lista de drivers ODBC presentes na máquina, também é necessário dispor da classe JAVA, que atuará como interface com ele. Para isso, o aplicativo solicitará a classe necessária da seguinte maneira:
A classe Class não está de forma alguma ligada à interface JDBC. Trata-se de uma classe geral de gerenciamento de classes. Seu método estático forName permite carregar dinamicamente uma classe e, assim, aproveitar seus atributos e métodos estáticos. A classe que faz a interface com os drivers ODBC do Windows, MS, chama-se “sun.jdbc.odbc.JdbcOdbcDriver”. Portanto, escreveremos (o método pode gerar uma exceção):
try{
Class.forName(« sun.jdbc.odbc.JdbcOdbcDriver ») ;
} catch (Exception e){
// tratar a exceção (classe inexistente)
}
As classes necessárias para a interface JDBC estão no pacote java.sql. Portanto, no início do programa, escreveremos:
Aqui está um programa que permite a conexão a um banco de dados:
import java.sql.*;
import java.io.*;
// chamada: pg PILOTE URL UID MDP
// conecta-se ao banco de dados URL por meio da classe JDBC PILOTE
// o usuário UID é identificado por uma senha MDP
public class connexion1{
static String syntaxe="pg PILOTE URL UID MDP";
public static void main(String arg[]){
// verificação do número de argumentos
if(arg.length<2 || arg.length>4)
erreur(syntaxe,1);
// conexão com o banco de dados
Connection connect=null;
String uid="";
String mdp="";
if(arg.length>=3) uid=arg[2];
if(arg.length==4) mdp=arg[3];
try{
Class.forName(arg[0]);
connect=DriverManager.getConnection(arg[1],uid,mdp);
System.out.println("Connexion avec la base " + arg[1] + " établie");
} catch (Exception e){
erreur("Erreur " + e,2);
}
// encerramento do banco de dados
try{
connect.close();
System.out.println("Base " + arg[1] + " fermée");
} catch (Exception e){}
}// main
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// classe
Aqui está um exemplo de execução:
E:\data\java\jdbc\0>java connexion1 sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles
Connexion avec la base jdbc:odbc:articles établie
Base jdbc:odbc:articles fermée
6.2.3. Envio de consultas ao banco de dados
A interface JDBC permite o envio de consultas SQL para o banco de dados conectado ao aplicativo Java, bem como o processamento dos resultados dessas consultas. A linguagem SQL (Structured Query Language) é uma linguagem de consultas padronizada para bancos de dados relacionais. Nela, distinguem-se vários tipos de consultas:
- consultas de pesquisa no banco de dados (SELECT)
- as consultas de atualização do banco de dados (INSERT, DELETE, UPDATE)
- as consultas de criação/eliminação de tabelas (CREATE, DELETE)
Supõe-se aqui que o leitor conheça os fundamentos da linguagem SQL.
6.2.3.1. A classe Statement
Para enviar uma consulta SQL, seja ela qual for, a um banco de dados, o aplicativo Java deve dispor de um objeto do tipo Statement. Esse objeto armazenará, entre outras coisas, o texto da consulta. Esse objeto está necessariamente vinculado à conexão atual. Portanto, é um método da conexão estabelecida que permite criar os objetos Statement necessários para a emissão das consultas SQL. Se connexion é o objeto que representa a conexão com o banco de dados, um objeto Statement é obtido da seguinte maneira:
Uma vez obtido um objeto Statement, é possível emitir consultas SQL. Isso será feito de maneira diferente, dependendo se a consulta é de consulta ou de atualização do banco de dados.
6.2.3.2. Enviar uma consulta de consulta ao banco de dados
Uma consulta é, normalmente, uma consulta do tipo:
Apenas as palavras-chave da primeira linha são obrigatórias; as demais são opcionais. Existem outras palavras-chave que não foram apresentadas aqui.
- É realizada uma junção com todas as tabelas que aparecem após a palavra-chave `from`
- Somente as colunas que aparecem após a palavra-chave `select` são mantidas
- Somente as linhas que atendem à condição da palavra-chave `where` são mantidas
- As linhas resultantes, ordenadas de acordo com a expressão da palavra-chave `order by`, formam o resultado da consulta.
O resultado de um select é uma tabela. Se considerarmos a tabela ARTICLES anterior e quisermos os nomes dos artigos cujo estoque atual está abaixo do limite mínimo, escreveremos: select nome from artigos where stock_actu<stock_mini. Se quisermos listá-los em ordem alfabética pelos nomes, escreveremos: select nome from artigos where stock_actu<stock_mini order by nome
Para executar esse tipo de consulta, a classe Statement oferece o método executeQuery:
onde requête é o texto da consulta SELECT a ser emitida.
Assim, se
- connexion for o objeto que representa a conexão com o banco de dados
- Statement s=connexion.createStatement() cria o objeto Statement necessário para a emissão das consultas SQL
- ResultSet rs=s.executeQuery(“ select nome from artigos where stock_actu<stock_mini”) executa uma consulta select e atribui as linhas de resultado da consulta a um objeto do tipo ResultSet.
6.2.3.3. A classe ResultSet: resultado de uma consulta SELECT
Um objeto do tipo ResultSet representa uma tabela, ou seja, um conjunto de linhas e colunas. A qualquer momento, só se tem acesso a uma linha da tabela, chamada de linha atual. Na criação inicial do ResultSet, a linha atual é a linha nº 1, caso o ResultSet não esteja vazio. Para passar para a linha seguinte, a classe ResultSet dispõe do método next:
Esse método tenta avançar para a próxima linha do ResultSet e retorna true se for bem-sucedido; caso contrário, retorna false. Se for bem-sucedido, a próxima linha passa a ser a nova linha atual. A linha anterior é perdida e não será possível voltar atrás para recuperá-la. A tabela do ResultSet possui colunas col1, col2,.... Para explorar os diferentes campos da linha atual, estão disponíveis os seguintes métodos:
para obter o campo “coli” da linha atual. Type designa o tipo do campo coli. É bastante comum utilizar o método getString em todos os campos, o que permite obter o conteúdo do campo como uma sequência de caracteres. Em seguida, realiza-se a conversão, se necessário. Caso não se conheça o nome da coluna, é possível utilizar os métodos
onde i é o índice da coluna desejada (i>=1).
6.2.3.4. Um primeiro exemplo
Aqui está um programa que exibe o conteúdo do banco de dados ARTICLES criado anteriormente:
import java.sql.*;
import java.io.*;
// exibe o conteúdo de um banco de dados do sistema ARTICLES
public class articles1{
static final String DB="ARTICLES"; // banco de dados a ser utilizado
public static void main(String arg[]){
Connection connect=null; // conexão com o banco de dados
Statement S=null; // objeto de emissão das consultas
ResultSet RS=null; // tabela de resultados de uma consulta
try{
// conexão com o banco de dados
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// criação de um objeto Statement
S=connect.createStatement();
// execução de uma consulta SELECT
RS=S.executeQuery("select * from " + DB);
// análise da tabela de resultados
while(RS.next()){ // enquanto houver uma linha a ser processada
// ela é exibida na tela
System.out.println(RS.getString("code")+","+
RS.getString("nom")+","+
RS.getString("prix")+","+
RS.getString("stock_actu")+","+
RS.getString("stock_mini"));
}// linha seguinte
} catch (Exception e){
erreur("Erreur " + e,2);
}
// fechamento do banco de dados
try{
connect.close();
System.out.println("Base " + DB + " fermée");
} catch (Exception e){}
}// mão
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// classe
Os resultados obtidos são os seguintes:
Connexion avec la base ARTICLES établie
a300,vélo,1202,30,2
d600,arc,5000,10,2
d800,canoé,1502,12,6
x123,fusil,3000,10,2
s345,skis nautiques,1800,3,2
f450,essai3,3,3,3
f807,cachalot,200000,0,0
z400,léopard,500000,1,1
g457,panthère,800000,1,1
Base ARTICLES fermée
6.2.3.5. A classe ResultSetMetadata
No exemplo anterior, conhecemos os nomes das colunas do ResultSet. Se não os conhecermos, não podemos usar o método getType(nom_colonne). Em vez disso, utilizaremos getType(n.º da coluna). No entanto, para obter todas as colunas, precisaríamos saber quantas colunas possui o ResultSet obtido. A classe ResultSet não nos fornece essa informação. É a classe ResultSetMetaData que nos fornece essa informação. De maneira mais geral, essa classe nos fornece informações sobre a estrutura da tabela, ou seja, sobre a natureza de suas colunas.
É possível acessar as informações sobre a estrutura de um ResultSet instanciando, primeiro, um objeto ResultSetMetaData. Se RS for um ResultSet, o ResultSetMetaData associado é obtido por:
Destacam-se dois métodos úteis na classe ResultSetMetaData:
- int getColumnCount(), que retorna o número de colunas do ResultSet
- String getColumnLabel(int i), que retorna o nome da coluna i do ResultSet (i >= 1)
6.2.3.6. Um segundo exemplo
O programa anterior exibia o conteúdo do banco de dados ARTICLES. Aqui, escrevemos um programa que executa no banco de dados ARTICLES qualquer consulta SQL Select que o usuário digite no teclado.
import java.sql.*;
import java.io.*;
// exibe o conteúdo de um banco de dados do sistema ARTICLES
public class sql1{
static final String DB="ARTICLES"; // banco de dados a ser explorado
public static void main(String arg[]){
Connection connect=null; // conexão com o banco de dados
Statement S=null; // objeto de emissão das consultas
ResultSet RS=null; // tabela de resultados de uma consulta
String select; // texto da consulta SQL select
int nbColonnes; // número de colunas do ResultSet
// criação de um fluxo de entrada do teclado
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// conexão ao banco de dados
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// criação de um objeto Statement
S=connect.createStatement();
// ciclo de execução das consultas SQL digitadas no teclado
System.out.print("Requête : ");
select=in.readLine();
while(!select.equals("fin")){
// execução da consulta
RS=S.executeQuery(select);
// número de colunas
nbColonnes=RS.getMetaData().getColumnCount();
// análise da tabela de resultados
System.out.println("Résultats obtenus\n\n");
while(RS.next()){ // enquanto houver uma linha a ser processada
// ela é exibida na tela
for(int i=1;i<nbColonnes;i++)
System.out.print(RS.getString(i)+",");
System.out.println(RS.getString(nbColonnes));
}// linha seguinte
// próxima consulta
System.out.print("Requête : ");
select=in.readLine();
}// enquanto
} catch (Exception e){
erreur("Erreur " + e,2);
}
// fechamento da base de dados e do fluxo de entrada
try{
connect.close();
System.out.println("Base " + DB + " fermée");
in.close();
} catch (Exception e){}
}// main
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// classe
Aqui estão alguns resultados obtidos:
Connexion avec la base ARTICLES établie
Requête : select * from articles order by prix desc
Résultats obtenus
g457,panthère,800000,1,1
z400,léopard,500000,1,1
f807,cachalot,200000,0,0
d600,arc,5000,10,2
x123,fusil,3000,10,2
s345,skis nautiques,1800,3,2
d800,canoé,1502,12,6
a300,vélo,1202,30,2
f450,essai3,3,3,3
Requête : select nom, prix from articles where prix >10000 order by prix desc
Résultats obtenus
panthère,800000
léopard,500000
cachalot,200000
6.2.3.7. Enviar uma solicitação de atualização do banco de dados
Um objeto do tipo Statement permite armazenar as solicitações SQL. O método que esse objeto utiliza para emitir solicitações SQL de atualização (INSERT, UPDATE, DELETE) não é mais o método executeQuery analisado anteriormente, mas sim o método executeUpdate:
A diferença está no resultado: enquanto executeQuery retornava a tabela de resultados (ResultSet), executeUpdate retorna o número de linhas afetadas pela operação de atualização.
6.2.3.8. Um terceiro exemplo
Retomamos o programa anterior e o modificamos ligeiramente: as consultas digitadas no teclado agora são consultas de atualização do banco de dados ARTICLES.
import java.sql.*;
import java.io.*;
// exibe o conteúdo de um banco de dados do sistema ARTICLES
public class sql2{
static final String DB="ARTICLES"; // banco de dados a ser explorado
public static void main(String arg[]){
Connection connect=null; // conexão com o banco de dados
Statement S=null; // objeto de emissão das consultas
ResultSet RS=null; // tabela de resultados de uma consulta
String sqlUpdate; // texto da consulta SQL de atualização
int nbLignes; // número de linhas afetadas por uma atualização
// criação de um fluxo de entrada do teclado
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// conexão com o banco de dados
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// criação de um objeto Statement
S=connect.createStatement();
// ciclo de execução das consultas SQL digitadas no teclado
System.out.print("Requête : ");
sqlUpdate=in.readLine();
while(!sqlUpdate.equals("fin")){
// execução da consulta
nbLignes=S.executeUpdate(sqlUpdate);
// acompanhamento
System.out.println(nbLignes + " ligne(s) ont été mises à jour");
// próxima consulta
System.out.print("Requête : ");
sqlUpdate=in.readLine();
}// while
} catch (Exception e){
erreur("Erreur " + e,2);
}
// fechamento do banco de dados e do fluxo de entrada
try{
// os recursos associados ao banco de dados são liberados
RS.close();
S.close();
connect.close();
System.out.println("Base " + DB + " fermée");
// fechamento do fluxo do teclado
in.close();
} catch (Exception e){}
}// mão
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// classe
Aqui está o resultado de várias execuções dos programas sql1 e sql2:
Lista de linhas do banco de dados ARTICLES:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,30
arc,10
canoé,12
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
Alteramos algumas linhas:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : update articles set stock_actu=stock_actu+1 where stock_actu>10
2 ligne(s) ont été mises à jour
Verificação:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
canoé,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
Adicionamos uma linha:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',200,20,10)
1 ligne(s) ont été mises à jour
Verificação:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requ_te : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
canoé,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
nouveau,20
Excluímos uma linha:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : delete from articles where code='x400'
1 ligne(s) ont été mises à jour
Requête : fin
Verificação:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
cano_,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
6.2.3.9. Enviar uma solicitação SQL qualquer
O objeto Statement, necessário para a emissão de solicitações SQL, possui um método execute capaz de executar qualquer tipo de solicitação SQL:
O resultado retornado é o valor booleano true se a consulta retornou um ResultSet (executeQuery), false se ela retornou um número (executeUpdate). O valor ResultSet obtido pode ser recuperado com o método getResultSet e o número de linhas atualizadas, pelo método getUpdateCount. Assim, escreveremos:
Statement S=...;
ResultSet RS=...;
int nbLignes;
String requête=...;
// execução de uma consulta SQL
if (S.execute(requête)){
// temos um conjunto de resultados
RS=S.getResultSet();
// análise do ResultSet
...
} else {
// era uma consulta de atualização
nbLignes=S.getUpdateCount();
...
}
6.2.3.10. Quarto exemplo
Retomamos a lógica dos programas sql1 e sql2 em um programa sql3, agora capaz de executar qualquer consulta SQL digitada no teclado. Para tornar o programa mais genérico, as características do banco de dados a ser utilizado são passadas como parâmetros para o programa.
import java.sql.*;
import java.io.*;
// chamada: pg PILOTE URL UID MDP
// conecta-se ao banco de dados URL por meio da classe JDBC PILOTE
// o usuário UID é identificado por uma senha MDP
public class sql3{
static String syntaxe="pg PILOTE URL UID MDP";
public static void main(String arg[]){
// verificação do número de argumentos
if(arg.length<2 || arg.length>4)
erreur(syntaxe,1);
// inicialização dos parâmetros de conexão
Connection connect=null;
String uid="";
String mdp="";
if(arg.length>=3) uid=arg[2];
if(arg.length==4) mdp=arg[3];
// outros dados
Statement S=null; // objeto de envio das consultas
ResultSet RS=null; // tabela de resultados de uma consulta
String sqlText; // texto da consulta SQL a ser executada
int nbLignes; // número de linhas afetadas por uma atualização
int nbColonnes; // número de colunas de um ResultSet
// criação de um fluxo de entrada do teclado
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// conexão com o banco de dados
Class.forName(arg[0]);
connect=DriverManager.getConnection(arg[1],uid,mdp);
System.out.println("Connexion avec la base " + arg[1] + " établie");
// criação de um objeto Statement
S=connect.createStatement();
// ciclo de execução das consultas SQL digitadas no teclado
System.out.print("Requête : ");
sqlText=in.readLine();
while(!sqlText.equals("fin")){
// execução da consulta
try{
if(S.execute(sqlText)){
// obtivemos um ResultSet — vamos analisá-lo
RS=S.getResultSet();
// número de colunas
nbColonnes=RS.getMetaData().getColumnCount();
// análise da tabela de resultados
System.out.println("\nRésultats obtenus\n-----------------\n");
while(RS.next()){ // enquanto houver uma linha a ser processada
// é exibido na tela
for(int i=1;i<nbColonnes;i++)
System.out.print(RS.getString(i)+",");
System.out.println(RS.getString(nbColonnes));
}// linha seguinte do ResultSet
} else {
// essa era uma solicitação de atualização
nbLignes=S.getUpdateCount();
// acompanhamento
System.out.println(nbLignes + " ligne(s) ont été mises à jour");
}//if
} catch (Exception e){
System.out.println("Erreur " +e);
}
// próxima solicitação
System.out.print("\nNouvelle Requête : ");
sqlText=in.readLine();
}// while
} catch (Exception e){
erreur("Erreur " + e,2);
}
// fechamento do banco de dados e do fluxo de entrada
try{
// os recursos associados à base são liberados
RS.close();
S.close();
connect.close();
System.out.println("Base " + arg[1] + " fermée");
// fechamento do fluxo do teclado
in.close();
} catch (Exception e){}
}// main
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// classe
Cria-se o seguinte arquivo de consultas:
select * from articles
update articles set stock_mini=stock_mini+5 where stock_mini<5
select nom,stock_mini from articles
insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',100,20,10)
select * from articles
delete from articles where code='x400'
select * from articles
fin
O programa é executado da seguinte maneira:
O programa, portanto, lê suas entradas do arquivo requetes e grava suas saídas no arquivo results. Os resultados obtidos são os seguintes:
Connexion avec la base jdbc:odbc:articles établie
Requête : (requete 1 du fichier des requetes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,3
d600,arc,5000,10,3
d800,canoé,1502,13,7
x123,fusil,3000,10,3
s345,skis nautiques,1800,3,3
f450,essai3,3,3,4
f807,cachalot,200000,0,1
z400,léopard,500000,1,2
g457,panthère,800000,1,2
Nouvelle Requête : (requete 2 du fichier des requetes : update articles set stock_mini=stock_mini+5 where stock_mini<5)
8 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 3 du fichier des requetes : select nom,stock_mini from articles)
Résultats obtenus
-----------------
vélo,8
arc,8
canoé,7
fusil,8
skis nautiques,8
essai3,9
cachalot,6
léopard,7
panthère,7
Nouvelle Requête : (requete 4 du fichier des requetes : insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',100,20,10))
1 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 5 du fichier des requetes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,8
d600,arc,5000,10,8
d800,canoé,1502,13,7
x123,fusil,3000,10,8
s345,skis nautiques,1800,3,8
f450,essai3,3,3,9
f807,cachalot,200000,0,6
z400,léopard,500000,1,7
g457,panthère,800000,1,7
x400,nouveau,100,20,10
Nouvelle Requête : (requete 6 du fichier des requêtes : delete from articles where code='x400')
1 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 7 du fichier des requêtes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,8
d600,arc,5000,10,8
d800,canoé,1502,13,7
x123,fusil,3000,10,8
s345,skis nautiques,1800,3,8
f450,essai3,3,3,9
f807,cachalot,200000,0,6
z400,léopard,500000,1,7
g457,panthère,800000,1,7
6.3. IMPOTS com um banco de dados
Da última vez que abordamos o problema do cálculo de impostos, foi por meio de uma interface gráfica e os dados estavam armazenados em um arquivo. Retomamos essa versão, partindo agora do pressuposto de que os dados estão em um banco de dados ODBC-MySQL. O MySQL é um SGBD de domínio público que pode ser utilizado em diversas plataformas, incluindo Windows e Linux. Com este SGBD, foi criado um banco de dados dbimpots contendo uma única tabela chamada impots. O acesso ao banco de dados é controlado por um login/senha, neste caso admimpots/mdpimpots. A captura de tela mostra como utilizar o banco de dados dbimpots com o MySQL:
C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use dbimpots;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots |
+--------------------+
1 row in set (0.00 sec)
mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES | | NULL | |
| coeffR | double | YES | | NULL | |
| coeffN | double | YES | | NULL | |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 12620 | 0 | 0 |
| 13190 | 0.05 | 631 |
| 15640 | 0.1 | 1290.5 |
| 24740 | 0.15 | 2072.5 |
| 31810 | 0.2 | 3309.5 |
| 39970 | 0.25 | 4900 |
| 48360 | 0.3 | 6898 |
| 55790 | 0.35 | 9316.5 |
| 92970 | 0.4 | 12106 |
| 127860 | 0.45 | 16754 |
| 151250 | 0.5 | 23147.5 |
| 172040 | 0.55 | 30710 |
| 195000 | 0.6 | 39312 |
| 0 | 0.65 | 49062 |
+---------+--------+---------+
14 rows in set (0.00 sec)
mysql>quit
A interface gráfica do aplicativo é a seguinte:

A interface gráfica sofreu algumas alterações:
n.º | tipo | nome | função |
1 | JTextField | txtConnexion | cadeia de conexão ao banco de dados ODBC |
2 | JScrollPane | JScrollPane1 | contêiner para a área de texto 3 |
3 | JTextArea | txtStatus | exibe mensagens de status, incluindo mensagens de erro |
A sequência de conexão digitada em (1) tem o seguinte formato: DSN;login;senha, com
o nome DSN da fonte de dados ODBC | |
| identidade de um usuário com direito de leitura na base de dados |
| sua senha |
O banco de dados dbimpots foi criado manualmente com o MySQL. Ele é transformado na fonte de dados ODBC da seguinte maneira:
- inicia-se o gerenciador de fontes de dados ODBC de 32 bits

- utiliza-se o botão [Add] para adicionar uma nova fonte de dados ODBC

- seleciona-se o driver MySQL e executa-se [Terminer]

- o driver MySQL solicita algumas informações:
1 | o nome DSN a ser atribuído à fonte de dados ODBC — pode ser qualquer um |
2 | a máquina na qual o SGBD MySQL é executado — neste caso, localhost. Vale ressaltar que o banco de dados pode ser remoto. Os aplicativos locais que utilizam a fonte de dados ODBC não perceberiam isso. Esse seria o caso, notadamente, do nosso aplicativo Java. |
3 | o banco de dados MySQL a ser utilizado. MySQL é um SGBD que gerencia bancos de dados relacionais, que são conjuntos de tabelas interligadas por relações. Aqui, fornece-se o nome do banco de dados gerenciado. |
4 | o nome de um usuário com direito de acesso a essa base de dados |
5 | sua senha |
Depois de definida a fonte de dados ODBC, podemos testar nosso programa:
![]() | ![]() |
Vamos examinar o código que, em comparação com a versão gráfica sem banco de dados, foi modificado. Recorde-se o código da classe impots utilizada até agora:
// criação de uma classe de impostos
public class impots{
// os dados necessários para o cálculo do imposto
// provêm de uma fonte externa
private double[] limites, coeffR, coeffN;
// construtor
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
// verifica-se se as três tabelas têm o mesmo tamanho
boolean OK=LIMITES.length==COEFFR.length && LIMITES.length==COEFFN.length;
if (! OK) throw new Exception ("Les 3 tableaux fournis n'ont pas la même taille("+
LIMITES.length+","+COEFFR.length+","+COEFFN.length+")");
// tudo certo
this.limites=LIMITES;
this.coeffR=COEFFR;
this.coeffN=COEFFN;
}//fabricante
// cálculo do imposto
public long calculer(boolean marié, int nbEnfants, int salaire){
// cálculo do número de cotas
double nbParts;
if (marié) nbParts=(double)nbEnfants/2+2;
else nbParts=(double)nbEnfants/2+1;
if (nbEnfants>=3) nbParts+=0.5;
// cálculo do rendimento tributável e do quociente familiar
double revenu=0.72*salaire;
double QF=revenu/nbParts;
// cálculo do imposto
limites[limites.length-1]=QF+1;
int i=0;
while(QF>limites[i]) i++;
// retorno do resultado
return (long)(revenu*coeffR[i]-nbParts*coeffN[i]);
}//calcular
}//classe
Essa classe cria as três tabelas limites, coeffR e coeffN a partir de três tabelas passadas como parâmetros ao seu construtor. Decide-se adicionar a ela um novo construtor que permita criar as mesmas três tabelas a partir de um banco de dados:
public impots(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: nome DSN do banco de dados
// userIMPOTS, mdpIMPOTS: login/senha de acesso ao banco de dados
Para este exemplo, decidimos não implementar esse novo construtor na classe impots, mas sim em uma classe derivada, a impotsJDBC:
// pacotes importados
import java.sql.*;
import java.util.*;
public class impotsJDBC extends impots{
// adição de um construtor que permite construir
// as tabelas limites, coeffr e coeffn a partir da tabela
// impostos de um banco de dados
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: nome DSN do banco de dados
// userIMPOTS, mdpIMPOTS: login/senha de acesso ao banco de dados
// as tabelas de dados
ArrayList aLimites=new ArrayList();
ArrayList aCoeffR=new ArrayList();
ArrayList aCoeffN=new ArrayList();
// conexão com o banco de dados
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection connect=DriverManager.getConnection("jdbc:odbc:"+dsnIMPOTS,userIMPOTS,mdpIMPOTS);
// criação de um objeto Statement
Statement S=connect.createStatement();
// consulta SELECT
String select="select limites, coeffr, coeffn from impots";
// execução da consulta
ResultSet RS=S.executeQuery(select);
while(RS.next()){
// análise da linha atual
aLimites.add(RS.getString("limites"));
aCoeffR.add(RS.getString("coeffr"));
aCoeffN.add(RS.getString("coeffn"));
}// linha seguinte
// fechamento de recursos
RS.close();
S.close();
connect.close();
// transferência de dados para tabelas delimitadas
int n=aLimites.size();
limites=new double[n];
coeffR=new double[n];
coeffN=new double[n];
for(int i=0;i<n;i++){
limites[i]=Double.parseDouble((String)aLimites.get(i));
coeffR[i]=Double.parseDouble((String)aCoeffR.get(i));
coeffN[i]=Double.parseDouble((String)aCoeffN.get(i));
}//for
}//construtor
}//classe
O construtor lê o conteúdo da tabela impots do banco de dados que lhe foi passado como parâmetro e preenche as três tabelas limites, coeffR e coeffN. Podem ocorrer alguns erros. O construtor não os trata, mas os “repassa” para o programa chamador:
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
Se prestarmos atenção ao código anterior, veremos que a classe impotsJDBC utiliza diretamente os campos limites, coeffR, coeffN de sua classe base impots. Esses campos estão declarados como privados:
a classe impotsJDBC não tem acesso direto a esses campos. Portanto, fazemos uma primeira modificação na classe base, escrevendo:
O atributo protected permite que as classes derivadas da classe impots tenham acesso direto aos campos declarados com esse atributo. Precisamos fazer uma segunda modificação. O construtor da classe filha impotsJDBC é declarado da seguinte forma:
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
Sabemos que, antes de instanciar um objeto de uma classe filha, é preciso primeiro instanciar um objeto da classe pai. Para isso, o construtor da classe filha deve chamar explicitamente o construtor da classe pai com uma instrução super(....). Aqui isso não foi feito, pois não fica claro qual construtor da classe pai poderia ser chamado. Por enquanto, existe apenas um, e ele não é adequado. O compilador, então, procura na classe pai um construtor sem parâmetros que possa ser chamado. Ele não encontra nenhum, e isso gera um erro na compilação. Portanto, adicionamos um construtor sem argumentos à nossa classe impots:
Nós o declaramos como “protected” para que ele só possa ser usado por classes filhas. A estrutura da classe impots ficou agora da seguinte forma:
public class impots{
// os dados necessários para o cálculo do imposto
// provêm de uma fonte externa
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// fabricante em branco
protected impots(){}
// fabricante
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
...........
}//fabricante
// cálculo do imposto
public long calculer(boolean marié, int nbEnfants, int salaire){
.............
}//calcular
}//classe
A ação do menu Initialiser do nosso aplicativo passa a ser a seguinte:
void mnuInitialiser_actionPerformed(ActionEvent e) {
// recuperamos a string de conexão
Pattern séparateur=Pattern.compile("\\s*;\\s*");
String[] champs=séparateur.split(txtConnexion.getText().trim());
// são necessários três campos
if(champs.length!=3){
// erro
txtStatus.setText("Chaîne de connexion (DSN;uid;mdp) incorrecte");
// retorno à interface gráfica
txtConnexion.requestFocus();
return;
}//if
// carregando os dados
try{
// criação do objeto impotsJDBC
objImpots=new impotsJDBC(champs[0],champs[1],champs[2]);
// confirmação
txtStatus.setText("Données chargées");
// o salário pode ser alterado
txtSalaire.setEditable(true);
// não é mais possível fazer alterações
mnuInitialiser.setEnabled(false);
txtConnexion.setEditable(false);
}catch(Exception ex){
// problema
txtStatus.setText("Erreur : " + ex.getMessage());
// fim
return;
}//interceptação
}
Após a criação do objeto objImpots, a aplicação é idêntica à aplicação gráfica já desenvolvida. Recomenda-se ao leitor que consulte essa aplicação.
6.4. Exercícios
6.4.1. Exercício 1
Crie uma interface gráfica para o programa sql3 anterior.
6.4.2. Exercício 2
Um applet Java só pode acessar um banco de dados por meio do servidor a partir do qual foi carregado. De fato, como um applet não tem acesso ao disco da máquina na qual está sendo executado, o banco de dados não pode estar na máquina do cliente que está utilizando o applet. Portanto, estamos na seguinte situação:

A máquina que executa o applet, o servidor e a máquina que hospeda o banco de dados podem ser três máquinas diferentes. Supõe-se aqui que o banco de dados esteja no servidor.
Problema 1
Escreva em Java o seguinte aplicativo de servidor:
- a aplicação de servidor opera em uma porta que lhe é passada como parâmetro
- quando um cliente se conecta, a aplicação de servidor envia a mensagem
- o cliente então envia os parâmetros necessários para a conexão com um banco de dados e a consulta que deseja executar no formato:
Os parâmetros são separados por uma barra. Os quatro primeiros parâmetros são os do programa sql3 descrito neste capítulo.
- O aplicativo servidor cria então uma conexão com o banco de dados especificado, que deve estar na mesma máquina que ele, e executa a consulta SQL nele. Os resultados são retornados ao cliente no formato:
...
se for o resultado de uma consulta Select ou
para retornar o número de linhas afetadas por uma consulta de atualização. Se ocorrer um erro de conexão com o banco de dados ou de execução da consulta, o aplicativo retorna
- assim que a consulta for executada, o aplicativo servidor encerra a conexão.
Problema 2
Crie um applet Java que permita consultar o servidor anterior. É possível se inspirar na interface gráfica do exercício 1 anterior. Como a porta do servidor pode variar, ela deverá ser inserida na interface do applet. O mesmo se aplica a todos os parâmetros necessários para o envio da linha:
que o cliente deve enviar ao servidor.
6.4.3. Exercício 3
O texto a seguir apresenta um problema inicialmente destinado a ser resolvido em Visual Basic. Adapte-o para ser resolvido em Java em um applet. Este applet utilizará o servidor do exercício 2. A interface gráfica poderá ser modificada para levar em conta o novo contexto de execução.
Propõe-se a criação de um aplicativo que destaque as diferentes operações de atualização possíveis de uma tabela do banco de dados ACCESS. O banco de dados ACCESS é denominado articles.mdb. Ele possui uma única tabela chamada “artigos”, que lista os artigos vendidos por uma empresa. Sua estrutura é a seguinte:
nome | tipo |
code | código do artigo com 4 caracteres |
nom | seu nome (sequência de caracteres) |
prix | seu preço (real) |
stock_actu | seu estoque atual (número inteiro) |
stock_mini | o estoque mínimo (número inteiro) abaixo do qual é necessário reabastecer o item |
Propomos visualizar e atualizar essa tabela a partir do seguinte formulário:

Os campos de verificação deste formulário são os seguintes:
n.º | tipo | nome | Função |
1 | caixa de texto | ficha | número do registro exibido o atributo “enabled” está definido como “false” |
2 | caixa de texto | código | código do artigo |
3 | campo de texto | nome | nome do artigo |
4 | campo de texto | preço | preço do artigo |
5 | caixa de texto | atual | estoque atual do artigo |
6 | caixa de texto | mínimo | estoque mínimo do item |
7 | data | data1 | Controle de dados associado ao banco de dados databasename=caminho do arquivo articles.mdb recordsource=artigos connect=access |
8 | HScrollBar | posição | permite navegar pela tabela |
9 | botão | OK | permite confirmar uma atualização — só aparece durante a atualização |
10 | botão | Cancelar | permite cancelar uma atualização — só aparece durante a atualização |
11 | frame | frame1 | para dar um toque de beleza |
12 | caixa de texto | basename | nome do banco de dados aberto enabled está definido como false |
13 | caixa de texto | sourcename | nome da tabela aberta enabled está definido como false |
verificação | Particularidades |
data1 | Os campos databasename e recordsource estão preenchidos. O campo databasename deve indicar o banco de dados Access articles.mdb do seu diretório e o campo recordsource, a tabela de artigos. |
code | Trata-se de uma caixa de texto que queremos vincular ao campo “code” do registro atual de “data1”. Para isso, preenchemos dois campos: datasource: insira “data1” para indicar que a caixa de texto está vinculada à tabela associada a “data1” datafield: selecione o campo “código” da tabela “artigos” Após essas operações, a caixa de texto “código” conterá sempre o campo “código” do registro atual de data1. Por outro lado, alterar o conteúdo dessa caixa de texto modificará o campo “código” do registro atual. Faz-se o mesmo para as demais caixas de texto |
nom | fonte de dados: data1 campo de dados: nome |
prix | fonte de dados: data1 campo de dados: preço |
actuel | fonte de dados: data1 campo de dados: stock_actu |
minimum | fonte de dados: data1 campo de dados: stock_mini |
A estrutura dos menus é a seguinte
Edição | Navegar | Sair |
Adicionar | Anterior | |
Editar | Próximo | |
Excluir | Primeiro | |
Último |
A função das diferentes opções é a seguinte:
menu | nome | função |
mnuadicionar | para adicionar um novo registro à tabela de itens | |
mnusupprimer | para excluir da tabela de artigos o registro atualmente visualizado | |
mnumodificar | para modificar, na tabela de artigos, o registro atualmente visualizado | |
mnuprecedent | para passar para o registro anterior | |
mnusuivant | para passar para o registro seguinte | |
mnupremier | para ir para o primeiro registro | |
mnudernier | para ir para o último registro | |
mnuquitter | para sair do aplicativo |
Durante o evento form_load, a tabela associada a data1 é aberta (data1.refresh). Se a abertura falhar, uma mensagem de erro é exibida e o programa é encerrado (end). Caso contrário, o formulário é exibido com o primeiro registro da tabela articles visível. Não é possível inserir dados nos campos (propriedade enabled definida como false). A inserção de dados só é possível por meio das opções “Adicionar” e “Modificar”. Os botões OK e “Cancelar” estão ocultos (visible=false).
Propomos construir os procedimentos relacionados às diferentes opções do menu, bem como aos botões OK e Cancelar. Inicialmente, ignoraremos os seguintes pontos:
- a ativação/desativação de certas opções do menu: por exemplo, a opção “Próximo” deve ser desativada se estivermos no último registro da tabela
- o gerenciamento da barra de rolagem horizontal
menu “Procurar/Próximo”
- avança para o próximo registro (data1.RecordSet.MoveNext) se não estivermos no final do arquivo (data1.RecordSet.EOF). Atualiza a caixa de texto do registro (data1.recordset.absoluteposition/ data1.recordset.recordcount).
Menu Navegar/Anterior
- avança para o registro anterior (data1.RecordSet.MovePrevious) se não estiver no início do arquivo (data1.RecordSet.BOF). Atualiza a caixa de texto do registro.
menu Navegar/Primeiro
- avança para o primeiro registro (data1.RecordSet.MoveFirst) se o arquivo não estiver vazio (data1.recordset.recordcount=0). Atualiza a caixa de texto do registro.
menu Navegar/Último
- vai para o último registro (data1.RecordSet.MoveLast) se o arquivo não estiver vazio (data1.recordset.recordcount=0). Atualiza a caixa de texto do registro.
menu Edição/Adicionar
- permite adicionar um registro à tabela
- entra no modo de adição de registro (data1.recordset.addnew)
- permite preencher os 5 campos: código, nome, preço, etc. (enabled=true)
- desativa os menus Editar, Procurar e Sair (enabled=false)
- exibe os botões OK e Cancelar (visible=true)
botão OK
- confirma uma alteração no registro (data1.recordset.Update)
- oculta os botões OK e Cancelar (visible=false)
- ativa as opções Editar, Procurar, Sair (enabled=true)
- atualiza a caixa de texto do registro
botão Cancelar
- invalida uma alteração no registro (data1.recordset.CancelUpdate)
- oculta os botões OK e Cancelar (visible=false)
- ativa as opções Editar, Navegar, Sair (enabled=true)
- atualiza a caixa de texto do registro
menu Editar/Modificar
- permite modificar o registro exibido no formulário
- entra no modo de edição do registro (data1.recordset.edit)
- permite preenchimentos nos 4 campos nome, preço, etc. (enabled=true), mas não no campo código (enabled=false)
- desativa os menus Editar, Procurar e Sair (enabled=false)
- exibe os botões OK e Cancelar (visible=true)
menu Editar/Excluir
- permite excluir (data1.recordset.delete) o registro visualizado da tabela
- avança para o próximo registro (data1.recordset.movenext)
menu Sair
- descarrega a planilha (unload me)
evento form_unload (cancel as integer)
- ativado pela operação “unload me” ou pelo fechamento do formulário com Alt-F4 ou um clique duplo no campo do sistema, portanto, não necessariamente pela opção “Sair”.
- exibe a pergunta “Deseja realmente sair do aplicativo?” com dois botões Sim/Não (msgbox com style=vbyes+vbno)
- Se a resposta for “Não” (=vbno), define-se “cancel” como -1 e sai-se do procedimento form_unload. O valor “cancel” igual a -1 indica que o fechamento da janela foi recusado.
- Se a resposta for “Sim” (=vbyes), o banco de dados é fechado (data1.recordset.close, data1.database.close).
Aqui, abordamos a ativação/desativação dos menus. Após cada operação que altere o registro atual, será chamado um procedimento que poderemos chamar de Oueston. Este verificará as seguintes condições:
. se o arquivo estiver vazio,
- desativaremos os menus “Navegar”, “Editar/Excluir”,
- autorizaremos os demais
. Se o registro atual for o primeiro,
- desativaremos “Navegar/Anterior”
- e permitirá o restante
. Se o registro atual for o último,
- desativaremos “Navegar/Próximo”
- permitirá o restante
Um variador horizontal possui três campos importantes:
- min: seu valor mínimo
- máx.: seu valor máximo
- value: seu valor atual
Inicialização do inversor
Ao carregar (form_load), o inversor será inicializado da seguinte forma:
- min=0
- max=data1.recordset.recordcount-1
- value=1
Observe que, logo após a abertura do banco de dados (data1.refresh), o número de registros da tabela representada por data1.recordset.recordcount está incorreto. É necessário ir até o final da tabela (MoveLast) e, em seguida, voltar ao início da tabela (MoveFirst) para que o valor fique correto.
Ação direta no inversor
A posição do cursor do variador representa a posição na tabela.
Quando o usuário altera o variador (denominado “posição” aqui), o evento position_change é acionado. Nesse evento, alteraremos o registro atual da tabela para que ele reflita o deslocamento realizado no variador. Para isso, utilizaremos o campo absoluteposition de data1.recordset. Ao atribuir o valor i a esse campo, o registro nº i da tabela passa a ser o registro atual. Os registros são numerados a partir de 0 e, portanto, possuem um número no intervalo [0,data1.recordset.recordcount-1]. No procedimento position_change, basta escrever
para que o registro atual exibido no formulário reflita o deslocamento realizado no variador.
Feito isso, chamaremos então o procedimento Oueston para atualizar os menus.
Atualização do variador
Como a posição do cursor do variador deve refletir a posição na tabela, é necessário atualizar o valor do variador sempre que houver uma mudança no registro atual da tabela, provocada por qualquer um dos menus. Como cada um deles chama o procedimento Oueston, o melhor é incluir essa atualização também nesse procedimento. Basta escrever aqui:
Adicionamos a opção “Procurar”, cujo objetivo é permitir que o usuário visualize um item cujo código ele forneça.
Quando essa opção é ativada, ocorrem as seguintes etapas:
- entra-se no modo “Adicionar registro” (Addnew), com o único objetivo de não alterar o registro atual em que se estava quando a opção foi ativada,
- permitimos a digitação no campo “código” e apagamos o conteúdo do campo “ficha”,
- os menus são desativados e os botões OK e “Cancelar” são exibidos
- quando o usuário clica em OK, precisamos procurar o registro correspondente ao código digitado pelo usuário. No entanto, o procedimento OK_click já é utilizado nas opções “Procurar/Adicionar” e “Procurar/Modificar”. Para distinguir entre esses casos, precisamos gerenciar uma variável global que chamaremos aqui de “estado”, que terá três valores possíveis: “adicionar”, “editar” e “pesquisar”. Os procedimentos associados aos botões OK e Cancelar usarão essa variável para saber em que contexto estão sendo chamados.
- Se “estado” for “pesquisar”, no procedimento associado a OK,
- cancela a operação addnew (data1.recordset.cancelupdate), pois não havia a intenção de adicionar um registro. É importante observar que, nesse caso, o registro atual volta a ser aquele que estava na tela antes da operação “Navegar/Pesquisar”.
- Constrói-se o critério de pesquisa relacionado ao código e inicia-se a pesquisa (data1.recordset.findfirst critério),
- se a pesquisa falhar (data1.recordset.nomatch=true), isso é sinalizado ao usuário; em seguida, retorna-se ao modo de adição (addnew) e sai-se do procedimento OK. O usuário deverá digitar um novo código ou selecionar a opção Cancelar.
- Se a pesquisa for bem-sucedida, o registro encontrado passa a ser o novo registro ativo. Os menus são restaurados, os botões OK/Cancelar são ocultados, a entrada no campo “código” é desativada e o procedimento é encerrado.
- . Se o estado for “procurar”, no procedimento vinculado a Cancelar,
- cancela-se a operação addnew (data1.recordset.cancelupdate). Em seguida, retorna-se automaticamente ao registro atual anterior à operação “Navegar/Pesquisar”.
- Recupera os menus, oculta os botões OK/Cancelar, impede a entrada de dados no campo código e sai do procedimento.
Um artigo deve ser identificado de forma exclusiva por seu código. Certifique-se de que, na opção “Navegar/Adicionar”, a adição seja recusada se o registro a ser adicionado tiver um código de artigo que já exista na tabela.
6.4.4. Exercício 4
Apresentamos aqui uma aplicação web baseada no servidor do exercício 2. Trata-se de um protótipo de aplicação de comércio eletrônico.
O cliente faz pedidos de itens por meio da seguinte interface da Web:

Ele pode realizar as seguintes operações:
- selecionar um item na lista suspensa
- especificar a quantidade desejada
- confirma a compra com o botão Acheter
- sua compra é exibida na lista de itens comprados
- ele pode remover itens dessa lista, selecionando um item e clicando no botão Retirer
- ao clicar no botão Bilan, ele obtém o seguinte resumo:

O extrato permite que o usuário conheça os detalhes de sua fatura. O usuário tem acesso aos detalhes relativos a um item selecionado na lista suspensa por meio do botão Informations:

Depois que o usuário solicitar o resumo da fatura, ele poderá confirmá-la na página seguinte. Para isso, ele deverá fornecer seu endereço de e-mail e confirmar o pedido clicando no botão correspondente.

Antes de salvar o pedido, o aplicativo solicita uma confirmação:

Assim que o pedido for confirmado, o aplicativo o processa e exibe uma página de confirmação:

Na verdade, o aplicativo não processa o pedido. Ele se limita a enviar um e-mail ao usuário solicitando que ele pague o valor das compras:
Cher client,
Vous trouverez ci-dessous le détail de votre commande au magasin SuperPrix. Elle vous sera livrée après réception de votre chèque établi à l'ordre de SuperPrix et à envoyer à l'adresse suivante :
SuperPrix
ISTIA
62 av Notre-Dame du Lac
49000 Angers
France
Nous vous remercions vivement de votre commande
----------------------------------------
Votre commande
----------------------------------------
article, quantité, prix unitaire, total
========================================
vélo, 2, 1202.00 F, 2404.00 F
skis nautiques, 3, 1800.00 F, 5400.00 F
Total à payer : 7804 F
Pergunta: Criar o equivalente a este aplicativo da Web usando um applet Java.

