99
Apostila de Java Tópicos Avançados Prof. Carlos Ribeiro Setembro de 1999

Apostila de Java - Aspectos Avancados (1)2[1]

Embed Size (px)

Citation preview

Page 1: Apostila de Java - Aspectos Avancados (1)2[1]

Apostila de JavaTópicos Avançados

Prof. Carlos Ribeiro

Setembro de 1999

Page 2: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 2

Índice

TRATAMENTO DE EXCEÇÕES E DEBUG DE PROGRAMAS ..........................................................4

Manipulando Erros ......................................................................................................................................4Classificação das Exceções .........................................................................................................................5Anunciando Exceções que um Método pode Gerar.....................................................................................7Como Gerar uma Exceção...........................................................................................................................9Criando uma Classe de Exceção................................................................................................................10Capturando Exceções ................................................................................................................................12Capturando Múltiplas Exceções ................................................................................................................14para obter o tipo real do objeto exceção. ...................................................................................................14Regerando Exceções..................................................................................................................................15Cláusula Finally.........................................................................................................................................15Algumas Dicas na Utilização de Exceções................................................................................................17Técnicas de Debugging .............................................................................................................................18Verificando Condições de Erro .................................................................................................................19Exibindo Mensagens de Debug em um Programa Gráfico........................................................................21Utilizando o JDB Degugger ......................................................................................................................22

STREAMS E ARQUIVOS..........................................................................................................................28

Streams ......................................................................................................................................................28Lendo e Escrevendo Bytes ........................................................................................................................28Tipos de Streams .......................................................................................................................................30Layering Streams Filters............................................................................................................................32Object Streams...........................................................................................................................................34Armazenando Objetos de Tipos Variados .................................................................................................34O Problema de Salvar Referências a Objetos ............................................................................................39

ACESSANDO BANCOS DE DADOS .......................................................................................................46

Arquitetura de Três Camadas ....................................................................................................................48Aplicações de uma Camada...................................................................................................................48Aplicações de duas Camadas.................................................................................................................49Aplicações Multicamadas......................................................................................................................49

Acessando um Banco de Dados com Java.................................................................................................52Tipos de Drivers Oracle JDBC..............................................................................................................53SQLJ......................................................................................................................................................55Desenvolvendo uma Aplicação SQLJ ...................................................................................................56Traduzindo e Compilando um Programa SQLJ.....................................................................................58JDBC 1.0 ...............................................................................................................................................59

Estabelecendo uma Conexão .............................................................................................................59Como Carregar o Driver ....................................................................................................................60Como fazer a Conexão ......................................................................................................................60Criando Statements JDBC .................................................................................................................63Entrando com Dados em uma Tabela ................................................................................................64Obtendo Dados de uma Tabela..........................................................................................................66Utilizando o método Next..................................................................................................................66

Page 3: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 3

Utilizando os Métodos getXXX ........................................................................................................66Utilizando o Método getString ..........................................................................................................69Atualizando Tabelas ..........................................................................................................................69Utilizando Prepared Statement ..........................................................................................................71Criando um objeto PreparedStatement ..............................................................................................71Fornecendo Valores para Parâmetros de PreparedStatement.............................................................71Utilizando um Loop para Atribuir Valores........................................................................................74Códigos de Retorno para o Método executeUpdate ..........................................................................74Utilizando Joins .................................................................................................................................76Utilizando Transações .......................................................................................................................77Chamando uma Stored Procedure .....................................................................................................80Capturando Exceções ........................................................................................................................81

JDBC 2.0 ..............................................................................................................................................85Navegando em um Cursor em Ambas as Direções............................................................................85Atualizando Result Sets.....................................................................................................................89Atualizando um Result Set Programaticamente ................................................................................89Inserindo e Deletando Linhas Programaticamente ............................................................................90Deletando uma Linha.........................................................................................................................94Fazendo Atualizações em Modo Batch .............................................................................................96Tratamento de Exceções em Batch Update .......................................................................................98

Page 4: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 4

TRATAMENTO DE EXCEÇÕES E DEBUG DE PROGRAMAS

Quando um erro ocorre você deve, no mínimo:

• Avisar que o erro ocorreu.• Salvar todo o trabalho.• Permitir que o usuário feche a aplicação por conta própria.

Para situações excepcionais, tais como: digitação de dados errados com o potencial de“detonar” o programa, java utiliza uma maneira de perceber e tratar o erro denominadatratamento de exceções.

A primeira parte deste capítulo aborda tratamento de exceções e a segunda parte veremostécnicas para se encontrar erros antes que eles causem exceções em tempo de execução.Infelizmente, se você utiliza apenas o JDK, você verá que a detecção de bugs éexatamente como era nos primórdios da computação. Para diminuir a dor explicaremoscomo utilizar o debbuger de linha de comando. Desenvolvedores sérios em Java devemutilizar produtos tais como Java WorkShop da Sun, Visual Café da Symantec e o Jbuilderda Inprise que possuem debuggers bastante úteis que por si só já valem o preço doproduto.

Manipulando Erros

A missão do tratamento do erro é transferir o controle da execução do ponto onde o erroocorreu para um manipulador de erros que pode tratar a situação.

Tipos de erros que podem ocorrer:

• Erros de entrada de dados: Como um URL com erro de sintaxe.• Erros de dispositivos: A impressora pode estar desligada, o papel pode acabar no

meio de uma impressão, ou uma página Web pode estar temporariamente nãodisponível.

• Limitações físicas: Um disco pode encher, ou a memória acabou.• Erros de código: A utilização de um índice inválido para um array.

A reação tradicional a um erro é retornar um código de erro especial que o métodochamador pode analisar. Por exemplo, métodos que lêem informações de arquivos àsvezes retornam um –1 como marca de fim de arquivo. Este pode ser um método eficientepara tratar muitas condições de exceções, e para certas operações de I/O Java retorna nullse a operação não foi bem sucedida. Infelizmente não é sempre possível retornar umcódigo de erro. Pode não haver uma forma obvia de distinguir dados válidos de inválidos.Um método que retorne um número inteiro não pode simplesmente retornar –1significando um erro, afinal o valor –1 pode ser um resultado perfeitamente válido.Além do mais, é difícil manter a idéia de orientação a objetos retornando um númerointeiro como uma marca de erro.

Page 5: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 5

Em vez de retornar um valor como mencionado, Java permite que todo método possuaum caminho de saída alternativo, se ele não for capaz de terminar sua tarefa de formanormal. Nesta situação o método não retorna um valor. Em vez disso, ele gera (throws)um objeto que encapsula a informação de erro. Note que o método terminaimediatamente. Ele não retorna seu valor normal. E mais, Java não ativa o código quechamou o método. Em vez disso, o mecanismo de tratamento de exceção inicia umabusca pelo “exception handler” que pode lidar com esta situação de erro particular.

Exceções possuem sua própria sintaxe e são parte de uma hierarquia de herança especial.

Classificação das Exceções

Em Java um objeto exception é sempre uma instância de uma classe descendente deThrowable, mas a hierarquia se ramifica imediatamente em: Error e Exception

A figura abaixo contém um diagrama simplificado da hierarquia exception em Java.

Page 6: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 6

A hierarquia Errors descreve erros internos e a exaustão de recursos dentro do Java run-time system. Pouco você pode fazer se um erro deste tipo ocorre, além de notificar ousuário e tentar terminar o programa normalmente. Estas situações são raras.

Quando você está programando em Java, você lida com a hierarquia Exception. Estahierarquia também se subdivide em dois ramos.: exceções que derivam deRuntimeException e as que não derivam. A regra geral é:

• Uma RuntimeException acontece quando você comete um erro de programação.

Exceções derivadas de RuntimeException incluem problemas como:

• Um cast mal feito.• Um acesso fora dos limites de um array.• Um acesso a um ponteiro null.

Exceções que não descendem de RuntimeException incluem:

• Tentativa de leitura após o final de um arquivo.• Tentativa de abrir um URL mal formado.• Tentativa de encontrar um objeto Class para um string que não representa uma classe

existente.

A regra “Se foi um erro de run-time a culpa é sua” funciona muito bem, isto é, vocêpoderia ter evitado. Em relação à má formação de URLs, sabe-se que diferentes browserspodem manipular diferentes tipos de URLs. Por exemplo, Netscape pode lidar commailto:URL, enquanto outros browsers não! Logo, a noção de URL mal formado dependedo ambiente e não apenas do código.

Page 7: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 7

Anunciando Exceções que um Método pode Gerar

Um método Java pode lançar uma exceção se ele encontra uma situação que não podetratar. A idéia é simples: um método não dirá apenas ao compilador que valores ele poderetornar. Ele também dirá ao compilador o que pode ir errado. Por exemplo, um códigoque tenta ler de um arquivo sabe que o arquivo pode não existir ou que o arquivo podeestar vazio. Logo o código que tenta processar a informação no arquivo necessitaránotificar o compilador que ele pode gerar algum tipo de IOException.

O local onde você anuncia que seu método pode gerar uma exceção é no cabeçalho dométodo. O header muda para refletir as exceções que ele pode gerar. Por exemplo: aquiestá um header para um método da classe BufferReader que deve ler uma linha de textode um stream. Obs: um stream pode ser proveniente de um arquivo ou uma conexão derede.

public String readLine() throws IOException

O header indica que este método retorna um string, mas também possui a capacidade dedar errado de uma maneira especial – gerando um IOException. Se esta triste situaçãoacontecer o método não retornará um string e sim uma exceção do tipo IOException. Seesta exceção ocorrer o run-time system começará a procurar um manipulador de exceçõesque saiba como manipular objetos do tipo IOException.

Quando você escreve seus próprios métodos, você não precisa anunciar todo tipo dethrowable object que pode ser gerado pelo seu método. Para entender quando e o quevocê deve anunciar na cláusula throws do seu método, mantenha em mente que umaexceção é gerada em qualquer uma das quatro situações abaixo:

• Você chama um método que gera uma exceção, por exemplo, o método readLine daclasse BufferedReader.

• Você detecta um erro e gera uma exceção com o comando throw. (Veremos estecomando na próxima seção).

• Você comete um erro de programação, como a[-1] = 0.

• Um erro Java interno ocorre.

Se qualquer dos dois primeiros cenários acontecerem, você deve dizer às pessoas que vãoutilizar o seu método que a utilização do seu método pode causar a geração de umaexceção.

Porque? Qualquer método que lança uma exceção é uma armadilha de morte empotencial. Se nenhum handler capturar a exceção, o programa termina. Programadores

Page 8: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 8

que utilizam o seu código geralmente não gostam de ver seus programas terminandoinesperadamente como resultado da utilização do seu código tão bem feito.

As exceções que um método pode gerar devem ser declaradas no header no método.

class Animacao{ . . .

public Image carregaImagem (String S) throws IOException{ . . .}

}

Se um método deve manipular mais de uma exceção, você deve indicar todas elas noheader. Separe-as por vírgulas, como no exemplo abaixo:

class Animacao{ . . .

public Image carregaImagem (String S)throws EOFException, MalformedURLException

{ . . .}

}

No entanto, você não necessita anunciar erros Java internos, isto é, exceçõesdescendentes de Error. Qualquer código potencialmente poderia gerar uma exceção destetipo, e elas estão inteiramente fora do nosso controle.

Da mesma forma, você não deveria anunciar exceções descendentes deRuntimeException.

class Animacao{ . . .

void drawImage (int i)throws ArrayIndexOutOfBoundsException // Não faça isso!

{ . . .}

}

Estes erros de run-time estão completamente sob o seu controle. Conserte o erro em vezde anunciar que ele pode acontecer.

A especificação da linguagem Java chama de unchecked exception qualquer exceção quederive da classe Error ou da classe RuntimeException. Todas as outras exceções sãodenominadas checked exceptions. Você deve se preocupar com as exceçõesdenominadas checked, ou manipulando-as ou anunciando que elas podem ser propagadas.Exceções unckecked estão fora do nosso controle ou resultam de condições que você nãodeveria ter permitido. (Ex. a[-1] = 0).

Page 9: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 9

Resumindo, um método deve declarar todas as checked exceptions que ele gera. Se o seumétodo não fizer isso, o compilador java emite uma mensagem de erro.

Observação: Se você faz o override de um método de uma superclasse na sua subclasse ométodo da subclasse não pode gerar mais checked exceptions do que o método dasuperclasse que você substituiu. (Pode gerar menos exceções, se você quiser).

Quando um método de uma classe declara que ele gera uma exceção que é uma instânciade uma classe particular, então ele pode gerar uma exceção daquela classe ou de qualquerde suas subclasses. Por exemplo, o método readLine da classe BufferedReader declaraque gera exceções da classe IOException. Nós não sabemos que tipo de IOException.Poderia ser um IOException puro ou um objeto de um dos seus vários filhos, comoEOFException.

Como Gerar uma Exceção

Vamos supor que você possui um método, readData, que está lendo um arquivo cujoheader informa que o tamanho do arquivo (content-length: 1024) é de 1024 bytes, masvocê recebe um EOF após ler 733 caracteres. Você decide que esta situação é tão anormalque você decide gerar uma exceção.

Você necessita decidir que tipo de exceção gerar. Algum tipo de IOException seria umaboa escolha. Olhando o arquivo tree.html na documentação das APIs Java, você descobreum EOFException com a descrição: “Signals that a EOF has been reached unexpectedlyduring input.” Perfeito. Aqui está como você gera esta exceção:

throw new EOFException();

ou, se você preferir,

EOFException e = new EOFException();throw e;

E aqui está como tudo se encaixa:

String readData(BufferedReader in) throws EOFException{ . . .

while ( . . . ){ if (ch == -1) // EOF encontrado

{ if (n < len)throw new EOFException();

}. . .

}return s;

}

Page 10: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 10

A exceção EOFException possui um segundo construtor que recebe um argumento string.Você pode utilizar este string para descrever a exceção que ocorreu.

String mensagem = “Tamanho do Arquivo: “ + len + “ Recebido:“ + n;throw new EOFException(mensagem);

Como você pode ver, é fácil gerar uma exceção se umas das classes de exceção lheatende. Neste caso:

1. Encontre a classe da exceção apropriada.2. Crie um objeto desta classe.3. Gere a exceção.

Quando um método gera uma exceção, ele não retorna para quem o chamou. Istosignifica que você não necessita atribuir um valor default de retorno ou copiar um códigode erro.

Criando uma Classe de Exceção

Pode ocorrer um problema com o seu código que não está adequadamente descrito pornenhuma das classes de exceções definidas em Java. Neste caso você deve criar suaprópria classe de exceção. É comum se fornecer dois construtores: um default e um quecontenha uma mensagem que descreve detalhadamente o que aconteceu. O métodotoString da classe base Throwable imprime a mensagem detalhada, que facilita muito oprocesso de depuração.

class MinhaExcecao extends Exception{ private String motivo;

public MinhaExcecao (String causa){ motivo = causa;}

public MinhaExcecao (){ motivo = "";}

public String getMotivo(){ return motivo;}

}

Page 11: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 11

Agora você pode gerar o seu próprio tipo de exceção.

import java.util.Calendar;class Empregado{ public Empregado (String n, double s, Calendar d)

throws MinhaExcecao{ if (s > SALARIO_MAXIMO)

throw new MinhaExcecao ("Inserção Inválida. Salário de " + s + " > " + SALARIO_MAXIMO);

else{ nome = n;

salario = s;dataContratacao = d;

}}

public void print(){ System.out.println (nome + " " + salario + " " +

anoContratacao());}

public void aumentaSalario (double percentualDeAumento) throws MinhaExcecao

{ double sal = salario * (1 + percentualDeAumento / 100);if (sal > SALARIO_MAXIMO)

throw new MinhaExcecao ("Aumento Inválido. Salário de " + sal + " > " + SALARIO_MAXIMO);

else salario = sal;

}

public void alteraDataContratacao (int ano, int mes, int dia){ dataContratacao.set(Calendar.YEAR, ano);

dataContratacao.set(Calendar.MONTH, mes);dataContratacao.set(Calendar.DATE, dia);

}

public int anoContratacao(){ return dataContratacao.get(Calendar.YEAR);}

public String getNome(){ return nome;}

public double getSalario(){ return salario;}

Page 12: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 12

public void setSalario(double sal) throws MinhaExcecao{ if (sal > SALARIO_MAXIMO)

throw new MinhaExcecao ("Modificação de salário Inválida. "+ " Salário de " + sal + " > " + SALARIO_MAXIMO);

elsesalario = sal;

}

private String nome;private double salario;private Calendar dataContratacao;final double SALARIO_MAXIMO = 3000;

}

Capturando Exceções

Naturalmente algum código necessita capturar a exceção gerada por um método. Se umaexceção é gerada e não é capturada em nenhum lugar (em uma aplicação não gráfica), oprograma termina e imprime uma mensagem uma mensagem na console fornecendo otipo da exceção e o stack trace. Em um programa gráfico (aplicações e applets), a mesmamensagem de erro é impressa, mas o controle da execução volta para o loop deprocessamento da interface gráfica do usuário. Quando você está debugando umprograma gráfico é uma boa idéia manter a console disponível na tela em vez deminimizá-la.

Para capturar uma exceção escreve-se um bloco try/catch. A forma mais simples do blocotry é:

try{ códigocódigo}catch (ExceptionType e){ código para manipular uma exceção deste tipo}

Se uma instrução dentro do bloco try gera uma exceção da classe especificada na cláusulacatch, então:

1. O programa salta as demais instruções dentro do bloco try.2. O programa executa o código dentro da cláusula catch.

Se nenhum código dentro do bloco try gerar uma exceção, então o programa salta acláusula catch.

Se um código do método gera uma exceção de um tipo diferente do tipo especificado nacláusula catch, este método encerra sua execução imediatamente retornando para quem o

Page 13: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 13

chamou. (Seria bom que o método que o chamou ou algum antecessor tivesse umacláusula catch para aquele tipo de erro.

Para mostrar isto em funcionamento, aqui está um código da classe TestaEmpregado:

import java.util.*;public class TestaEmpregado{ public static void main (String[] args)

{ Empregado[] vetEmpregados = new Empregado[2];try{ System.out.println

("Tentando inserir Luciana Arruda - Sal: 2500");vetEmpregados [0] =

new Empregado ("Luciana Arruda", 2500,new GregorianCalendar (1993, 1, 12));

}catch (MinhaExcecao e){ System.out.println (e.getRazao());}

try{ System.out.println

("Tentando inserir Lucio Arruda - Sal: 3500");vetEmpregados [0] =

new Empregado ("Lucio Arruda", 3500,new GregorianCalendar (1994, 11, 14));

}catch (MinhaExcecao e){ System.out.println (e.getRazao());}

try{ System.out.println ("Tentando conceder aumento a " +

vetEmpregados[0].getNome() + " de 25%");vetEmpregados[0].aumentaSalario(25);

}catch (MinhaExcecao e){ System.out.println (e.getRazao());}

}}

Note que a parte do código está dentro da cláusula try. Observe que a própria classe quegera um exceção pode tratá-la. No exemplo acima, deixamos o programa chamador tratara exceção. Por esta razão tivemos que anunciar no header do construtor da classeEmpregado e no header dos métodos aumentaSalario e setSalario que estes métodospodiam gerar a exceção MinhaExcecao.

public void aumentaSalario (double percentualDeAumento) throws MinhaExcecao

Lembre-se que o compilador verifica as exceções especificadas com throws. Se vocêchama um método que gera uma exceção do tipo checked, você deve tratá-la ou passá-laadiante.

Page 14: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 14

Qual a melhor alternativa? A regra geral diz que você deve tratar as exceções que vocêsabe como tratar e passar a diante as exceções que você não sabe como tratar. Há umaexceção para esta regra: se você está escrevendo um método que faz um override em ummétodo da superclasse que não gera exceções então você deve capturar cada checkedexception no código do método. Seu código não pode adicionar novas exceções à lista deexceções a um método de uma subclasse que está presente na superclasse.

Capturando Múltiplas Exceções

Você pode capturar múltiplos tipos de exceção em um bloco try e tratar cada tipo deforma diferente. Você utiliza uma cláusula catch separada para cada tipo. Por exemplo:

try{ código que pode gerar exceções}catch (MalformedURLException e1){ // Ação a ser tomada em relação a URL mal formadas.}catch (UnknownHostException e2){ // Ação a ser tomada em relação a Host desconhecido}catch (IOException e3){ // Ação a ser tomada em relação a problema de I/O}

O objeto exceção (e1, e2, e3) pode conter informações sobre a natureza da exceção. Paradescobrir mais sobre o objeto utilize:

e3.getMessage()

para obter a mensagem de erro detalhada (se ela existir), e

e3.getClass().getName()

para obter o tipo real do objeto exceção.

Page 15: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 15

Regerando Exceções

Ocasionalmente pode ser necessário capturar uma exceção, para efetuar algumprocessamento, mas sem efetivamente resolver o problema. Quando isto ocorre, vocêexecuta alguma ação de emergência em função da exceção que ocorreu e gera novamentea mesma exceção chamando throw de forma que ela seja passado para o métodochamador.

Exemplo:

Graphics g = image.getGraphics();try{ Código que pode gerar exceções}catch(MalformedURLException e){ g.dispose();

throw e;}

Embora tendo tomado alguma atitude com g.dispose(), a URL mal formada continuaexistindo daí ser repassada ao método chamador que poderá, por exemplo, solicitar aousuário que a corrija.

Cláusula Finally

Quando seu código gera uma exceção, o processamento é encerrado e o códigoremanescente do método não é executado. Isto é um problema se o método obteve algunsrecursos locais que só ele conhece. Pode ser preciso, então, no momento que umaexceção é gerada, liberar este recurso. Uma solução seria capturar e regerar todas asexceções, no entanto esta solução seria muito trabalhosa, uma vez que você teria queliberar o tal recurso alocado em vários lugares, isto é, no tratamento de cada exceção.

Page 16: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 16

Java possui uma solução melhor:

Graphics g = image.getGraphics();try{ Código que pode gerar exceções}catch(IOException e){ done = true;}finally{ g.dispose();}

Este código executa o código na clausula finally caso a exceção seja ou não capturada.Isto é, em qualquer circunstância g.dispose será executado.

Vamos examinar as três possibilidades de execução da cláusula finally:

1. O código não gera exceção. O bloco try é executado e, em seguida, o bloco finally. Aseguir, é executada a primeira linha após o bloco finally.

2. O código gera uma exceção que é capturada pela cláusula catch. O código no blocotry é executado até a linha que causou a exceção. O código remanescente no bloco tryé saltado. Então o programa executa o código na cláusula catch que captura a exceçãoe, em seguida, o bloco finally. A seguir, é executada a primeira linha após o blocofinally. Se a cláusula catch não gera uma exceção, então o programa executa aprimeira linha após o bloco try-catch-finally. Se na cláusula catch é gerada umaexceção, o bloco finally é executado e esta exceção é retornada para o programachamador.

3. O código gera uma exceção que não é capturada por nenhuma cláusula catch. Ocódigo no bloco try é executado até a linha que causou a exceção. O códigoremanescente no bloco try é saltado. Então o programa executa o código na clausulafinally e a exceção é retornada para o programa chamador.

É possível utilizar a cláusula finally sem a clausula catch. Por exemplo:

Graphics g = image.getGraphics();try{ Código que pode gerar exceções}finally{ g.dispose();}

Page 17: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 17

Algumas Dicas na Utilização de Exceções

1. Tratamento de exceções não deve substituir um simples teste.

Tratar uma exceção geralmente demora muito mais do que efetuar um simples teste.

2. Não ponha cada instrução dentro de um bloco try.

3. Não cale as exceções.

Você escreve um método que chama um método que gera uma exceção uma vez acada 100 anos. O compilador reclama porque você não declarou a exceção na listathrows do seu método. Você não deseja declará-lo na lista throws porque senão ocompilador vai criar caso com todos os métodos que chamam o seu método. Logo,você simplesmente cala a exceção.

Image loadImage (String s){ try

{ Muito código}catch (Exception e){} // Não faz nada

}

4. Propagar uma exceção não é errado.

Muitos programadores se sentem obrigados a capturar todas as exceções que elesconhecem.

Page 18: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 18

Técnicas de Debugging

1. Inserir no código comandos como:

System.out.println (“x = “ + x);

Se x for um número, será convertido para string, e se for um objeto, seu métodotoString será executado.

2. Obtenha o estado do objeto corrente com o comando:

System.out.println (“Objeto corrente = “ + this);

3. Utilize o código genérico para o método toString que imprime para cada campo o seuvalor.

4. Você pode obter uma stack trace de qualquer exceção através do métodoprintStackTrace da classe Throwable:

try{ . . .}catch (Throwable t){ t.printStackTrace();

throw t;}

5. Não é necessário capturar uma exceção para gerar um stack trace. Apenas insira ocomando abaixo em qualquer lugar do seu código:

new Throwable().printStackTrace();

Page 19: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 19

6. Se você projeta seus próprios componentes swing e eles não parecem estarfuncionando corretamente, utilize o debugger do swing. Para ligar o debugger paraum componente swing, utilize o método setDebugGraphicsOptions da classeJComponent. As seguintes opções estão disponíveis:

Nós (autor do livro CoreJava) descobrimos que para a opção flash funcionar é precisodesabilitar o “double buffering” , a estratégia utilizada pelo Swing para reduzir oflicker quando atualiza uma janela. Para ligar a opção flash faça:

RepaintManager.currentManager(getRootPane()).setDoubleBufferingEnabled(false);

((JComponent)getContentPane()).setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);

Simplesmente ponha estas linhas no final do construtor do seu frame e você verá ocontent pane sendo preenchido vagarosamente. Ou para um debug mais localizado,coloque o setDebugGraphicsOptions para um componente específico.

7. Um truque aparentemente pouco conhecido é que você pode colocar um método mainem cada classe pública. Assim você poderá testar cada classe separadamente. Criealguns objetos, chame cada método e verifique se eles funcionam corretamente.

Verificando Condições de Erro

Você pode, ao longo da execução de seu programa verificar se determinadas condiçãosão verdadeiras e caso contrário você deve gerar uma exceção. Exemplo: Você poderiagerar uma exceção se descobrir que o índice de um array está fora dos limites permitidos.

public void f(int[] a, int i){ if (!(a != null && i >= 0 && i < a.length))

throw new IllegalArgumentError (“Condição falhou”);. . .

}

Page 20: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 20

Estas verificações são denominadas assertivas, e para fazer com que o código fique maisfácil de ler podemos utilizar um método estático check.

public void f(int[] a, int i){ Assertion.check( a != null && i >= 0 && i < a.length);

. . .}

public class Assertion{ public static void check(boolean condition)

{ if (!condition)throw new IllegalArgumentError (“Condição falhou”);

}}

Quando o programa estiver testado teremos que remover este código. Uma maneirasimples de fazer isto é acrescentando uma variável boleana que determina se este testedeve ou não ser executado:

public void f(int[] a, int i){ if (debug) // Uma variável static final

Assertion.check( a != null && i >= 0 && i < a.length);. . .

}

Basta atribuir false a debug quando o sistema entrar em produção.

Page 21: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 21

Exibindo Mensagens de Debug em um Programa Gráfico

Se você executa uma applet dentro de um browser, você pode não conseguir vermensagens enviada para System.out. Esperamos que, no futuro, a maioria dos browserspossuam algum tipo de java console window. O Netscape, assim como o InternetExplorer, possuem uma. Se você utiliza o Java Plug-In, marque, no painel deconfiguração, o check box denominado Show Java Console.

Em alguns browsers a Java Console Window possui barras de rolamento de tal forma queé possível ver mensagens que rolaram para fora da tela. No ambiente DOS estasmensagens não podem ser vistas.

Page 22: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 22

Utilizando o JDB Degugger

O JDK inclui um debugger de linha de comando extremamente rudimentar denominadoJDB.

Vamos examinar o código deliberadamente corrompido do programa ButtonTest abaixo:

import java.awt.*;import java.awt.event.*;import javax.swing.*;

public class BuggyButtonTest extends JFrame implements ActionListener{ public BuggyButtonTest() { pane = new JPanel();

JButton yellowButton = new JButton("Yellow"); pane.add(yellowButton); yellowButton.addActionListener(this);

JButton blueButton = new JButton("Blue"); pane.add(blueButton); blueButton.addActionListener(this);

JButton redButton = new JButton("Red"); pane.add(redButton); redButton.addActionListener(this);

Container contentPane = getContentPane(); contentPane.add(pane); }

Page 23: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 23

public void actionPerformed(ActionEvent evt) { String arg = evt.getActionCommand(); Color color = Color.black; if (arg.equals("yellow")) color = Color.yellow; else if (arg.equals("blue")) color = Color.blue; else if (arg.equals("red")) color = Color.red; pane.setBackground(color); repaint(); }

public static void main(String[] args) { JFrame f = new BuggyButtonTest(); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); f.setSize(400, 400); f.show(); }

private JPanel pane;}

Para utilizar o JDB você deve compilar seu programa com a opção –g:

javac –g BuggyButtonTest.java

Quando você compila com esta opção, o compilador adiciona os nomes das variáveislocais e outras informações de debug nos arquivos .class. Então, você executa odebugger:

jdb BuggyButtonTest

Uma vez executando-se o debugger, você verá uma mensagem como a que vem abaixo:

Initializing jdb . . .0x139f3c8:class(BuggyButtonTest)>

O prompt > indica que o debugger está esperando por um comando. A tabela abaixomostra todos os comandos de debug que você pode fornecer. Itens entre colchetes sãoopcionais, e o sufixo significam que você pode fornecer um ou mais argumentosseparados por espaços.

Page 24: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 24

Page 25: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 25

Vamos examinar apenas os comandos JDB mais úteis. A idéia básica é simples: vocêmarca um ou mais breakpoints e então executa o programa. Quando o programa chega nobreakpoint, ele pára. Então você pode inspecionar os valores de variáveis locais.

Para estabelecer um breakpoint:

stop in class.method

Ou

stop at class:line

Por exemplo, vamos por um breakpoint no método actionPerformed deBuggyButtonTest:

stop in BuggyButtonTest.actionPerformed

Agora desejamos que o programa seja executado:

run

O programa será executado, mas o breakpoint só será alcançado quando java começar aexecutar o código do método actionPerformed. Para que isto aconteça clique no botãoAmarelo. O debugger pára no início do método actionPerformed. Você verá:

Breakpoint hit: BuggyButtonTest.actionPerformed(BuggyButtonTest:32)

Como o debugger não nos fornece uma janela mostrando a linha corrente que vai serexecutada, utilize o comando list para ver qual é a linha corrente. Por exemplo:

Page 26: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 26

Digite locals para ver todas as variáveis locais. Por exemplo:

Para maiores detalhes digite:

dump variable

Por exemplo,

dump evt

mostrará todos os campos de instância da variável evt:

Existem dois comandos básicos para executar um programa passo a passo. O comandostep que faz com que a execução passe por todos os métodos do programa, passo a passo.Infelizmente, fica muito confuso entre threads. Achamos que é mais seguro utilizar ocomando next, que executa a próxima linha sem entrar em nenhum método que sejachamado. Digite next 3 vezes para ver onde você está:

Page 27: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 27

Isto não é o que deveria ter acontecido. A variável color deveria ter sido designada parayellow para depois executar o comando setBackground.

Agora podemos entender o que aconteceu. O valor de arg era “Yellow”, com um ymaiúsculo, mas o teste compara arg com “yellow’:

if (arg.equals(“yellow”))

Para encerrar o debugger digite:

quit

Page 28: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 28

STREAMS E ARQUIVOS

Applets normalmente não podem trabalhar com arquivos no sistema do usuário. Masapplications normalmente lidam com arquivos. Nesta seção veremos os métodos paralidar com arquivos bem como os métodos para escrever e ler informações de arquivos.Este capítulo também apresenta o mecanismo de serialização que nos permite armazenarobjetos tão facilmente quanto armazenamos texto ou dados numéricos.

Streams

Esta seção trata de como obter dados de qualquer fonte de dados que possa enviar umaseqüência de bytes e como enviar dados para qualquer destino que possa receber umaseqüência de bytes. Estas fontes e destinos de seqüência de bytes podem ser arquivos,mas também podem ser conexões de rede e até blocos de memória. Informaçõesarmazenadas em arquivos e informações recuperadas de conexões de rede sãomanipuladas essencialmente da mesma forma. Enquanto os dados são armazenados comouma série de bytes, é geralmente mais conveniente pensar neles como possuindo umaestrutura de mais alto nível como sendo uma seqüência de caracteres ou objetos. Tambémveremos as facilidades que Java provê para entrada e saída de dados em mais alto nível.

Em java, um objeto a partir do qual podemos ler uma seqüência de bytes é denominadoum input stream. E um objeto no qual podemos escrever uma seqüência de bytes édenominado um output stream. Eles encontram-se implementados nas classes abstratasInputStream e OutputStream.

Lendo e Escrevendo Bytes

A classe InputStream possui um método abstrato:

public abstract int read() throws IOException

Este método lê um byte e retorna o byte que foi lido ou –1 se ele encontra o final da fontede dados. O projetista de uma classe input stream concreta faz o override deste métodopara prover funcionalidade util. Por exemplo, na classe FileInputStream, este método lêum byte de um arquivo. System.in é um objeto predefinido de uma subclasse deInputStream que nos permite ler informações do teclado.

A classe InputStream também possui métodos não abstratos para ler um array de bytes oupara pular um certo número de bytes. Estes métodos chamam o método abstrato read,para que as subclasses necessitem fazer o override de apenas um método.

Page 29: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 29

Da mesma forma, a classe OutputStream define o método abstrato:

public abstract void write (int b) throws IOException

que escreve um byte em um local de saída.

Ambos os métodos read e write podem bloquear uma thread até que o byte seja realmentelido ou escrito. Isto significa que se o byte não pode ser imediatamente lido ou escrito(geralmente em função de uma conexão de rede ocupada), Java suspende a thread quecontém esta chamada. Isto dá a oportunidade a outras threads de fazer algo útil enquantoo método está esperando pelo stream para novamente se tornar disponível.

O método available permite que você verifique a quantidade de bytes que estádisponível para leitura.

Para ler um array de bytes:

int bytesDisponiveis = System.in.available();if (bytesDisponiveis > 0){ byte [] data = new byte[bytesDisponiveis];

System.in.read(data);}

Quando você termina de ler ou de escrever em um stream, feche-o, utilizando o métodoclose, porque streams utilizam recursos limitados do sistema operacional. Se umaaplicação abre muitos streams e não os fecha, os recursos do sistema pode se exaurir.Fechar um output stream também envia o buffer utilizado pelo output stream: quaisquercaracteres que foram temporariamente colocados para que eles pudessem ser entreguescomo um pacote maior são imediatamente enviados. Em particular, se você não fecha umarquivo, o último pacote de bytes pode nunca ser entregue. Você também podemanualmente remeter a saída com o método flush. Programadores Java raramenteutilizam estes métodos porque programas Java raramente necessitam ler e escreverstreams de bytes. Os dados nos quais você está interessado provavelmente contémnúmeros, strings e objetos.

Java nos fornece muitas classes stream derivadas das classes básicas InputStream eOutputStream que nos permite trabalhar com dados da forma como geralmente utilizamosem vez de trabalhar a nível de byte.

Page 30: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 30

Tipos de Streams

Java possui mais de 60 tipos de streams. Segundo os projetistas da linguagem o fato deexistirem muitos tipos de streams faz com que o compilador seja capaz de detectar errosque só poderiam ser detectados em tempo de execução (como em C++).

Em Java, por exemplo, não há como escrever em um stream aberto apenas para leiturauma vez que InputStream não possui métodos para saída de dados.

Existem 4 classes abstratas que formam a base dos Streams: InputStream, OutputStream,Reader e Writer. Você não cria objetos destes tipos mas outros métodos podem retorná-los. Por exemplo, a classe URL possui o método OpenStream que retorna umInputStream. Você então utiliza este objeto InputStream para ler do URL. Comomencionamos anteriormente, as classes InputStream e OutputStream nos permite ler eescrever apenas bytes individuais e arrays de bytes. Elas não possuem métodos para ler eescrever strings e números. Você necessita de subclasses mais capazes para este fim. Porexemplo, as classes DataInputStream e DatOutputStream nos permitem ler e escrevertodos os tipos de dados básicos da linguagem.

Para código Unicode (dois bytes para representar um) você necessita utilizar as classesque descendem de Reader e Writer. Os métodos básicos das classes Reader e Writer sãosimilares aos das classes InputStream e OutputStream:

public abstract int read() throws IOExceptionpublic abstract void write (int b) throws IOException

Eles funcionam como os métodos das classes InputStream e OutputStream exceto que ométodo read retorna um caracter Unicode (um número inteiro entre 0 e 65535) ou –1quando você encontra o final do arquivo.

Page 31: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 31

Page 32: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 32

E existem streams que são úteis, como por exemplo as classes ZipInputStream eZipOutputStream que nos permitem ler e escrever arquivos no formato de compressãoZIP.

Layering Streams Filters

As classes FileInputStream e FileOutputStream nos fornece Streams de entrada e de saídaligados a um arquivo em disco. Você deve fornecer o nome do arquivo ou o caminhocompleto do arquivo no construtor:

Por exemplo, o comando:

FileInputStream fin = new FileInputStream (“empregados.dat”);

Procura no diretório corrente por um arquivo denominado “empregados.dat”.

Cuidado: Como “\” é um caracter especial em Strings Java, utilize \\ para caminhos dearquivos estilo windows. Por exemplo: C:\\WINDOWS\\WIN.INI.

Você também pode utilizar um objeto File:

File f = new File(“empregados.dat”);FileInputStream fin = new FileInputStream(f);

Como as classes abstratas InputStream e OutputStream, estas classes apenas suportam aleitura e escrita a nível de byte. Isto é, podemos apenas ler bytes e array de bytes com oobjeto fin.

Observação: Como todas as classes java.io interpretam caminhos de arquivos relativoscomeçando no diretório de trabalho corrente do usuário, para se saber que diretório é esteutilize o método System.getProperty(“user.dir”). O diretório corrente do usuário é odiretório onde se encontra o arquivo .class que está sendo executado.

Como você verá mais adiante, se nós apenas tivéssemos DataInputStream, entãopoderíamos ler tipos numéricos:

DataInputStream din = . . .;double s = din.readDouble();

Mas assim como FileInputStream não possui métodos para ler tipos numéricos,DataInputStream não possui nenhum método para obter dados de um arquivo.

Java utiliza um mecanismo inteligente para separar dois tipos de responsabilidades.Alguns streams (tais como o FileInputStream e o input stream retornado pelo métodoopenStream da classe URL) podem recuperar bytes de arquivos e de outros locais mais

Page 33: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 33

exóticos. Outros streams (tais como DataInputStream e PrinterWriter) podem juntar bytesem tipos de dados mais úteis. O programador Java tem de combinar os dois no que sãogeralmente denominados filtered streams fornecendo um stream existente ao construtorde outro stream. Por exemplo, para se pode ler números de um arquivo, primeiramentecrie um FileInputStream e então passe ele para o construtor de DataInputStream.

FileInputStream fin = new FileInputStream(“empregados.dat”);DataInputStream din = new DataInputStream(fin);Double s = din.readDouble();

É importante entender que o data input stream que criamos com o código acima nãocorresponde a um novo arquivo em disco. O mais novo stream criado ainda acessa osdados do arquivo atachado ao file input stream, mas a questão é que agora este novostream possui uma interface mais poderosa.

Se você olhar a figura da pág 31 você verá as classes FilterInputStream eFilterOutputStream. Para construir os streams que você deseja você deve combinar asclasses filho em um novo “filtered stream”. Por exemplo, por default, streams não sãomovidos para um buffer. Isto é, toda chamada a read entra em contato com o sistemaoperacional para pedir uma nova informação.

Se você desejar agilizar a busca a dados em um arquivo denominado empregados.dat nodiretório corrente, você necessita utilizar a seguinte seqüência de construtores:

DataInputStream din = new DataInputStream(new BufferedInputStream

(new FileInputStream (“empregados.dat”)));

Note que colocamos o DataInputStream por último na cadeia de construtores porquequeremos utilizar os métodos de DataInputStream, e queremos que eles utilizem ométodo buffered read. Embora este código seja feio você deve estar preparado parautilizar construtores de streams em camadas até que você tenha acesso à funcionalidadeque você deseja.

Page 34: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 34

Object Streams

Quando desejamos salvar em um arquivo informações sobre objetos, devemosprimeiramente salvar o tipo de cada objeto e então os dados que definem o estadocorrente do objeto. Quando lemos esta informação (previamente armazenada em umarquivo) devemos:

• Ler o tipo do objeto.• Criar um objeto vazio daquele tipo.• Preenchê-lo com os dados que armazenamos no arquivo.

É inteiramente possível, embora tedioso fazer isto manualmente, no entanto a SunMicrosystems desenvolveu um poderoso mecanismo que permite que isto seja feito commuito menos esforço. Este mecanismo, denominado serialização, quase quecompletamente automatiza este processo.

Armazenando Objetos de Tipos Variados

Para salvar dados de objetos, você primeiramente necessita abrir um objeto do tipoObjectOutputStream:

ObjectOutputStream out =new ObjectOutputStream(new FileOutputStream(“empregados.dat”);

Agora, para salvar um objeto, você simplesmente utiliza o método writeObject da classeObjectOutputStream como no fragmento de código abaixo:

Empregado luis = new Empregado ("Luis Claudio", 3500,new GregorianCalendar (1989, 10, 1));

Gerente vini = new Gerente ("Vinicius Aguiar", 4500,new GregorianCalendar (1992, 11, 6));

out.writeObject(luis);out.writeObject(vini);

Para ler os objetos de volta, primeiramente obtenha o objeto ObjectInputStream:

ObjectInputStream in =new ObjectInputStream(new FileInputStream(“empregados.dat”));

E então, recupere os objetos na mesma ordem em que foram escritos, utilizando o métodoreadObject.

Empregado e1 = (Empregado)in.readObject();Empregado e2 = (Empregado)in.readObject();

Page 35: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 35

Quando você está recuperando objetos que foram escritos em disco, você deve tercuidado com o número de objetos que foram salvos, sua ordem e seus tipos. Cadachamada a readObject lê um objeto do tipo Object, logo você necessita efetuar o cast parao tipo correto. Se você não necessita do tipo exato ou você não se lembra dele, então vocêpode efetuar o cast para qualquer superclasse, ou pode até mesmo deixá-lo como sendodo tipo Object. Por exemplo, e2 é uma variável objeto do tipo Empregado embora elapossa se referir na realidade a um objeto do tipo Gerente. Se você necessita recuperardinamicamente o tipo do objeto, você pode utilizar o método getClass, descrito nocapítulo sobre Herança na apostila de Introdução.

Você pode escrever e ler apenas objetos com os métodos writeObject e readObject, isto é,você não pode ler e escrever números com estes métodos. Para escrever e ler númerosvocê utiliza métodos tais como writeInt/readInt ou writeDouble/readDouble. As classesObject Stream implementam as interfaces DataInput e DataOutput. Naturalmentenúmeros dentro de objetos (como o campo salário do objeto empregado) sãoarmazenados e recuperados automaticamente. Lembre-se que, em Java, Strings e arrayssão objetos e podem, conseqüentemente ser restaurados com os métodoswriteObject/readObject.

Existe, no entanto, uma modificação que você necessita fazer com qualquer classe quevocê queira salvar e restaurar em um object stream. A classe deve implementar ainterface Serializable:

class Empregado implements Serializable { . . . }

A interface Serializable não possui métodos, logo você não necessita modificar as suasclasses. Neste aspecto, é similar à interface Clonable. No entanto, para fazer uma classecloneable, você ainda tinha que efetuar o override do método clone da classe Object. Parafazer com que uma classe seja Serializable, você não necessita fazer nada mais. Asclasses não são serializable por default por questões de segurança.

O programa abaixo escreve em disco um array contendo dois empregados e um gerente edepois os recupera. Escrever o array é feito com uma única operação:

Empregado [] vetEmpregados = new Empregado[3];. . .out.writeObject(vetEmpregados);

De forma similar, ler o resultado também é feito com uma única operação. No entanto,devemos aplicar um cast ao valor de retorno do método readObject:

Empregado [] novoVetEmpregados = (Empregado[])in.readObject();

Uma vez a informação armazenada , damos a cada empregado um aumento de 100%, nãoporque estamos sendo generosos mas porque podemos facilmente distinguir objetosempregados de objetos gerentes através de seus métodos aumentarSalario. Isto lhedeveria convencer de que nós restauramos os tipos corretos.

Page 36: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 36

import java.io.*;import java.util.*;public class ObjectFileTest{ public static void main (String[] args)

{ try{ Empregado[] vetEmpregados = new Empregado[3];

vetEmpregados [0] = new Empregado ("Luis Claudio", 3500,new GregorianCalendar (1989, 10, 1));

vetEmpregados [1] = new Gerente ("Vinicius Aguiar", 4500,new GregorianCalendar (1992, 12, 6));

vetEmpregados [2] = new Empregado ("Luciana Arruda", 2500,new GregorianCalendar (1993, 1, 12));

// O arquivo empregados.dat será escrito no diretório onde// se encontra o arquivo ObjectFileTest.class.

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("empregados.dat")); out.writeObject(vetEmpregados); out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("empregados.dat")); Empregado[] novoVetEmpregados = (Empregado[])in.readObject();

int i; for (i = 0; i < novoVetEmpregados.length; i++) novoVetEmpregados[i].aumentaSalario(100); for (i = 0; i < novoVetEmpregados.length; i++) novoVetEmpregados[i].print(); } catch(Exception e) { System.out.print("Erro: " + e); System.exit(1); } }}

Page 37: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 37

class Empregado implements Serializable{ public Empregado(String n, double s, GregorianCalendar d) { nome = n; salario = s; dataContratacao = d; }

public Empregado() {}

public void print() { System.out.println(nome + " " + salario + " " + anoContratacao()); }

public void aumentaSalario(double percentual) { salario *= 1 + percentual / 100; }

public int anoContratacao() { return dataContratacao.getYear(); }

private String nome; private double salario; private GregorianCalendar dataContratacao;}

class Gerente extends Empregado{ public Gerente(String n, double s, GregorianCalendar d) { super(n, s, d); nomeDaSecretaria = ""; }

public Gerente() {}

public void aumentaSalario(double percentual) { // adiciona 0.5% de bonus para cada ano de serviço GregorianCalendar hoje = new GregorianCalendar(); double bonus = 0.5 * (hoje.getYear() - anoContratacao()); super.aumentaSalario(percentual + bonus); }

public void setNomeDaSecretaria(String n) { nomeDaSecretaria = n; }

public String getNomeDaSecretaria() { return nomeDaSecretaria; }

private String nomeDaSecretaria;}

Page 38: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 38

Observação: O arquivo empregados.dat é salvo em um formato particular. Você podeutilizar os métodos writeObject e readObject sem ter que conhecer qual é a seqüência debytes que representa os objetos em um arquivo.

java.io.ObjectOutputStream

java.io.ObjectInputStream

Page 39: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 39

O Problema de Salvar Referências a Objetos

Agora sabemos como salvar objetos que contém números, strings ou outros objetossimples (como o objeto GregorianCalendar na classe Empregado). No entanto existe umaimportante situação que necessitamos considerar. O que acontece quando um objeto écompartilhado por vários objetos como parte do seu estado?

Para ilustrar o problema, vamos fazer uma pequena modificação na classe Gerente. Emvez de armazenar o nome da secretária, vamos salvar uma referência para o objetosecretária, que é um objeto do tipo Empregado.

class Gerente extends Empregado{ public Gerente(String n, double s, GregorianCalendar d, Empregado e) { super(n, s, d); secretaria = e; }

public Gerente() {}

public void aumentaSalario(double percentual) { // adiciona 0.5% de bonus para cada ano de serviço GregorianCalendar hoje = new GregorianCalendar(); double bonus = 0.5 * (hoje.getYear() - anoContratacao()); super.aumentaSalario(percentual + bonus); }

public String getNomeDaSecretaria() { return secretaria.getNome(); }

private Empregado seretaria; // A única modificação}

Esta é uma abordagem melhor para projetar uma classe Gerente real do que simplesmenteutilizar o nome da secretária – o objeto Empregado referente à secretária pode agora serlocalizado sem a necessidade de fazer uma busca no array vetEmpregados.

Uma vez feito isto, você deve ter em mente que o objeto Gerente agora contém umareferência para o objeto Empregado, que descreve a secretária, não uma cópia separadado objeto.

Page 40: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 40

Em particular, dois gerentes podem compartilhar a mesma secretária, como ocorre nafigura abaixo e no seguinte código:

luiza = new Empregado ("Luiza Thomé", 3500,new GregorianCalendar (1989, 10, 1));

Gerente vini = new Gerente ("Vinicius Aguiar", 4500,new GregorianCalendar (1992, 12, 6));

vini.setSecretaria(luiza);Gerente carlos = new Gerente ("Carlos Ribeiro", 9500,

new GregorianCalendar (1990, 17, 8));carlos.setSecretaria(luiza);

Agora suponha que o vetor vetEmpregados seja escrito em disco. O que nós nãoqueremos é que para cada objeto Gerente seja utilizada a seguinte lógica para armazenaresta informação:

• Salvar dado de empregado• Salvar dado da secretária

Page 41: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 41

Porque com esta lógica os dados de luiza seriam salvos três vezes. Quando recarregadosos objetos teriam a configuração da figura abaixo:

E isto não é o que queremos. Suponha que a secretária receba um aumento. Teríamos queprocurar por todas as outras cópias deste objeto para conceder o mesmo aumento. Naverdade desejamos salvar e recuperar apenas uma cópia deste objeto secretária. Para fazeristo devemos copiar e restaurar as referências originais para os objetos. Em outraspalavras, desejamos que o layout dos objetos no disco seja exatamente como o layout dosobjetos na memória. No linguajar da orientação a objetos isto se chama persistência.

Naturalmente não podemos salvar e restaurar os endereços de memória para os objetossecretária. Quando um objeto é recarregado, ele provavelmente ocupará um endereço dememória completamente diferente do endereço ocupado pelo objeto original.

Page 42: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 42

O que java faz nestes casos é utilizar uma abordagem baseada na serialização. Daí onome “object serialization” para este mecanismo. Aqui está o algoritmo:

1. Todos os objetos salvos em disco recebem um número de série (1, 2, 3, ...)

2. Quando um objeto vai ser salvo em disco é verificado se este objeto já foi salvoanteriormente.

3. Se ele já tiver sido salvo em seu lugar é escrito apenas o seu número de série, casocontrário, todos os seus dados são salvos.

Page 43: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 43

Ao ler de volta os objetos, este procedimento é revertido. Para cada objeto que écarregado na memória é armazenado seu número de seqüência e a posição na memóriaonde ele foi colocado. Quando um objeto com um número de seqüência já carregado namemória é procurado, a referência ao endereço de memória para este objeto éestabelecida.

Observe que os objetos não necessitam ser salvos em nenhuma ordem particular. A figuraacima mostra o que ocorre quando um gerente ocorre primeiro no array vetEmpregados.Todo este processo é automático.

Um outro tipo de aplicação muito importante que utiliza serialização é a transmissão deuma coleção de objetos através de uma conexão de rede para um outro computador.Assim como endereços absolutos de memória não tem significado em um arquivo, elestambém não têm significado quando estamos efetuando a comunicação com umprocessador diferente. Como a serialização substitui os endereços de memória pornúmeros seriais, isto permite o transporte de uma coleção de objetos de uma máquinapara outra. Nós veremos este uso de serialização quando estudarmos a chamada amétodos remotos (Servlets).

O programa abaixo salva em disco e recupera para a memória um vetor de objetoscontendo Empregados e Gerentes. Observe que alguns destes empregados compartilhama mesma secretária.

Page 44: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 44

import java.io.*;

public class ObjectFileTest{ public static void main (String[] args)

{ try{ Empregado[] vetEmpregados = new Empregado[3];

Empregado luiza = new Empregado ("Luiza Thomé", 3500,new GregorianCalendar (1989, 10, 1));

vetEmpregados [0] = luiza;vetEmpregados [1] = new Gerente ("Vinicius Aguiar", 4500,

new GregorianCalendar (1992, 12, 6), luiza);vetEmpregados [2] = new Gerente ("Luciana Arruda", 2500,

new GregorianCalendar (1993, 1, 12), luiza);

// O arquivo empregados.dat será escrito no diretório onde// se encontra o arquivo ObjectFileTest.class.

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("empregados.dat")); out.writeObject(vetEmpregados); out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("empregados.dat")); Empregado[] novoVetEmpregados = (Empregado[])in.readObject();

int i; for (i = 0; i < novoVetEmpregados.length; i++) novoVetEmpregados[i].aumentaSalario(100); for (i = 0; i < novoVetEmpregados.length; i++) novoVetEmpregados[i].print(); } catch(Exception e) { e.printStackTrace(); System.exit(1); } }}

Page 45: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 45

class Empregado implements Serializable{ public Empregado(String n, double s, GregorianCalendar d) { nome = n; salario = s; dataContratacao = d; }

public Empregado() {}

public void print() { System.out.println(nome + " " + salario + " " + anoContratacao()); }

public void aumentaSalario(double percentual) { salario *= 1 + percentual / 100; }

public int anoContratacao() { return dataContratacao.getYear(); }

private String nome; private double salario; private GregorianCalendar dataContratacao;}

class Gerente extends Empregado{ public Gerente(String n, double s, GregorianCalendar d, Empregado e) { super(n, s, d); nomeDaSecretaria = e; }

public Gerente() {}

public void aumentaSalario(double percentual) { // adiciona 0.5% de bonus para cada ano de serviço GregorianCalendar hoje = new GregorianCalendar(); double bonus = 0.5 * (hoje.getYear() - anoContratacao()); super.aumentaSalario(percentual + bonus); }

public void print() { super.print();

System.out.print(“Secretária: “);if (secretaria != null) secretaria.print();

}

private Empregado secretaria;}

Page 46: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 46

ACESSANDO BANCOS DE DADOS

A maioria das principais aplicações de uma empresa envolvem acesso a dados em tabelas– na sua maioria, bancos de dados relacionais. Com o objetivo de suportar esses tipos deaplicações, Java conta com a API JDBC. Esta API, que surgiu como um dos avanços daversão 1.1 do SDK (JDK) da SUN.

JDBC é uma API Java para execução de expressões SQL. Sendo mais específico, JDBC éum conjunto de classes e interfaces que permitem acesso a bases de dados relacionais demaneira uniforme, numa solução similar ao ODBC. Existem atualmente mais de 20implementações de drivers de bancos de dados compatíveis com JDBC, mas basicamentepodemos distinguir quatro categorias de soluções, que são definidas pela JavaSoft:

Tipo 1 – Ponte JDBC / ODBC

A ponte JDBC / ODBC foi desenvolvida em conjunto pela JavaSoft e a Intersolv, demodo a aproveitar a grande quantidade de drivers ODBC que já existem instalados nasmáquinas. Esta arquitetura funciona como explicado abaixo.

O cliente (escrito em Java) utiliza a API JDBC. O driver JDBC converte as chamadasJDBC em chamadas ODBC e passa estas chamadas para o driver ODBC, que por sua vezacessa a API nativa do banco, que finalmente acessa os dados. A principal vantagemdesta solução é que uma aplicação pode facilmente se conectar a uma fonte de dados:basta utilizar o driver ODBC correto. Em compensação, este tipo de conexão envolveuma complexidade adicional e uma sobrecarga considerável na máquina, levando aproblemas de desempenho. Isto sem falar que os drivers ODBC devem estar instalados namáquina cliente, causando os velhos problemas de administração de versões econfiguração. Por tudo isso a ponte JDBC / ODBC tente a ser uma solução passageira,apenas para resolver questões imediatas.

Tipo 2 – Driver Java Parcial + API Nativa

Nesta solução o driver ODBC é eliminado e o driver JDBC converte as chamadas JDBCem chamadas diretas à API nativa do banco. Essa API traduz para uma linguagemespecífica do banco de dados as funções JDBC.

Devemos atentar para o fato de que geralmente o driver JDBC é, na verdade, construídoparte em Java e parte em C / C++. O driver deve possuir essa camada de C para que sejapossível fazer as chamadas às APIs nativas (que são escritas em C).

Esse tipo de arquitetura, assim como a ponte JDBC / ODBC, requer que algum códigoseja instalado na máquina cliente. Entretanto, esta solução é mais rápida por não precisarda camada ODBC.

Page 47: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 47

Tipo 3 – Driver 100% Java + Protocolo de Rede (arquitetura em três camadas)

Nesta configuração o driver JDBC converte as chamadas JDBC em um protocolo de redeindependente (independente do banco de dados). Estas chamadas são convertidas, então,em chamadas à API nativa do banco por um servidor intermediário (middleware). Essaarquitetura na verdade consiste de três camadas.

Como podemos observar, o driver JDBC é escrito totalmente em Java, e sua única funçãoé implementar a lógica necessária para passar os comandos SQL para o servidorintermediário de forma conveniente. Por isso esse tipo de driver é bem pequeno(aproximadamente 200k).

Quanto ao servidor intermediário, pode ter sido escrito em java ou em qualquer outralinguagem apropriada (C / C++). Se for feito em Java, pode utilizar qualquer drivercompatível com JDBC para chegar ao banco de dados. Se for feito em outra linguagem,serão utilizadas as bibliotecas nativas do banco, como nos drivers do tipo 2.

Uma grande vantagem desta solução é ter no cliente uma solução 100% Java, sem anecessidade de instalação de nenhum produto. Uma desvantagem é que o middleware égeralmente proprietário, junto com o protocolo utilizado para trafegar na rede. Cadafabricante usa o seu próprio middleware. Isto praticamente inviabiliza o uso de outrastecnologias mais abertas e promissoras, como por exemplo, CORBA.

Tipo 4 – Driver 100% Java:

Nesta solução, o driver converte as chamadas JDBC diretamente para o protocolo de redeutilizado pelo banco de dados. Escritos somente em Java, estes drivers não necessitam deODBC nem de API nativa, gerando um enorme ganho de desempenho e permitindo odesenvolvimento de aplicações 100% Java.

Hoje em dia (junho de 1999), a oferta de drivers do tipo 4 ainda é pequena, mas atendência é que aumente bastante em breve. Os drivers do tipo 3 também têm seu lugar,enquanto os drivers do tipo 1 e 2 tendem a morrer.

Page 48: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 48

Arquitetura de Três Camadas

A arquitetura de uma aplicação determina como os seus componentes serão distribuídosatravés do sistema. A maioria das aplicações é feita de três tipos fundamentais decomponentes.

Um componente de apresentação contém a lógica que apresenta a informação a uma fonteexterna e obtém entradas daquela fonte. Na maioria dos casos a fonte externa é umusuário final trabalhando num terminal ou estação de trabalho, embora a fonte externapossa ser um sistema robótico, um telefone ou algum outro dispositivo de entrada dedados.

Um componente de negócio contém a lógica da aplicação que governa as funções donegócio e os processos executados pela aplicação. Essas funções e processos sãoexecutadas ou por um componente de apresentação, quando um usuário executa umaopção, ou por uma outra função de negócio.

Um componente de acesso a dados contém a lógica que fornece à interface um sistema dearmazenamento de dados ou algum outro tipo de fonte de dados externos, como umaaplicação externa.

Aplicações de uma Camada

A arquitetura one-tier é baseada em um ambiente onde todos os componentes sãocombinados num simples programa integrado, rodando somente em uma máquina. Essaarquitetura, totalmente centralizada, corresponde ao tradicional ambiente mainframe.

A alternativa one-tier oferece uma quantidade significativa de vantagens. Sendo aaplicação centralizada em um único ambiente, é fácil gerenciar, controlar e garantir asegurança de acesso a essas aplicações. Esses sistemas são seguros, confiáveis e suportamvários usuários.

Por outro lado, existe uma grande quantidade de desvantagens associadas a essaarquitetura. A primeira delas é a escalabilidade. Se a máquina corrente ficarsobrecarregada, a única saída é fazer um upgrade para uma máquina de maior capacidade.Aplicações one-tier são também extremamente dependentes do hardware e do ambienteoperacional. Essencialmente as companhias ficam presas a um fornecedor específico dasua plataforma de hardware. Como resultado, não se pode tirar proveito de novastecnologias, até que elas sejam disponibilizadas por aquele fornecedor específico.

Page 49: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 49

Aplicações de duas Camadas

Com o advento dos computadores pessoais, redes locais, bancos de dados relacionais,poderosas aplicações e ferramentas desktop, a indústria de computação direcionou-separa o mundo dos sistemas "abertos" e cliente/servidor. Tomadores de decisão podemgerar seus próprios relatórios e manipular dados com ferramentas desktop poderosas, emsuas próprias estações de trabalho. Esse tipo de arquitetura permite o tratamento defunções, anteriormente impossíveis de serem manipuladas.

A arquitetura cliente/servidor em duas camadas divide o processamento entre umaestação desktop e uma máquina servidora. O ambiente cliente/servidor mais popular usaum PC (Windows-based) com uma poderosa ferramenta de desenvolvimento GUI(Graphical User Interface) e um servidor de banco de dados UNIX ou Windows NT.

Algumas vantagens surgem dessa arquitetura. As ferramentas GUI possibilitam umdesenvolvimento e distribuição de aplicações muito mais rápidos. Em função dadistribuição do processamento entre o cliente e o servidor a máquina servidora resulta emcustos mais baixos do que os sistemas mainframe. Os sistemas de bancos de dados, porindependerem de plataforma, permitem portabilidade mais fácil entre sistemas,efetivamente quebrando a dependência no fornecimento do hardware. Considerando afacilidade de uso das ferramentas GUI, o nível de qualificação dos desenvolvedores nãoprecisa ser alto.

Em contrapartida, surgem algumas desvantagens: perda de segurança, confiança econtrole é uma delas. Esse modelo é extremamente eficaz para aplicações de médio porte,acessando poucos bancos de dados e que não suportam uma grande quantidade deusuários. Sem as facilidades de controle e segurança, disponíveis nos sistemascentralizados, cada aplicação cliente deve cuidar do seu próprio processo de segurança.Com isso, muitas companhias ainda relutam em mover suas aplicações de missão críticapara PCs.

Aplicações Multicamadas

Felizmente, existe uma forma de se obter o melhor das arquiteturas anteriores, sem assuas desvantagens. Além de suportar os benefícios de ambas as arquiteturas (one e two-tier), a arquitetura multi-tier também suporta os benefícios de uma arquitetura bastanteflexível.

As três camadas referem-se as três partes lógicas que compõem uma aplicação, e não aonúmero de máquinas usadas pela aplicação. O modelo multi-tier divide a aplicação nasseguintes camadas: lógica da apresentação, 1ógica do negócio e lógica de acesso aosdados, podendo existir qualquer número de qualquer das camadas numa aplicação. Oscomponentes da aplicação comunicam-se entre si utilizando uma interface abstrata, quefunciona como um contrato, em termos do que esta sendo tornado público. Essa mesmacamada abstrata esconde todos os detalhes da implementação das funções desempenhadas

Page 50: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 50

por um componente. Ela identifica a operação a ser realizada e define os parâmetros deentrada e saída necessários a execução da operação. Esse tipo de infra-estruturapossibilita serviços de localização, segurança e comunicação entre os componentes daaplicação. São várias as vantagens advindas desta nova “onda” da informática. Algumasdelas são descritas nos parágrafos seguintes.

Reutilização de objetos por outras aplicações - Objetos podem ser compartilhados pordiversas aplicações. De fato, o que está sendo construído não é propriamente umaaplicação, mas uma coleção de módulos (objetos) clientes e servidores que se comunicamatravés de uma interface padronizada e abstrata e que, ao serem combinados, funcionamcomo um sistema integrado de aplicações. Cada módulo é, na realidade, um objeto quepode ser reutilizado e compartilhado por diversos sistemas. Essa versatilidade plug-and-play é útil quando o setor de tecnologia da informação necessita suportar partes dife-rentes, mas relacionadas, do negócio.

Facilidade de manutenção do sistema -- A vantagem mais óbvia dessa arquitetura é afacilidade de manutenção. Desde que as funções da aplicação são isoladas em objetosgranulares, a 1ógica da aplicação pode ser modificada muito mais facilmente do queantes - por exemplo, uma função que é realizada por uma aplicação financeira dizrespeito a cálculo de taxas e impostos. O algoritmo para esse tipo de cálculo mudaperiodicamente, por força legal do cálculo de taxas. Se isolarmos essas regras de negócioem objetos autônomos, os algoritmos podem ser trocados, sem afetar o resto da aplicação.

A aplicação passa a independer do fornecedor de banco de dados - Uma grandevantagem da arquitetura multi-tier reside no fato de que a lógica da aplicação não é maisvinculada diretamente a estruturas de banco de dados ou a um DBMS particular. Objetosindividuais da aplicação trabalham com as suas próprias estruturas de dados, que podemcorresponder a uma estrutura do banco ou podem ser estruturas derivadas de um diferentenúmero de fontes de dados. Quando os objetos na aplicação se comunicam, eles somentenecessitam passar os parâmetros, como especificado na interface abstrata, em vez depassar os registros de um banco de dados, reduzindo assim o tráfego de rede. Os objetosde acesso a dados são os únicos componentes da aplicação que fazem uma interfacediretamente com o banco de dados. Dessa forma um banco de dados pode ser migrado deum fornecedor para outro, sem afetar a aplicação como um todo. Somente a camada dalógica de acesso aos dados precisará ser alterada. Isso representa uma autonomia para sereagir melhor a uma mudança tecnológica ou de negócio. Um outro fato importante éque, na camada da lógica de apresentação, ou seja, o lado cliente da aplicação, não há amenor necessidade de ter configurações para acesso a dados, como existe hoje nasaplicações cliente/servidor com a instalação dos chamados database clients. Isto é o quese chama de aplicações thin client.

A abstração da lógica de acesso a dados leva a um outro significativo beneficio: oconceito de que os dados podem ser estendidos para incluir arquivos seqüenciais,indexados, bancos não relacionais e mesmo legacy application systems. Um conjunto demódulos de acesso a dados pode ser desenvolvido para prover acesso a esses ambienteslegados, com um conveniente conjunto de interfaces abstratas que são acessíveis de

Page 51: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 51

qualquer lugar para toda a organização. Num mundo multi-tier, um módulo de acesso adados que acessa legacy data hoje, pode ser substituído por um outro módulo que acessedados relacionais amanhã, sem afetar o resto da aplicação. O segredo disso tudo chama-seinterfaces.

Alta produtividade de desenvolvimento através de especialização - Com a tecnologiatwo-tier, cada programador deve desenvolver todos os aspectos de uma aplicação,incluindo apresentação, negócio e lógica de acesso a dados. Com isso, o fato de quemuitos programadores se superam em determinadas tarefas, e não em outras, não éconsiderado, bem como o fato de que eles são mais produtivos quando especializados.

No mundo multi-tier, programadores com excelente skill para interface de usuáriospodem se concentrar em desenvolver poderosos componentes de apresentação, e eles nãonecessitam saber sobre os "internals" da lógica de negócios ou como dados são acessadosde um banco de dados. Isso vale também para analistas de banco de dados, que conhecema melhor maneira de acessar dados, ou analistas de negócio, que podem se concentrar emdesenvolver algoritmos de negócio. Tudo que eles precisam saber são as interfaces, e aquitalvez a figura do contrato faça mais sentido. Quando um componente de negócioprecisar de dados, e só fazer a chamada apropriada ao componente de acesso a dados.

Infra-estrutura distribuída de computação - Uma infra-estrutura distribuída decomputação provê os serviços que permitem aos componentes da aplicação seremtransparentemente distribuídos por qualquer número de sistemas físicos, um conceitonormalmente conhecido como particionamento (partitioning).

Page 52: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 52

Acessando um Banco de Dados com Java

O que utilizar quando se pretende acessar um banco de dados a partir de uma aplicaçãoJava: JDBC ou SQLJ?

Ambas estas APIs são utilizadas no acesso a bancos de dados. Mas porque duas APIs enão apenas uma? Na realidade você apenas necessita de JDBC, mas você verá que emfunção da simplicidade de programação em SQLJ você desejará utilizar esta ferramenta.

JDBC é um padrão independente de vendedor desenvolvido pela Javasoft. JDBC foidesenvolvido após ODBC (desenvolvido pela Microsoft). JDBC é a principal forma deacesso a bancos de dados através de código Java. Vários fabricantes de SGBD, inclusiveOracle, Intersolv e Weblogic, desenvolveram vários tipos de drivers JDBC.

SQLJ é um novo padrão para embutir código SQL diretamente em programas Java. É umproduto padrão desenvolvido pela Oracle, IBM, Sybase, Tandem e Informix. Para aquelesque estão familiarizados com Pro*C (SQL embutido em C), você pode ver SQLJ comoPro*Java. Então porque não se chama Pro*Java? Pro*qualquer coisa significa umatecnologia pro*prietária da Oracle, por exemplo, enquanto SQLJ é um padrão aberto.

JDBC

import java.sql.*;public class JDBCExample{ public static void main(String args[]) throws SQLException

{ DriverManager.registerDriver(neworacle.jdbc.driver.OracleDriver());

Connection conn =DriverManager.getConnection("jdbc:oracle:thin:@sbd:1521:orcl", "scott",

"tiger");// @nome_do_computador:porta:nome_da_instânciaStatement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(“SELECT ENAME, EMPNO, SAL “

+ “FROM EMP”);while (rs.next()){ String nome = rs.getString(1);

int numero = rs.getInt(2);double salario = rs.getDouble(3);System.out.println(nome + “ “ + numero + “ “ +

salario);}

rs.close();conn.close();

}}

Page 53: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 53

A primeira linha do método main é:

DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

Tudo o que você realmente necessita saber é que esta chamada faz com que JDBC saibaque você está utilizando um driver da oracle.

A próxima chamada é:

Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@sbd:1521:orcl", "scott", "tiger");

Esta chamada cria uma objeto que representa uma conexão com o banco de dados. Aprimeira parte do string de conexão, jdbc:oracle:thin, indica que deverá serutilizado um driver 100% java para estabelecer a conexão com o banco de dados. ASegunda parte do string de conexão, @sbd:1521:orcl, especifica que a conexãodeverá ser efetuada com a instância orcl na máquina denominada sbd, cujo listener estána porta 1521. A conexão é efetuada na conta do usuário scott que possui a passwordtiger.

Agora que a conexão foi estabelecida podemos emitir comandos SQL para o banco dedados:

Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(“SELECT ENAME, EMPNO, SAL FROM EMP”);

As últimas linhas, rs.close() e conn.close(), permitem que JDBC saiba que vocêterminou com o result set e que a conexão com o banco, bem como a correspondentesessão, devem ser encerradas.

Tipos de Drivers Oracle JDBC

A Oracle fornece três tipos de drivers JDBC: um driver thin (fino) 100% Java, um OCI-based fat driver e um driver do tipo server-side (também conhecido como KPRB). Todosestes três drivers atualmente (fevereiro de 1999) atendem à especificação 1.2.2 de JDBCe estão a caminho de atender à especificação de JDBC 2.0. Estes drivers são compatíveiscom JDK 1.0.2 e 1.1.6. Os drivers thin e fat podem ser utilizados com bancos de dadosOracle a partir da versão 7.2. Todos estes drivers utilizam as mesmas APIs,proporcionando flexibilidade máxima em como aplicações JDBC podem serdesenvolvidas.

Como já mencionado, o driver Oracle do tipo thin é uma implementação 100% Java. Poresta razão, é ideal para aplicações que necessitam ser 100% Java, tais como applets. Umoutro benefício do driver thin é que ele é pequeno (300k – ou 150k quando comprimido),sendo interessante a sua utilização em aplicações que necessitam sofrer download, como

Page 54: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 54

as applets. Em termos de performance, o driver thin é ligeiramente mais lento do que seubig-brother OCI-based, que foi implementado em C.O driver Oracle do tipo fat possui uma camada JDBC implementada em cima da OracleCall Interface (OCI). OCI é uma biblioteca C para acesso a bancos de dados Oracle.Como o driver fat necessita que esta biblioteca (OCI) seja pré-instalada, não pode serutilizado em aplicações 100% Java e por aplicações carregadas através de download. Estedriver, no entanto, é o ideal para utilização em aplicações middle-tier, tais como servlets.

O driver Oracle server-side é o último da família. Agora que o Oracle8i permite queaplicações Java rodem dentro do banco de dados, estas aplicações necessitam de umaforma de acessar os dados no banco de dados (a principal razão para executar Java nobanco é se o programa manipula muitos dados). Os drivers do tipo server-side foramcriados para este tipo de utilização. Como estes drivers sabem que o código está no bancoeles não necessitam abrir explicitamente uma conexão. Eles também evitamcompletamente a latência devido à rede ou devido à comunicação entre processosacessando diretamente dados SQL. Como você já deve saber, código Java no Oracle 8i éexecutado acessando a mesma memória e ocupando o mesmo espaço dos processos queacessam a instância do banco de dados.

Todos os três drivers mencionados anteriormente utilizam a mesma API. Isto dá aosdesenvolvedores de aplicações Java/JDBC grande flexibilidade em como desenvolveraplicações Java. Por exemplo, digamos que uma aplicação Client/Server seja escritautilizando um driver JDBC-OCI. Neste caso, o cliente seria uma combinação de umaaplicação Java com um driver e o servidor seria um banco de dados Oracle. Surge então oOracle8i e o desenvolvedor percebe que teria um grande ganho de desempenho seexecutasse a parte da sua aplicação que faz muito acesso a dados, dentro do banco dedados. Para efetuar esta modificação o desenvolvedor teria que carregar para dentro dobanco as classes que fazem acesso aos dados e modificar as linhas que estabelecem aconexão para:

Connection conn =new oracle.jdbc.driver.OracleDriver().defaultConnection();

Da mesma forma, o desenvolvedor poderia decidir modificar a sua aplicação para umaapplet Java. Neste caso, a única modificação JDBC necessária seria a especificação dodriver thin em vez do driver OCI-based no string de conexão. No mundo de hoje estaflexibilidade deve ser levada em consideração. Ela permite que os desenvolvedorespossam rapidamente e facilmente adaptar suas aplicações para a configuração dedesenvolvimento do momento.

Page 55: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 55

JDBC Não é Perfeito

Erros em comandos SQL só podem ser capturados em tempo de execução. Isto porque ocompilador Java utilizado para compilar o código JDBC não conhece a sintaxe e asemântica de SQL. Em segundo lugar, escrever um comando SQL complexo em JDBCpode ser bastante trabalhoso. Isto é ilustrado pelo código abaixo:

String nome = “CAFE BRASILEIRO”;int qtd = 75;PreparedStatement atualizarVendas = con.preparedStatement

(“UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?”);atualizarVendas.setInt(1, qtd);atualizarVendas.setString(2, nome);atualizarVendas.execute();atualizarVendas.close();

Observação: Um prepared statement deve ser utilizado quando valores dentro de umcomando SQL devem ser modificados dinamicamente e você pretende utilizar estemesmo prepared statement diversas vezes, com diferentes valores de variáveis.

Este exemplo, nos trás duas questões: Há alguma coisa que pode ser feito para fazer comque queries SQL em Java 1) sejam mais fáceis de escrever e de ler e 2) possam serverificadas (através de debug) em tempo de compilação? A resposta é SQLJ.

SQLJ

Para lhe dar uma idéia rápida de como SQLJ funciona vejamos o exemplo abaixo que fazo mesmo que as linhas JDBC acima.

String nome = “CAFE BRASILEIRO”;int qtd = 75;#sql {UPDATE PRODUTOS SET QTD_SEMANA = :qtd WHERE NOME = :nome};

Note que no exemplo acima as variáveis qtd e nome foram diretamente embutidas nocomando SQL. Toda variável embutida em código SQL deve ser precedida por “:”.

SQLJ também resolve o problema do debug de código SQL. Como o código SQLJ não épuro Java, ele necessita ser traduzido antes que possa ser compilado para o bytecodeJava. Este processo de tradução permite que os comandos SQL embutidos sejamanalisados semântica e sintaticamente, capturando erros de SQL em tempo de traduçãoem vez de em tempo de execução (como ocorre com JDBC).

Há várias razões porque você deveria conhecer JDBC e SQLJ. JDBC deve ser utilizadopara gerar comandos SQL em tempo de execução enquanto SQLJ é ideal para comandosSQL estáticos, isto é, quando o formato do comando SQL é conhecido no momento dodesenvolvimento da aplicação. Observe que em aplicações típicas a grande maioria doscomandos SQL são estáticos.

Page 56: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 56

Em segundo lugar, a atual encarnação de SQLJ (da Oracle) é executada sobre JDBC. Istopermite o desenvolvimento de aplicações SQLJ com qualquer configuração de driver,assim como com JDBC (thin, fat ou server-side) mas isto também significa que odesenvolvedor que utiliza SQLJ necessita conhecer os vários tipos de drivers JDBC.

Desenvolvendo uma Aplicação SQLJ

Existem 3 etapas no desenvolvimento de uma aplicação SQLJ:

1. Escrever o programa SQLJ.2. Traduzir o programa SQLJ.3. Executar o programa SQLJ.

Exemplo de Programa SQLJ:

import java.sql.*;import sqlj.runtime.ref.DefaultContext;import oracle.sqlj.runtime.Oracle;#sql iterator MyIter (String ename, int empno, float sal);public class MyExample{ public static void main (String args[]) throws SQLException

{ Oracle.connect (“jdbc:oracle:thin:@sbd:1521:orcl”, “scott”, “tiger”);

#sql { INSERT INTO EMP (ENAME, EMPNO, SAL) VALUES (‘SALMAN’, 32, 20000) };

MyIter iter;

#sql iter = { SELECT ENAME, EMPNO, SAL FROM EMP };while (iter.next()){ System.out.println (iter.ename() +“ “+ iter.empno() +

“ “+ iter.sal());}

}}

Page 57: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 57

A primeira linha do método main é:

Oracle.connect (“jdbc:oracle:thin:@sbd:1521:orcl”, “scott”, “tiger”);

Esta chamada estabelece uma conexão de banco de dados default para SQLJ utilizar. Apartir deste momento podemos escrever comandos SQLJ. O primeiro comando é:

#sql { INSERT INTO EMP (ENAME, EMPNO, SAL) VALUES (‘SALMAN’, 32, 20000) };

O comando abaixo executa uma query. E o resultado da query não vai para um result setcomo em JDBC. Em SQLJ não utilizamos result sets. Utilizamos iterators. A diferençaentre um result set e um iterator é que um result set pode armazenar o resultado dequalquer query enquanto um iterator só pode armazenar o resultado de um tipo específicode query. Por esta razão é preciso definir um iterator type para cada tipo de querydiferente.

#sql iter = { SELECT ENAME, EMPNO, SAL FROM EMP }

A linha de código abaixo define um tipo iterator denominado MyIter:

#sql iterator MyIter (String ename, int empno, float sal);

Olhando a definição acima podemos ver que este tipo de iterator pode armazenarresultados com as seguintes características: a primeira coluna deve poder ser mapeadapara um String Java, a segunda para um int Java e a terceira para um float Java. Estadefinição também atribui nomes a estas colunas: ename, empno e sal respectivamente.

Uma instância de MyIter é criada com o comando:

MyIter iter;

E finalmente a instância iter é populada com dados. Então podemos acessar estes dados,linha a linha, com o comando abaixo:

while (iter.next()){ System.out.println (iter.ename() +“ “+ iter.empno() +“ “+

iter.sal());}

O método next() de um iterator faz o mesmo que o método next de um result set.Se o iterator não está na última linha de dados, next() retorna true e aponta o iteratorpara a próxima linha. Caso contrário, retorna false. A maneira como os dados dedeterminada linha são acessados em um iterator é um pouco diferente da forma como istoé feito em com um result set em JDBC. O iterator (como você pode ver no exemploacima) possui métodos que correspondem aos nomes das colunas no iterator. Chamandoestes métodos, o dado na coluna correspondente é retornado.

Page 58: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 58

Traduzindo e Compilando um Programa SQLJ

Para traduzir um programa SQLJ faça:

>sqlj MyExample.sqlj

Há muitas opções que podem ser especificadas, mas a chamada acima é suficiente.

Enquanto o tradutor sqlj gera o arquivo MyExample.java ele verifica se o seu códigoSQL embutido faz sentido. Ele analisa este código sintática e semanticamente. Ele podeinclusive verificar se as tabelas, colunas e tipos de dados Java que você está utilizandosão apropriados. Ele efetua uma conexão com o banco de dados para efetuar estaverificação. Se você deseja que o tradutor se conecte a um banco de dados de teste (e nãocom o banco de produção) para efetuar esta verificação, você necessita especificar umURL de um banco de dados com o qual a conexão deve ser efetuada. Você deveráfornecer também a conta e a password do usuário.

Além de efetuar a verificação do SQL embutido em tempo de tradução, o translatortambém gera um serialized object file (com a extensão .ser). Este arquivo contém umadescrição das queries SQL feitas no programa SQLJ. Isto é feito com dois objetivos:

1) Os arquivos .java gerados pelo tradutor SQLJ efetuam chamadas a uma bibliotecaJava denominada SQLJ runtime. As classes nesta biblioteca utilizam o serializedobject file para descobrir que chamadas JDBC deve efetuar.

2) O serialized object file pode ser utilizado para customizar o programa SQLJ para umfabricante específico de SGBD (isto é feito automaticamente pelo tradutor SQLJ).

A compilação dos arquivos .java gerados pelo tradutor é efetuada pelo tradutor SQLJ. Ocompilador javac do JDK é executado em todos os arquivos .java. O processo decompilação gera bytecode Java logo, após esta etapa, o programa SQLJ pode serexecutado chamando-se o interpretador java, assim:

>java MyExample

Page 59: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 59

JDBC

Inicialmente estudaremos a API JDBC 1.0, que acompanham a versão 1.1 do JDK.Posteriormente estudaremos a API JDBC 2.0, que acompanham a versão 1.2 do JDK.

No estudo da API JDBC 1.0 aprenderemos como utilizar esta API para criar tabelas,inserir valores em tabelas, emitir queries, recuperar os resultados de queries, e atualizartabelas. Você aprenderá a emitir os comandos statement e prepare statement, e verá,ainda, como executar uma stored procedure e como capturar exceções e warnings.

No estudo da API JDBC 2.0 aprenderemos a mover o cursor em um scrollable result set,como atualizar um result set e como fazer atualizações batch.

JDBC 1.0

Em um programa que utiliza JDBC a primeira coisa a fazer é importar os packages eclasses que você vai utilizar na nova classe que você vai criar na sua aplicação. As classesdos programas exemplo apresentadas nesta apostila utilizam o package java.sql (a APIJDBC), que se torna disponível colocando-se a seguinte linha de código antes dadefinição da class:

import java.sql.*;

Se você não incluir “import java.sql.*;” no seu código, você terá de escrever “java.sql. “mais o nome da classe em frente de todos os métodos e campos JDBC que você utiliza.

Estabelecendo uma Conexão

Para se estabelecer uma conexão com o banco de dados é preciso:

1. Carregar o driver.2. Fazer a conexão.

Page 60: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 60

Como Carregar o Driver

Para carregar o driver do tipo bridge JDBC-ODBC é preciso escrever a seguinte linha decódigo:

Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);

A documentação do seu driver lhe informará o nome da classe que você deve utilizar. Seo nome da classe é jdbc.DriverXYZ, então você deve carregar o diver assim:

Class.forName(”jdbc.DriverXYZ”);

Você não necessita criar uma instância do driver e registrá-lo no DriverManager porqueao chamar Class.forName isto será feito automaticamente. Uma vez tendo-se carregado odriver, ele se encontra disponível para uso.

Como fazer a Conexão

A conexão, realizada pelo driver, é efetuada com a seguinte linha de código:

Connection con = DriverManager.getConnection(url, ”Conta”, “Senha”);

A dificuldade aqui está em saber o que utilizar como url. Se você está utilizando umdriver do tipo ponte JDBC-ODBC, o URL JDBC começará com jdbc:odbc:. O resto doURL é geralmente o nome do data source. Logo, se você está utilizando ODBC paraacessar um data source ODBC denominado “Uff_ds”, por exemplo, seu URL JDBCseria: jdbc:odbc:Uff_ds.

Exemplo:

String url = “jdbc:odbc:Uff_ds”;Connection con = DriverManager.getConnection (url, “Conta”, “Senha”);

Se você está utilizando um driver desenvolvido por terceiros, a documentação lhe diráque subprotocolo utilizar, isto é, o que colocar após jdbc: no URL JDBC. Por exemplo, seum desenvolvedor de driver registrou o nome acme como o subprotocolo, a primeira e asegunda parte do URL JDBC será: jdbc:acme: .

Se um dos drivers que você carregou reconhece o URL JDBC fornecido para o métodoDriverManager.getConnection, este driver irá estabelecer a conexão com o DBMSespecificado no URL JDBC. A classe DriverManager gerencia todos os detalhesnecessários para estabelecer a conexão. A menos que você esteja escrevendo um driver,você nunca utilizará os métodos da interface Driver, e o único método da classeDriverManager que você realmente necessita conhecer é oDriverManager.getConnection.

Page 61: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 61

A conexão retornada pelo método DriverManager.getConnection poderá ser utilizadapara criar comandos JDBC que passam seus comandos SQL para o SGBD. No exemploanterior o objeto con representa a conexão e será utilizado nos exemplos seguintes.

Exemplo:

import java.sql.*;public class Lista_Empregados{ public void lista_emp() throws Exception

{ String nome, salario;Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection conn =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = conn.createStatement();ResultSet rset = stmt.executeQuery("SELECT * FROM EMP");

System.out.println ("Nome Salario");while (rset.next()){ nome = rset.getString ("ENAME");

salario = rset.getString("SAL");System.out.println (nome+" "+salario);

}}

public static void main (String[] args) throws Exception{ Lista_Empregados emp = new Lista_Empregados();

emp.lista_emp();}

}

No exemplo acima estamos utilizando um driver do tipo ponte JDBC-ODBC. A palavraOracle no comando DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");representa o nome de uma fonte de dados ODBC.

Page 62: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 62

Criando Tabelas

Primeiramente criaremos as tabelas do nosso banco de dados exemplo. Os nomes dastabelas são: Produtos e Fornecedores. A tabela Produtos, contém as informaçõesnecessárias sobre os tipos de produtos vendidos pela firma The Coffe Break. E a tabelaFornecedores contém dados a respeito dos fornecedores de cada tipo de café vendido.

Layout da tabela Produtos:

Numero Number(3)Nome Varchar2(32)Num_Fornededor Number(4)Preco Number(5,2)Qtd_Semana Number(7,2)Qtd_Total Number(9,2)

Numero Nome Num_Fornecedor Preco Qtd_Semana Qtd_total101 Café Brasileiro 1001 7.99 0 0102 Café Colombiano 1002 8.99 0 0103 Café Francês 1003 9.99 0 0104 Café Expresso 1001 5.77 0 0105 Café Colombiano Descafeinado 1002 8.99 0 0106 Café Frances Descafeinado 1003 9.99 0 0

Layout da tabela Fornecedores:

Numero Number(4)Nome Varchar2(40)Rua Varchar2(40)Cidade Varchar2(20)Estado Char(2)CEP Char(9)

Numero Nome Rua Cidade Estado CEP1001 Cia do Café Ltda R. das Laranjeiras 107 Rio RJ 22241-0901002 Café Colombiano Ltda Av Brasil, 5240 Rio RJ 22348-0301003 Café Francês Ltda R. México 70/403 Rio RJ 22127-040

Page 63: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 63

O comando abaixo cria a tabela PRODUTOS.

CREATE TABLE PRODUTOS(NUMERO NUMBER(3)

NOME VARCHAR2(32) NUM_FORNECEDOR NUMBER (4) PRECO NUMBER (5,2) QTD_SEMANA NUMBER (7,2) QTD_TOTAL NUMBER (9,2))

O comando acima não termina com um terminados do SGBD, que varia de SGBD paraSGBD. Por exemplo, o Oracle utiliza um ; para indicar o final de um comando, enquantoo Sysbase utiliza a palavra go. O driver que você estiver utilizando automaticamentefornece o terminador apropriado e você não precisará inclui-lo no seu código JDBC.

Para criar a tabela PRODUTOS através de um programa Java:

String createTableProdutos = “CREATE TABLE PRODUTOS “ +“(NUMERO INTEGER, NOME VARCHAR(32), “ +“NUM_FORNECEDOR INTEGER, PRECO FLOAT, “ +“QTD_SEMANA INTEGER, QTD_TOTAL INTEGER))”;

Os tipos de dados utilizados no comando CREATE TABLE acima são os tipos SQLgenéricos (também denominados tipos JDBC) que são definidos na classe java.sql.Types.

Alternativamente, é possível utilizar tipos de dados específicos do SGBD que se estáutilizando:

String createTableProdutos = “CREATE TABLE PRODUTOS “ +“(NUMERO NUMBER(3), NOME VARCHAR2(32), “ +“NUM_FORNECEDOR NUMBER(4), PRECO NUMBER (5,2), “ +

“QTD_SEMANA NUMBER(7,2), QTD_TOTAL NUMBER(9,2))“;

Criando Statements JDBC

Um objeto statement é utilizado para enviar o seu comando SQL para o SGBD. Vocêsimplesmente cria um objeto statement e o executa através dos métodos executeQuery ouexcuteUpdate, passando como parâmetro o string SQL que se deseja executar.

É utilizada uma instância da conexão ativa para criar um objeto do tipo Statement. Noexemplo abaixo, utilizamos nosso objeto Connection (con) para criar o objeto Statement(stmt).

Statement stmt = con.createStatement();

Neste momento stmt existe, mas não possui um comando SQL para passar para o SGBD.É preciso fornecer este string SQL para o método que utilizaremos para executar o objeto

Page 64: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 64

stmt. Para executar um comando SELECT utilizamos o método executeQuery, e paracomandos que criam ou modificam tabelas, utilizamos o método executeUpdate.

Stmt.executeUpdate (“CREATE TABLE PRODUTOS “ +“(NUMERO NUMBER(3), NOME VARCHAR2(32), “ +“NUM_FORNECEDOR NUMBER(4), PRECO NUMBER (5,2), “ +

“QTD_SEMANA NUMBER(7,2), QTD_TOTAL NUMBER(9,2))“);

Uma vez que criamos um string com o comando SQL que desejamos executar, eassociamos este string à variável createTableProdutos, poderíamos ter escrito o comandoque executa este string SQL, assim:

Stmt.executeUpdate (createTableProdutos);

Entrando com Dados em uma Tabela

Segue abaixo o código necessário para inserir uma linha na tabela PRODUTOS:

Statement stmt = com.createStatement();

stmt.executeUpdate (“INSERT INTO FORNECEDORES(NUMERO, NOME, RUA, CIDADE, ESTADO, CEP) ” +“VALUES(1001, ‘CIA DO CAFE LTDA’, ‘R. DAS LARANJEIRAS 107’, ‘RIO’, “ +“‘RJ’, ‘22241-090’)”);

Observe que para inserir novas linhas na tabela PRODUTOS não é preciso instanciar umnovo objeto stmt.

stmt.executeUpdate (“INSERT INTO FORNECEDORES(NUMERO, NOME, RUA, CIDADE, ESTADO, CEP) ” +“VALUES(1002, ‘CAFE COLOMBIANO LTDA’, ‘AV. BRASIL, 5240’, ‘RIO’, “ +“‘RJ’, ‘22348-030’)”);

Page 65: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 65

Exemplo:

import java.sql.*;public class Cria_Tabela{ public void cria_tab_Produtos() throws Exception

{ String nome, salario;Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");

Connection conn =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

String createTableFornecedores = "CREATE TABLE FORNECEDORES " +"(NUMERO NUMBER(4), NOME VARCHAR2(40), RUA VARCHAR2(40), " +"CIDADE VARCHAR2(20), ESTADO CHAR(2), CEP CHAR(9))";

Statement stmt = conn.createStatement();stmt.executeUpdate (createTableFornecedores);

stmt.executeUpdate (“INSERT INTO FORNECEDORES(NUMERO, NOME, RUA, CIDADE, “ +“ESTADO, CEP) ” +“VALUES(1001, ‘CIA DO CAFE LTDA’, “ +“‘R. DAS LARANJEIRAS 107’, ‘RIO’, ‘RJ’, ‘22241-090’)”);

stmt.executeUpdate (“INSERT INTO FORNECEDORES(NUMERO, NOME, RUA, CIDADE, “ +“ESTADO, CEP) ” +“VALUES(1002, ‘CAFE COLOMBIANO LTDA’, “ +“‘AV. BRASIL, 5240’, ‘RIO’, ‘RJ’, ‘22348-030’)”);

}

public static void main (String[] args) throws Exception{ Cria_Tabela tab = new Cria_Tabela ();

tab.cria_tab_Produtos();}

}

Page 66: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 66

Obtendo Dados de uma Tabela

Agora que a tabela Fornecedores possui valores podemos escrever um comando SELECTpara recuperar estes valores.

JDBC retorna os resultados em um objeto do tipo ResultSet, logo é preciso declarar umainstância da classe ResultSet para armazenar os resultados. O código abaixo declara umobjeto rs do tipo ResultSet e atribui o resultado de uma query a ele.

ResultSet rs = stmt.executeQuery(“SELECT * FROM FORNECEDORES”);

Utilizando o método Next

A variável rs, que é uma instância de ResultSet contém as linhas da tabela deFornecedores. Para acessar cada coluna é preciso navegar pelas linhas retornadas erecuperar os valores de acordo com seus tipos. O método next nos permite percorrer ocursor resultante da execução do comando select... Inicialmente o cursor encontra-seposicionado logo acima da primeira linha de um objeto ResultSet, logo, a primeirachamada ao método next move o cursor para a primeira linha, fazendo com que ela seja alinha corrente. Sucessivas chamadas ao método next faz com que possamos percorrer ocursor do início ao fim. Com a API JDBC 2.0 é possível, também, navegar em um cursorde trás para frente, para posições específicas, e para posições relativas à linha corrente.

Utilizando os Métodos getXXX

Utilizamos os métodos getXXX de um tipo apropriado para recuperar o valor de cadacoluna. Por exemplo, a coluna NOME da tabela de Fornecedores armazena um valor dotipo VARCHAR. O método para recuperar um valor de SQL do tipo VARCHAR égetString. A Coluna NUMERO da tabela de Fornecedores é do tipo INTEGER e ométodo utilizado para recuperar valores do tipo integer é getInt. A parte XXX do métodogetXXX permite que JDBC saiba para que tipo de dado Java deve ser convertido o tipode dado SQL.

O código abaixo acessa os valores armazenados na linha corrente de rs e imprime emcada o número e o nome de cada fornecedor.

String query = “SELECT * FROM FORNECEDORES”;ResultSet rs = stmt.executeQuery(query);

System.out.println ("Nome Salario");while (rs.next()){ int numero = rs.getInt("NUMERO");

String nome = rs.getString ("NOME");System.out.println (numero+" "+nome);

}

Page 67: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 67

import java.sql.*;public class Lista_Fornecedores{ public void lista_fornec() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection conn =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = conn.createStatement();

String query = “SELECT * FROM FORNECEDORES”;ResultSet rs = stmt.executeQuery(query);

System.out.println ("Numero Nome");while (rs.next()){ int numero = rs.getInt("NUMERO");

String nome = rs.getString ("NOME");System.out.println (numero+" "+nome);

}}

public static void main (String[] args) throws Exception{ Lista_Fornecedores fornec = new Lista_Fornecedores();

fornec.lista_fornec();}

}

O método getString é chamado para recuperar o valor armazenado na coluna Nome nalinha corrente do objeto rs. O valor que getString recupera é convertido de umVARCHAR2 SQL para um String da linguagem Java e é atribuído ao objeto String s.

JDBC também permite que se recupere o valor de uma coluna através do seu número:

String s = rs.getString(2);

Observe que o número da coluna se refere ao número da coluna no result set, e não natabela original. Usar o número da coluna é ligeiramente mais eficiente.

JDBC permite que vários métodos getXXX sejam utilizados para recuperar certos tiposde dados SQL. Por exemplo, o método getInt pode ser utilizado para recuperar qualquervalor SQL numérico ou do tipo caracter. O dado recuperado será convertido para umnúmero int. Isto é, JDBC tentará converter um varchar em int. O método getInt deve serutilizado para recuperar apenas tipos SQL INTEGER, no entanto, não pode ser utilizadopara os tipos SQL BINARY, VARBINARY, LONGBINARY, DATE, TIME, ouTIMESTAMP.

A tabela abaixo mostra que métodos podem ser legalmente utilizados para recuperar osvários tipos de dados SQL e quais métodos são recomendados.

Page 68: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 68

Page 69: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 69

Utilizando o Método getString

Embora o método getString possa seja recomendado para recuperar os tipos de dadosSQL CHAR e VARCHAR, é possível recuperar qualquer tipo de dado SQL básico. Vocênão pode, no entanto, recuperar os novos tipos de dados SQL3 com ele. Recuperarvalores com getString pode ser muito útil, mas possui limitações. Se for utilizado pararecuperar um tipo numérico, getString converterá o tipo numérico para um objeto StringJava e o valor terá de ser convertido de volta para numérico antes de ser manipuladocomo um número. Logo, se você desejar que uma aplicação recupere valores de qualquertipo SQL padrão (exceto os tipos SQL3), utilize o método getString.

Atualizando Tabelas

Suponha que após uma semana de trabalho, o proprietário da loja “The Coffee Break”queira informar, para cada tipo de café, a quantidade (em kilos) vendida na semana.

O comando SQL que atualiza uma linha é:

String updateString = “UPDATE PRODUTOS SET QTD_SEMANA = 75 “ + “WHERE NUMERO = 101”;

Utilizando o objeto Statement (stmt) o código abaixo executa o comando SQL contido nostring updateString:

stmt.executeUpdate(updateString);

Page 70: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 70

Exemplo:

Para executar este programa é preciso rodar o script CriaCafe.sql existente no diretórioc:\Java\TopicosAvancados\Banco_de_Dados

import java.sql.*;public class Atualiza_Cafe{ public void atualizaQtdCafe() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement();

// Também poderia substituir por: String setenta_e_Cinco = “75”;String updateString = “UPDATE PRODUTOS SET QTD_SEMANA = 75 “ +

“WHERE NUMERO = 101”;stmt.executeUpdate(updateString);

String query = “SELECT * FROM PRODUTOS”;ResultSet rs = stmt.executeQuery(query);

System.out.println ("Numero Qtd Vendida");while (rs.next()){ int numero = rs.getInt("NUMERO");

int qtdSemana = rs.getInt ("QTD_SEMANA");System.out.println (numero+" "+qtdSemana);

}}

public static void main (String[] args) throws Exception{ Atualiza_Cafe cafe = new Atualiza_Cafe();

cafe.atualizaQtdCafe();}

}

Page 71: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 71

Utilizando Prepared Statement

Se você deseja executar um objeto Statement muitas vezes, geralmente o tempo deexecução será reduzido utilizando-se o objeto PreparedStatement, em vez de Statement.A vantagem de se utilizar o objeto PreparedStatement é que o comando SQL será enviadodo SGBD onde ele será compilado. Conseqüentemente, o objeto PreparedStatementcontém não apenas um comando SQL mas um comando SQL que foi previamente pré-compilado. Isto significa que quando o comando PreparedStatement é executado, oSGBD pode apenas executar o comando SQL PreparedStatement, sem ter que compilá-lo.

Embora objetos PreparedStatement possam ser utilizados para comandos SQL semparâmetros, você provavelmente os utilizará mais freqüentemente para comandos SQLcom parâmetros. A vantagem de se utilizar comandos SQL com parâmetros é que vocêpode utilizar o mesmo comando fornecendo valores diferentes a cada execução.

Criando um objeto PreparedStatement

Assim como ocorre com objetos Statement, você cria um objeto PreparedStatement comum objeto connection. Utilizando a nossa conexão aberta (con) dos exemplos anteriores,você poderia escrever código como o que vem a seguir para criar um objetoPreparedStatement que possui dois parâmetros:

String comando = “UPDATE PRODUTOS SET QTD_SEMANA = ? “ + “WHERE NOME = ?”);

PreparedStatement atualizarVendas =con.preparedStatement(comando);

A variável atualizarVendas agora contém o comando SQL “UPDATE PRODUTOS SETQTD_SEMANA = ? WHERE NUMERO = ?”, que foi enviado (na maioria dos casos) aoSGBD para uma pré-compilação.

Fornecendo Valores para Parâmetros de PreparedStatement

Você precisará fornecer valores para serem utilizados no local das interrogações (?), antesde você executar o objeto PreparedStatement. Você efetua esta substituição chamandoum dos métodos setXXX definidos para a classe PreparedStatement. Se o valor que vocêquer substituir é um número inteiro Java, então você chama o método setInt. Se o valorque você deseja substituir é um string Java então você chama o método setString. Existeum método setXXX para cada tipo de dado Java.

Page 72: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 72

Para atualizarmos para 75 kilos, a quantidade vendida na semana do produto 101 seriapreciso o seguinte código:

atualizarVendas.setInt(1, 75);atualizarVendas.setString(2, “CAFE BRASILEIRO”);atualizarVendas.executeUpdate();

Após estes valores terem sido atribuídos a seus dois parâmetros de entrada, o comandoSQL em atualizaVendas será equivalente ao comando SQL no objeto do tipo String(comando) abaixo. Logo, os dois fragmentos de código definidos abaixo são equivalentes(mas não em termos de desempenho, uma vez que o objeto atualizarVendas contém ocomando SQL pré-compilado).

Fragmento de Código 1

String comando = “UPDATE PRODUTOS SET QTD_SEMANA = 75 “ + “WHERE NOME = ‘CAFÉ BRASILEIRO’”);

stmt.executeUpdate(comando);

Fragmento de Código 2

PreparedStatement atualizarVendas = con.preparedStatement(“UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?”);atualizarVendas.setInt(1, 75);atualizarVendas.setString(2, “CAFE BRASILEIRO”);atualizarVendas.executeUpdate();

Utilize o método executeUpdate para executar o objeto Statement (stmt) e o objetoPreparedStatement (atualizarVendas). Note, no entanto, que nenhum argumento éfornecido para executeUpdate quando este método é utilizado para executaratualizarVendas. A razão para isto é que o objeto atualizarVendas já contém o comandoSQL que deve ser executado.

Se o comando do fragmento de código 2 encontra-se dentro de um loop, e portanto seráexecutado várias vezes, é conveniente utilizar o objeto PreparedStatement.

Quando um parâmetro recebe um valor este valor é retido pelo parâmetro até que ele sejasubstituído por outro valor ou até que o método clearParameters seja chamado. Utilizandoo objeto atualizarVendas (do tipo PreparedStatement), o seguinte fragmento de códigoilustra a reutilização de um prepared statement após ressetar o valor de apenas um de seusparâmetros.

Page 73: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 73

PreparedStatement atualizarVendas = con.prepareStatement("UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?");atualizarVendas.setInt(1, 175);atualizarVendas.setString(2, "CAFE BRASILEIRO");atualizarVendas.executeUpdate();

atualizarVendas.setString(2, "CAFE COLOMBIANO");atualizarVendas.executeUpdate();

Exemplo:

Para executar este programa é preciso rodar o script CriaProd.sql existente no diretórioC:\Java\TopicosAvancados\Banco_de_Dados

import java.sql.*;public class Atualiza_Duas_Linhas{ public void atualizaLinhas() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

PreparedStatement atualizarVendas = con.prepareStatement("UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?");atualizarVendas.setInt(1, 175);atualizarVendas.setString(2, "CAFE BRASILEIRO");atualizarVendas.executeUpdate();

atualizarVendas.setString(2, "CAFE COLOMBIANO");atualizarVendas.executeUpdate();

Statement stmt = con.createStatement();String query = "SELECT * FROM PRODUTOS";ResultSet rs = stmt.executeQuery(query);

System.out.println ("Numero Qtd Vendida");while (rs.next()){ int numero = rs.getInt("NUMERO");

int qtdSemana = rs.getInt ("QTD_SEMANA");System.out.println (numero+" "+qtdSemana);

}}

public static void main (String[] args) throws Exception{ Atualiza_Duas_Linhas linha = new Atualiza_Duas_Linhas();

linha.atualizaLinhas();}

}

Page 74: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 74

Utilizando um Loop para Atribuir Valores

Você pode tornar o código mais fácil utilizando um for ou um while para atribuir valorespara parâmetros de entrada:

PreparedStatement atualizarVendas;String stringAtualizacao = "UPDATE PRODUTOS " +

"SET QTD_SEMANA = ? WHERE NOME = ?";atualizarVendas = con.prepareStatement(stringAtualizacao);

int [] vendasDaSemana = {175, 150, 60, 155, 90};String [] cafes = {"CAFE BRASILEIRO", "CAFE COLOMBIANO",

"CAFE FRANCES", "CAFE EXPRESSO", "CAFE COLOMBIANO DESCAFEINADO"};

int comprimento = cafes.length;for (int i = 0; i < comprimento; i++){ atualizarVendas.setInt(1, vendasDaSemana[i]);

atualizarVendas.setString(2, cafes[i]);atualizarVendas.executeUpdate();

}

Observe que em uma aplicação real os dados provavelmente seriam digitados pelousuário.

Códigos de Retorno para o Método executeUpdate

Enquanto executeQuery retorna um objeto ResultSet que contém o resultado da queryenviada ao SGBD, o valor de retorno para executeUpdate é um int que indica quantaslinhas da tabela foram atualizadas. O código abaixo mostra o valor de retorno deexecuteUpdate sendo atribuído à variável n:

atualizarVendas.setInt(1, 50);atualizarVendas.setString(2, "CAFE BRASILEIRO");int n = atualizarVendas.executeUpdate();

// Se n = 1, a linha foi atualizada com sucesso.

Quando o método executeUpdate é utilizado para executar um comando DDL, tal comoum comando CREATE TABLE, ele retorna um int 0.

Se o comando executeUpdate retorna 0, isto pode significar duas coisas: (1) o comandoexecutado foi um update que afetou zero linhas, ou (2) o comando executado foi umcomando DDL.

Page 75: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 75

Exemplo:

Para executar este programa é preciso rodar o script CriaProd.sql existente no diretórioc:\Java\TopicosAvancados\Banco_de_Dados

import java.sql.*;public class Atualiza_Com_Array{ public void atualizaLinhas() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

PreparedStatement atualizarVendas;String stringAtualizacao = "UPDATE PRODUTOS " +

"SET QTD_SEMANA = ? WHERE NOME = ?";atualizarVendas = con.prepareStatement(stringAtualizacao);

int [] vendasDaSemana = {175, 150, 60, 155, 90};String [] cafes = {"CAFE BRASILEIRO", "CAFE COLOMBIANO",

"CAFE FRANCES", "CAFE EXPRESSO", "CAFE COLOMBIANO DESCAFEINADO"};

int comprimento = cafes.length;for (int i = 0; i < comprimento; i++){ atualizarVendas.setInt(1, vendasDaSemana[i]);

atualizarVendas.setString(2, cafes[i]);int n = atualizarVendas.executeUpdate();if n = 0

System.out.println(cafes[i]+" não foi encontrado.");

}

Statement stmt = con.createStatement();String query = "SELECT * FROM PRODUTOS";ResultSet rs = stmt.executeQuery(query);

System.out.println ("Numero Qtd Vendida");while (rs.next()){ int numero = rs.getInt("NUMERO");

int qtdSemana = rs.getInt ("QTD_SEMANA");System.out.println (numero+" "+qtdSemana);

}}

public static void main (String[] args) throws Exception{ Atualiza_Com_Array linha = new Atualiza_Com_Array();

linha.atualizaLinhas();}

}

Page 76: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 76

Utilizando Joins

Exemplo:

import java.sql.*;public class Join{ public void listaJoin() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement();String query = "SELECT F.NOME FNOME, P.NOME PNOME " +

"FROM FORNECEDORES F, PRODUTOS P " + "WHERE F.NUMERO = NUM_FORNECEDOR";

ResultSet rs = stmt.executeQuery(query);

System.out.println ("Nome do Fornecedor Nome do Produto");while (rs.next()){ String fnome = rs.getString("FNOME");

String pnome = rs.getString("PNOME");System.out.println (fnome+" "+pnome);

}}

public static void main (String[] args) throws Exception{ Join joinTabelas = new Join();

joinTabelas.listaJoin();}

}

Page 77: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 77

Utilizando Transações

Toda conexão é criada no modo auto-commit. Isto significa que cada comando SQLindividual é tratado como uma transação. Para ser mais preciso, um comando SQL écomitado, por default, quando ele é concluído, não quando é executado. E um comando éconcluído quando todos os seus resultados e contadores de atualizações são recuperados.Na maioria das situações, no entanto, um comando é concluído, e então comitado, logoapós a sua execução.

A maneira de se permitir que dois ou mais comandos sejam agrupados em uma transaçãoé desabilitando o modo auto-commit.

con.setAutoCommit(false);

Comitando uma Transação

Uma vez tendo-se desabilitado o modo auto-commit, nenhum comando SQL serácomitado até que você chame o método commit explicitamente. O código abaixo ilustrauma transação:

con.setAutoCommit(false);PreparedStatement atualizarVendas = con.prepareStatement("UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?");atualizarVendas.setInt(1, 17);atualizarVendas.setString(2, "CAFE BRASILEIRO");atualizarVendas.executeUpdate();

PreparedStatement atualizarTotal = con.prepareStatement("UPDATE PRODUTOS SET QTD_TOTAL = ? WHERE NOME = ?");atualizarTotal.setInt(1, 17);atualizarTotal.setString(2, "CAFE BRASILEIRO");atualizarTotal.executeUpdate();con.commit();con.setAutoCommit(true);

Neste exemplo, o modo auto-commit é desabilitado para a conexão, o que significa quedois prepared statements atualizarVendas e atualizarTotal serão commitados quando ométodo commit é chamado.

Para evitar conflitos ao longo de uma transação, os SGBD utilizam um mecanismo delock para bloquear o acesso por outros usuários ao dado que está sendo atualizado poruma transação. Uma vez colocado um lock em uma linha de uma tabela, ele permanecerálá até que a transação seja comitada o sofra um rollback. O efeito deste lock evita que umusuário leia uma informação suja, isto é, evita que o usuário leia uma informação que nãofoi comitada e que, portanto, pode sofrer um rollback. Se você consegue ler um valor

Page 78: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 78

alterado mas ainda não comitado, e este valor sofre um rollback, você leu um valorinválido.

O efeito de um lock no acesso a um dado é determinado pelo Transaction Isolation Level,que varia desde “sem suporte a transações” a “suportando transações com regrasrigorosas a respeito”. Um exemplo de um transaction Isolation level éTRANSACTION_READ_COMMITED, que não vai permitir que um valor seja acessadoaté que ele seja comitado. Em outras palavras, se o transaction isolation level é designadopara TRANSACTION_READ_COMMITED, o SGBD não permitirá que leituras sujassejam efetuadas. A interface Connection inclui 5 valores que representam os níveis deisolamento que você pode utilizar com JDBC.

Normalmente você não necessita fazer nada a respeito do transaction isolation level.Você pode apenas utilizar o default para seu SGBD. JDBC permite que você descubraqual o nível de isolamento que seu SGBD está utilizando, por default. Para fazer istoutilize o método getTransactionIsolation da classe Connection. JDBC também nospermite trocar o nível de isolamento com o método setIsolationLevel da classeConnection. No entanto, para que estas modificações tenham efeito, é preciso que odriver que você está utilizando dê suporte a níveis de isolamento.

Quando Chamar o Método rollback

Se você está tentando executar um ou mais comandos DML em uma transação e recebeuma SQLException, você deveria chamar o método rollback para abortar a transação. Aocapturar uma exceção SQLException você fica sabendo que algo saiu errado, daí anecessidade de efetuar o rollback.

Page 79: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 79

import java.sql.*;public class Transacoes{ public void atualizaLinha() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

con.setAutoCommit(false);PreparedStatement atualizarVendas = con.prepareStatement("UPDATE PRODUTOS SET QTD_SEMANA = ? WHERE NOME = ?");atualizarVendas.setInt(1, 17);atualizarVendas.setString(2, "CAFE BRASILEIRO");atualizarVendas.executeUpdate();

PreparedStatement atualizarTotal = con.prepareStatement("UPDATE PRODUTOS SET QTD_TOTAL = ? WHERE NOME = ?");atualizarTotal.setInt(1, 17);atualizarTotal.setString(2, "CAFE BRASILEIRO");atualizarTotal.executeUpdate();con.commit();con.setAutoCommit(true);

Statement stmt = con.createStatement();String query = "SELECT * FROM PRODUTOS";ResultSet rs = stmt.executeQuery(query);

System.out.println ("Numero Qtd Semana Qtd Total");while (rs.next()){ int numero = rs.getInt("NUMERO");

int qtdSemana = rs.getInt ("QTD_SEMANA");int qtdTotal = rs.getInt ("QTD_TOTAL");System.out.println (numero+" "+qtdSemana+" "+qtdTotal);

}}

public static void main (String[] args) throws Exception{ Transacoes linha = new Transacoes();

linha.atualizaLinha();}

}

Page 80: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 80

Stored Procedures

É possível criar uma stored procedure através de um programa java, assim:

import java.sql.*;public class CriaStoredProcedure{ public void criaProcedure() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement();

String createProcedure = “CREATE OR REPLACE PROCEDURE ... “;stmt.executeUpdate(createProcedure);

}

public static void main (String[] args) throws Exception{ CriaStoredProcedure procedure = new CriaStoredProcedure();

procedure.criaProcedure();}

}

Chamando uma Stored Procedure

O primeiro passo é criar um objeto CallableStatement. Assim como objetos Statement ePreparedStatement, isto é feito com o objeto Connection aberto. Um objetoCallableStatement contém uma chamada a uma stored procedure. Ele não contém a storedprocedure, mas apenas a chamada. A primeira linha de código abaixo, cria uma chamadaà stored procedure utilizando a conexão con. Quando o driver encontra“{call Nome_da_Stored_Procedure}”, traduz esta chamada para o comando SQL nativoutilizado pelo SGBD para chamar stored procedures.

import java.sql.*;public class ExecutaStoredProcedure{ public void executaProcedure() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

CallableStatement cs = con.prepareCall("{call Remover_Produto}");cs.execute();

}

public static void main (String[] args) throws Exception{ ExecutaStoredProcedure procedure = new ExecutaStoredProcedure();

procedure.executaProcedure();}

}

Page 81: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 81

Alguns SGBDs, ao executarem uma stored procedure, conseguem retornar um conjuntode dados. Neste caso executaríamos esta stored procedure assim:

CallableStatement cs = con.prepareCall("{call Recupera_Produtos}");ResultSet rs = cs.executeQuery();

Por outro lado, se a Stored Procedure executa apenas um comando DDL ou DMLdeveríamos executá-la assim:

CallableStatement cs = con.prepareCall("{call Remover_Produto}");cs.executeUpdate();

E se a stored procedure executa vários comandos DML e retorna ainda um conjunto dedados deveríamos executá-la assim:

CallableStatement cs = con.prepareCall("{call Remover_Produto}");cs.execute();

No caso do Oracle, que não é capaz de retornar um conjunto de dados em uma storedprocedure, podemos executar todas as stored procedures assim:

CallableStatement cs = con.prepareCall("{call Remover_Produto}");cs.execute();

A classe CallableStatement é uma subclasse de PreparedStatement, logo um objetoCallableStatement pode receber parâmetros, assim como objetos do tipoPreparedStatement. Além disso, um objeto CallableStatement pode possuir parâmetros desaída, e parâmetros de entrada e de saída. Parâmetros INOUT e o método execute sãoraramente utilizados.

Capturando Exceções

No exemplo abaixo utilizamos dois blocos try e dois blocos catch. O primeiro bloco trycontém o método Class.forName, do package java.lang. Este método gera uma exceçãodo tipo ClassNotFoundException, e o bloco catch abaixo trata esta exceção. O segundobloco try contém métodos JDBC, que geram exceções do tipo SQLException, logo umbloco catch no final da aplicação pode tratar todas estas exceções, porque todas serãoexceções do mesmo tipo.

Page 82: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 82

Recuperando Exceções

JDBC nos permite ver exceções e warnings gerados pelo seu SGBD e pelo runtime java.

Exemplo:

try{ // Código que eventualmente pode gerar uma exceção.}catch (SQLException ex){ System.err.println (“SQLException: “ + ex.getMessage());}

try{ Class.forName(“myDriverClassName”);}catch (java.lang.ClassNotFoundException e){ System.err.print (“ClassNotFoundException: “);

System.err.println (e.getMessage());}

Este exemplo ilustra como imprimir o componente mensagem de um objetoSQLException, que é suficiente na maioria dos casos.

Existem, no entanto, três componentes que podem ser impressos: a mensagem (um stringque descreve a mensagem de erro), o SQL state (um string que identifica o erro de acordocom a CONVENÇÃO x/Open SQLState), e o código de erro do fabricante do SGBD). Oobjeto exception (e) é capturado, e seus três componentes são acessados com os métodosgetMessage, getSQLState, e getErrorCode.

O bloco catch abaixo captura mais de uma exceção, caso ocorra. Se existir uma segundaexceção ela será concatenada à primeira (e), logo, e.getNextException pode ser chamadopara se verificar se há mais alguma exceção. Se existe outra exceção o loop continua eimprime a próxima mensagem de erro. O loop continua até que não haja mais exceções.

Page 83: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 83

try{ // Código que pode gerar uma ou mais exceções.}catch (SQLException ex){ System.out.print (“\n--- SQLException Capturada ---\n“);

while (ex != null){ System.out.print (“Message: “ + ex.getMessage());

System.out.print (“SQLState: “ + ex.getSQLState());System.out.print (“ErrorCode: “ + ex.getErrorCode());System.out.print (““);ex = ex.getNextException();

}}

SQLState é um código definido em X/Open e ANSI-92 que identifica a exceção. Doisexemplos de SQLState são:

8001 -- No suitable driverHY011 -- Operation invalid at this time

Já o código de erro do fabricante do SGBD é depende do driver.

Recuperando Warnings

Objetos do tipo SQLWarnings são uma subclasse de SQLException e tratam de warningsno acesso a bancos de dados. Warnings não param a execução de uma aplicação, como asexceções. Eles simplesmente alertam o usuário de que algo inesperado aconteceu. Porexemplo, um warning pode lhe avisar que um privilégio que você tentou revogar não foirevogado. Ou um warning pode lhe avisar que um erro ocorreu no momento dadesconexão solicitada.

Um warning pode ser reportado por um objeto Connection, Statement (incluindoPreparedStatement e CallableStatement), ou ResultSet. Cada uma destas classes possuium método getWarnings, que deve ser invocado para ver o primeiro warning reportadopara o objeto em questão. Se um getWarnings retorna um warning você pode chamar ométodo getNextWarning para recuperar warnings adicionais. Isto significa, no entanto,que se você deseja capturar um warning gerado para um statement, você deve capturá-loantes de executar o próximo statement.

Page 84: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 84

Exemplo:

Statement stmt = con.createStatement();ResultSet rs = stmt.executeQuery (“select * from produtos”);While (rs.next()){ String nome = rs.getString(“NOME”);

System.out.print (“Café disponivel: “ + nome);SQLWarning warning = stmt.getWarnings();if (warning != null){ while (warning != null)

{ System.out.print (“Message:“ + warning.getMessage());System.out.print(“SQLState:“ + warning.getSQLState());System.out.print(“ErrorCode:“+warning.getErrorCode());System.out.print (““);warning = warning.getNextException();

}}SQLWarning warn = rs.getWarnings();if (warn != null){ while (warn != null)

{ System.out.print (“Message: “ + warn.getMessage());System.out.print (“SQLState: “ + warn.getSQLState());System.out.print (“ErrorCode:“ + warn.getErrorCode());System.out.print (““);warn = warn.getNextException();

}}

}

O tratamento de warnings não é comum. O warning mais comum é o do tipoDataTruncation, uma subclasse de SQLWarning. Todos os objetos do tipoDataTruncation possuem um SQLState = 01004. Métodos de DataTruncation nospermitem descobrir qual coluna ou parâmetro foi truncada, se o truncamento ocorreunuma operação de leitura ou de escrita, quantos bytes deveriam ter sido transferidos equantos foram efetivamente transferidos.

Page 85: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 85

JDBC 2.0

O package java.sql que faz parte do JDK 1.2 (conhecido como JDBC 2.0 API) incluivárias características novas não incluídas no package java.sql que faz parte do JDK 1.1(conhecido como JDBC 1.0 API). Todos os exemplos de código apresentados até aquiforam escritos utilizando a API JDBC 1.0.

Com a API JDBC 2.0 será possível:

1. Navegar para frente e para trás em um result set, ou mover-se para uma linhaespecífica.

2. Fazer atualizações em tabelas do banco utilizando métodos da linguagem Java em vezde utilizar SQL.

3. Enviar vários comandos SQL para o banco como uma unidade, ou batch.4. Utilizar os novos tipos de dados SQL3 como valores de colunas.

Até o momento que este material foi escrito (junho de 99) não havia nenhum driverimplementado com as características aqui apresentadas, no entanto, vários driversestavam em desenvolvimento.

Navegando em um Cursor em Ambas as Direções

Scrollable Result Sets tornam possível a criação de aplicações gráficas com grids para aexibição de result sets. Outra possibilidade é a navegação no ResultSet para atualizaçãode dados.

O código abaixo mostra como criar um objeto ResultSet scrollable:

Statement stmt = com.CreateStatement (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);ResultSet srs = stmt.executeQuery(“SELECT NOME, PRECO FROM PRODUTOS”);

Observe que este código adiciona dois argumentos ao método CreateStatement. Oprimeiro argumento é uma das três constantes adicionadas ao ResultSet API para indicaro tipo do objeto ResultSet:

• TYPE_FORWARD_ONLY - É possível navegar apenas para frente, como com API JDBC 1.0.

• TYPE_SCROLL_INSENSITIVE - É possível navegar em ambas as direções. O result set reflete as modificações nele efetuadas enquanto ele está aberto.

• TYPE_SCROLL_SENSITIVE - É possível navegar em ambas as direções. O result set não reflete as modificações nele efetuadas enquanto ele está aberto.

Page 86: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 86

O segundo argumento é uma das duas constantes que indicam se o ResultSet é read-onlyou pode ser atualizado. Estas variáveis são: CONCUR_READ_ONLY e CONCUR_UPDATE. Épreciso especificar os dois parâmetros, no entanto, como ambos são numéricos é precisotomar cuidado para não inverter a ordem, uma vez que o compilador não vai detectar oerro.

Se você não especificar nenhuma constante para o tipo de navegação e possibilidade deatualização, o default é: TYPE_FORWARD_ONLY e CONCUR_READ_ONLY. (Como acontecequando se utiliza a API JDBC 1.0).

Especificando-se TYPE_SCROLL_INSENSITIVE ou TYPE_SCROLL_SENSITIVE você obtémum objeto ResultSet scrollable. A diferença entre eles tem a ver com o fato do result setrefletir ou não as modificações nele efetuadas enquanto ele está aberto e se certosmétodos podem ser chamados para detectar estas modificações. Todos os tipos de resultset tornarão as modificações visíveis se forem fechados e reabertos. Você deve ter emmente que independentemente do tipo de result set que você venha a escolher, ofuncionamento deste result set estará limitado pelo que o seu SGBD e Driver provêem.

Quando você cria um novo objeto do tipo ResultSet, o cursor é inicialmente posicionadoantes do primeira linha. Com um scrollable result set é possível utilizar os métodos next(assim como com a API JDBC 1.0) e o método previous. Ambos estes métodos retornamfalse quando o cursor vai além do result set. (posição após o último ou antes do primeiro).

Page 87: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 87

Exemplo:

import java.sql.*;public class TiposDeResultSet{ public void lista() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);

String query = "SELECT NOME " + "FROM FORNECEDORES";

ResultSet rs = stmt.executeQuery(query);

System.out.println ("Nome do Fornecedor");while (rs.next()){ String fnome = rs.getString("NOME");

System.out.println (fnome);}

}

public static void main (String[] args) throws Exception{ TiposDeResultSet tipos = new TiposDeResultSet();

tipos.lista();}

}

E para processar os dados de trás para frente:

import java.sql.*;public class TiposDeResultSet{ public void lista() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

ResultSet rs = stmt.executeQuery("SELECT NOME FROM FORNECEDORES");rs.afterLast();System.out.println ("Nome do Fornecedor");while (rs.previous()){ String fnome = rs.getString("NOME");

System.out.println (fnome);}

}

public static void main (String[] args) throws Exception{ TiposDeResultSet tipos = new TiposDeResultSet();

tipos.lista();}

}

Page 88: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 88

É possível mover o cursor para uma linha específica:

rs.absolute(4); // Move o cursor para a quarta linha.

Se o cursor tem 500 linhas a linha de código abaixo move o cursor para a linha 497:

rs.absolute(-4);

Com o método relative você pode especificar quantas linhas você deseja mover o cursor eem que direção:

rs.absolute(4); // Move o cursor para a quarta linha.. . .rs.relative(-3); // Move o cursor para a primeira linha.. . .rs.relative(2); // Move o cursor para a terceira linha.

O método getRow nos permite verificar em que linha o cursor encontra-se posicionado.

rs.absolute(4); // Move o cursor para a quarta linha.int numeroLinha = rs.getRow(); // numeroLinha deveria ser 4.rs.relative(-3); int numeroLinha = rs.getRow(); // numeroLinha deveria ser 1.rs.relative(2); int numeroLinha = rs.getRow(); // numeroLinha deveria ser 3.

Quatro métodos adicionais nos permitem verificar se o cursor se encontra em umaposição específica: isFirst, isLast, isBeforeFirst e isAfterLast. O código abaixotesta se o cursor está posicionado após a última linha. Caso não esteja movemos o cursorpara esta posição:

if (rs.isAfterLast() == false){ rs.afterLast();}while (rs.previous()){ String fnome = rs.getString("NOME");

System.out.println (fnome);}

Nas duas próximas seções você verá como utilizar os dois métodos moveToInsertRow emoveToCurrentRow.

Page 89: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 89

Atualizando Result Sets

É preciso criar uma Result Set atualizável. Para fazer isso forneça a constanteCONCUR_UPDATABLE da classe ResultSet para o método createStatement. O objetoStatement criado irá produzir um objeto do tipo ResultSet atualizável cada vez que umaquery for executada. O código abaixo cria um result set atualizável e scrollable. Com umscrollable result set você pode navegar pelas linhas que deseja modificar e se o tipo forTYPE_SCROLL_SENSITIVE, você poderá recuperar o novo valor em uma linha do cursor,após tê-lo atualizado.

import java.sql.*;public class TiposDeResultSet{ public void lista() throws Exception

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

ResultSet rs = stmt.executeQuery("SELECT * FROM FORNECEDORES");

// Agora você pode utilizar novos métodos JDBC 2.0 da// interface ResultSet para inserir uma nova linha em rs,// deletar uma linha de rs, ou modificaro valor de uma// coluna de rs.

. . .

Atualizando um Result Set Programaticamente

Uma modificação é uma alteração de um valor de uma coluna na linha corrente. Vamossupor que desejamos aumentar o preço de um produto. Com JDBC 1.0 fazemos:

stmt.executeUpdate(“UPDATE PRODUTOS “ + “SET PRECO = 10.99 “ +

“WHERE NUMERO = 101”);

Utilizando a API JDBC 2.0 podemos escrever:

rs.first();rs.updateFloat(“PRECO”, 10.99);

Uma operação de update em JDBC 2.0 afeta a linha corrente do cursor. Existe um métodoupdateXXX para cada tipo de dado. updateString, updateBigDecimal, updateInt, etc.

Até este momento, o preço do produto número 101 é 10.99 em rs, mas o preço na tabelaPRODUTOS ainda não foi alterado. Para que esta atualização seja efetuada no banco e

Page 90: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 90

não apenas no result set é preciso chamar o método updateRow da interface ResultSet,conforme vem abaixo:

rs.first();rs.updateFloat(“PRECO”, 10.99);rs.updateRow();

Se você tivesse movido o cursor para outra linha antes de executar rs.updateRow(),a alteração teria sido perdida.

Após executar os comandos:

rs.first();rs.updateFloat(“PRECO”, 10.99);

é possível cancelar a alteração chamando o método cancelRowUpdates. Este métodocancela todas as modificações efetadas em uma linha e ainda não confirmadas através ders.updateRow().

rs.first();rs.updateFloat(“PRECO”, 10.99);rs.cancelRowUpdates();rs.updateFloat(“PRECO”, 11.99);rs.updateRow();

Todas as movimentações em um cursor fazem referência a linhas em um objeto result set,e não a linhas da tabela no banco de dados. Se uma query seleciona cinco linhas de umatabela de banco de dados, existirão 5 linhas no result set. A ordem das linhas no result setnão tem nada a ver com a ordem das linhas na tabela. De fato, a ordem das linhas natabela é indeterminada. O SGBD acompanha que linha foi selecionada e faz asatualizações nas linhas apropriadas, que podem ser encontradas em qualquer local databela. Quando uma linha é inserida, por exemplo, não há como saber onde na tabela elafoi inserida.

Inserindo e Deletando Linhas Programaticamente

Como inserir uma linha com a API JDBC 1.0:

Stmt.executeUpdate(“INSERT INTO PRODUTOS “ + “VALUES (107, ‘CAFÉ BOLIVIANO’, 1003, 10.44, 0, 0)”;

Como inserir uma linha com a API JDBC 2.0:

É possível inserir uma nova linha em um result set e na tabela correspondente em umúnico passo. Você constrói uma nova linha em algo denominado “the insert row”,uma linha especial associada a todo objeto ResultSet. Esta linha não faz realmente parte

Page 91: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 91

do result set. Você pode pensar nela como um buffer separado no qual uma nova linha écomposta.

O primeiro passo será mover o cursor para a insert row, chamando-se o métodomoveToInsertRow. O próximo passo é designar um valor para cada coluna na linha. Istodeve ser feito chamando-se os métodos updateXXX para cada valor. E fianlmente chama-se o método insertRow. Este método simultaneamente insere a linha no result set e natabela a partir da qual o result set foi montado.

O código abaixo cria um objeto ResultSet scrollable e atualizável que contém todas aslinhas e colunas da tabela produtos.

Connection con =DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt =con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,

ResultSet.CONCUR_UPDATABLE);ResultSet rs = stmt.executeQuery("SELECT * FROM FORNECEDORES");

E o fragmento de código abaixo, utiliza o objeto ResultSet (rs) para inserir um novoproduto.

rs.moveToInsertRow();rs.updateInt(“NUMERO”, 107);rs.updateString(“NOME”, “CAFÉ BOLIVIANO”);rs.updateInt(“NUM_FORNECEDOR”, 1003);rs.updateFloat(“PRECO”, 10.44);rs.updateFloat(“QTD_SEMANA”, 0);rs.updateFloat(“QTD_TOTAL”, 0);rs.insertRow(); // Insere no result set e no banco ao mesmo tempo.

Como você pode utilizar o número da coluna em vez de seu nome, poderíamos fazer oseguinte:

rs.updateInt(1, 107);rs.updateString(2, “CAFÉ BOLIVIANO”);. . .

Após chamar o método insertRow, você pode começar a construir outra linha para serinserida, ou você pode mover o cursor de volta para uma linha do result set. Para por ocursor em uma linha específica você pode utilizar os métodos: first, last, beforeFirst,afterLast, e absolute. Você também pode utilizar os métodos previous, relative emoveToCursorRow. Você pode chamar o método moveToCurrentRow apenas quando ocursor está na insert row.

Quando você chama o método moveToInsertRow, o resultset registra qual é a linhacorrente. Consequentemente, o método moveToCurrentRow pode mover o cursor da

Page 92: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 92

insert row para a linha que era a corrente antes do método moveToInsertRow serchamado.

O result set rs acima é atualizável, scrollable, e sensível a modificações efetuadas por elepróprio e por outros. Embora seu tipo seja TYPE_SCROLL_SENSITIVE, é possível que ummétodo getXXX chamado após a inserção de uma linha não recupere valores para a novalinha inserida. Existem métodos da interface DatabaseMetaData que nos indicarão o queé visível e o que é detectado nos diferentes tipos de result set para o seu driver e SGBD.Estes métodos são discutidos em detalhe em JDBC Database Access with Java, mas elesestão fora do escopo deste curso.

Page 93: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 93

import java.sql.*;public class InsereLinha{ public static void main (Strings args[])

{ try{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");}catch (java.lang.ClassNotFoundException e){ System.err.print(“ClassNotFoundException: “);System.err.println(e.getMessage());}try{ Connection con =

DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement

(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

ResultSet rs = stmt.executeQuery("SELECT NOME FROM PRODUTOS");

rs.moveToInsertRow();rs.updateInt(“NUMERO”, 107);rs.updateString(“NOME”, “CAFÉ BOLIVIANO”);rs.updateInt(“NUM_FORNECEDOR”, 1003);rs.updateFloat(“PRECO”, 10.44);rs.updateFloat(“QTD_SEMANA”, 0);rs.updateFloat(“QTD_TOTAL”, 0);rs.insertRow(); // Insere no result set e no banco

// ao mesmo tempo.rs.updateInt(“NUMERO”, 108);rs.updateString(“NOME”, “CAFÉ PILAO”);rs.updateInt(“NUM_FORNECEDOR”, 1001);rs.updateFloat(“PRECO”, 7.48);rs.updateFloat(“QTD_SEMANA”, 0);rs.updateFloat(“QTD_TOTAL”, 0);rs.insertRow(); // Insere uma Segunda linha

rs.beforeFirst();System.out.println ("Nome do Fornecedor");while (rs.previous()){ String fnome = rs.getString("NOME");

System.out.println (fnome);}rs.close();stmt.close();con.close();

}catch (SQLException ex)

{ System.err.println(“SQLexception: “ + ex.getMessage());}

}}

Page 94: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 94

Deletando uma Linha

Para deletar uma linha basta mover o cursor para a linha que se deseja remover, paraentão, chamar o método deleteRow. Por exemplo:

rs.absolute(4);rs.deleteRow();

Assim, a quarta linha é removida do result set e da tabela no banco de dados.

Alguns drivers JDBC removem a linha deletada do result set. Outros drivers JDBCutilizam uma linha em branco para demonstrar que ali havia uma linha que foi removida.Desta forma, ainda é possível utilizar o método absolute para posicionar-se em uma linhaespecífica. Isto é, o número absoluto de uma linha não muda com a deleção de uma linhaanterior.

Lembre-se que os drivers funcionam de maneira diferente. Se você pretende escrever umaaplicação para rodar com diferentes bancos de dados, você não deveria escrever códigoque dependa da existência de uma linha em branco no local de uma linha deletada.

Vendo Modificações em Result Sets

Se você ou qualquer outro usuário modifica os dados de um result set, a modificaçãoefetuada será sempre visível se você fechar e então reabrir o result set. Em outraspalavras, se você executar novamente a query que criou o result set.

A questão é se você verá as modificações efetuadas por você e por outros usuáriosenquanto o result set ainda está aberto. (Geralmente você está mais interessado nasmodificações efetuadas pelos outros usuários). A resposta a esta questão depende doSGBD, do driver, e do tipo de objeto ResultSet que você possui.

Com um objeto ResultSet do tipo TYPE_SCROLL_SENSITIVE, você pode sempre ver asatualizações feitas por quem quer que seja. Você geralmente pode ver inserções edeleções, mas a única maneira de se ter certeza é utilizando os métodos da interfaceDatabaseMetaData que retornam esta informação.

Você pode, até certo ponto regular que modificações são visíveis aumentando oudiminuindo o transaction isolation level para a sua conexão com o banco de dados. Porexemplo, a seguinte linha de código (onde con é um objeto de uma conexão ativa)designa TRANSACTION_READ_COMMITTED para o isolation level da conexão.

con.setTransactionIsolation(TRANSACTION_READ_COMMITTED);

Page 95: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 95

Com este nível de isolamento, o seu objeto ResultSet não mostrará nenhuma modificaçãoantes que elas sejam comitadas.

Em um result set do tipo TYPE_SCROLL_INSENSITIVE, você geralmente não pode ver asmodificações efetuadas no result set enquanto ele está aberto. Este tipo de result set deveser utilizado quando o programador deseja trabalhar com uma visão consistente dosdados e, portanto, não deseja ver as modificações efetuadas por terceiros.

Você pode utilizar o método refreshRow para obter os últimos valores para uma linhadiretamente do banco de dados. Este método pode ser muito custoso especialmente se obanco de dados retorna várias linhas quando o refresh é executado.

Mesmo quando um result set é sensitive e as modificações são visíveis, uma aplicaçãopode nem sempre ver a última modificação efetuada em determinada linha, se o driverrecupera várias linhas de cada vez e faz um cache delas. Neste caso, a utilização dométodo refreshRow é a única maneira de se ter certeza que você está vendo a versãomais atualizada do dado.

Para utilizar o método refreshRow, o result set deve ser sensitive, caso contrário nadaacontecerá. Um exemplo da necessidade de um refresh seria, por exemplo, para verificarse o assento que você está reservando (em uma passagem aérea) ainda está disponível.

import java.sql.*;public class VerificaPreco{ public static void main (Strings args[])

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =

DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,

ResultSet.CONCUR_UPDATABLE);ResultSet rs = stmt.executeQuery("SELECT * FROM PRODUTOS");

rs.absolute(4);Float preco1 = rs.getFloat(“PRECO”);// faz algo . . .

rs.absolute(4);

rs.refreshRow();Float preco2 = rs.getFloat(“PRECO”);if (preco2 > preco1){ // Faz algo . . .}

Page 96: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 96

Fazendo Atualizações em Modo Batch

Um Batch Update é um conjunto de múltiplos comandos UPDATE que são submetidosao banco para processamento na forma batch. Enviar vários comandos UPDATE para obanco de dados, como uma unidade, pode em algumas situações, ser muito mais eficientedo que enviar cada comando UPDATE separadamente. Esta é uma das característicasprovidas pela API JDBC 2.0.

Com a API JDBC 1.0 você pode submeter atualizações ao banco de dadosindividualmente com o método executeUpdate. Múltiplos comandos executeUpdatepodem ser enviados em uma mesma transação, mas embora sejam comitados ou sofreramrollback como uma unidade, eles ainda são processados individualmente.

Com a API JDBC 2.0, objetos Statement, PreparedStatement e CallableStatementpossuem a habilidade de manter uma lista dos comandos que devem ser submetidosjuntos como um batch. Eles são criados com uma lista associada que está inicialmentevazia. Você adiciona comandos SQL a esta lista com o método addBatch e pode esvaziá-la com o método clearBatch. Para enviar todos os comandos para o banco de dados, deveser utilizado o método executeBatch.

Page 97: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 97

Exemplo:

import java.sql.*;public class VerificaPreco{ public static void main (Strings args[])

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");Connection con =

DriverManager.getConnection("jdbc:odbc:Oracle","Scott","Tiger");

con.setAutoCommit(false); // Desabilita o auto-commit// mode para que a transação não seja automaticamente// comitada ou rolbecada quando o método executeBatch// é chamado. Para permitir o correto tratamento de// erros, você deve sempre desabilitar o auto-commit// mode antes de começar um batch update.

Statement stmt = con.createStatement(); // Esta linha cria// um objeto Statement (que como todo objeto// Statement) possui uma lista de comandos associada a// ele. Inicialmente esta lista encontra-se vazia.

smt.addBatch(“INSERT INTO PRODUTOS “ + “VALUES(109,‘AMARETO’,1003, 9.99, 0, 0)”);

smt.addBatch(“INSERT INTO PRODUTOS “ + “VALUES(110,‘HAZELNUT’,1003, 9.99, 0, 0)”);

smt.addBatch(“INSERT INTO PRODUTOS “ + “VALUES(111,‘AMARETO DESCAF.’,1003, 9.99, 0, 0)”);

smt.addBatch(“INSERT INTO PRODUTOS “ + “VALUES(112,‘ HAZELNUT DESCAF.’,1003, 9.99, 0, 0)”);

// Cada uma das linhas de código acima adiciona um// comando à lista de comandos do objeto stmt.

int [] contador = stmt.executeBatch();

/* Nesta linha stmt envia os quatro comandos adicionados àsua lista de comandos para serem executados no banco de dados comoum batch. Note que stmt utiliza o método executeBatch para enviaras inserções para o banco, e não o método executeUpdate, que enviaapenas um comando e retorna um único contador de atualizações. Obanco de dados executará os comandos na mesma ordem em que foramadicionados à lista e retornará um contador de atualizações paracada comando da lista. Estes contadores serão armazenados no arrayde inteiros denominado contador. Neste caso, os contadores valerão1 uma vez que estas inserções afetam uma única linha. Agora alista de comandos é esvaziada uma vez que os quatro comandosadicionados previamente enviados para execução no banco de dados.A qualquer momento esta lista pode ser esvaziada com o métodoclearBatch. */

Page 98: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 98

Tratamento de Exceções em Batch Update

Há duas exceções que podem ser geradas durante uma operação de Batch Update:SQLException e BatchUpdateException.

Todos os métodos da API JDBC geram exceções do tipo SQLException (através de umobjeto deste tipo) quando ocorre um problema no acesso ao banco de dados. O métodoexecuteBatch gerará uma exceção do tipo SQLException para adicionar um comandoque retorna um result set, isto é, se você adicionar um SELECT, ou alguns dos métodosda interface DatabaseMetaData que também retornam um result set. A exceção ocorrequando o comando que retorna um result set é executado, e não quando ele é adicionado àlista de comandos.

Quando cada comando é executado ele deve retornar um contador de atualização quepode ser adicionado a um array de contadores de atualização. A tentativa de colocar umresult set em um array de contadores de atualização causará um erro e fará com queexecuteBatch gere uma exceção do tipo SQLException. Em outras palavras, apenascomandos que retornam um contador de atualização (INSERT, DELETE, UPDATE, CREATETABLE, DROP TABLE, ALTER TABLE, etc) podem ser executados como um batch com ocomando executeBatch.

Se nenhuma SQLException foi gerada, você sabe que não ocorreram problemas de acessoao banco de dados e que todos os comandos produziram contadores de atualização. Se umdos comandos não pôde ser executado por alguma outra razão, o método executeBatchgera uma exceção do tipo BatchUpdateException. Além das informações que todas asexceções possuem, esta exceção contém um array com os contadores de atualização paraos comandos que executaram com sucesso antes da exceção ser gerada. Como oscontadores de atualização estão na mesma ordem dos comandos que os produziram, vocêpode saber quantos comandos foram executados com sucesso e quais são eles.

BatchUpdateException é derivada de SQLException. Isto significa que você pode utilizartodos os métodos disponíveis para um objeto do tipo SQLException. O código abaixoimprime informações de SQLException e os contadores de atualização contidos em umobjeto do tipo BatchUpdateException. Como getUpdateCounts retorna um array de int, éutilizado um for loop para imprimir cada um dos contadores de atualização.

Page 99: Apostila de Java - Aspectos Avancados (1)2[1]

_______________________________________________________________________________________

_______________________________________________________________________________________Apostila de Java – Tópicos Avançados 99

try{ // Faz algumas atualizações}catch (BatchUpdateException b){ System.err.println(“SQLException: “ +b.getMessage());

System.err.println(“SQLState: “ +b.getSQLState());System.err.println(“Message: “ +b.getMessage());System.err.println(“Fabricante do SGBD: “ +b.getErrorCode());System.err.println(“Contadores de Atualização: “);int [] contadoresDeAtualizacao = b.getUpdateCounts();

for (int i = 0; i < contadoresDeAtualizacao.length; i++){ System.err.print(contadoresDeAtualizacao [i] + “ “);}

}