Programação iii volume 3 v23

76
Recife, 2011 Programação III UNIVERSIDADE FEDERAL RURAL DE PERNAMBUCO (UFRPE) COORDENAÇÃO GERAL DE EDUCAÇÃO A DISTÂNCIA (EAD/UFRPE) Fernando Antonio Mota Trinta Volume 3

description

Apostila de Programação III. Direito sobre o conteúdo reservado aos autores da apostila.

Transcript of Programação iii volume 3 v23

Page 1: Programação iii   volume 3 v23

Recife, 2011

Programação III

UNIVERSIDADE FEDERAL RURAL DE PERNAMBUCO (UFRPE)

COORDENAÇÃO GERAL DE EDUCAÇÃO A DISTÂNCIA (EAD/UFRPE)

Fernando Antonio Mota Trinta

Volume 3

Page 2: Programação iii   volume 3 v23

Universidade Federal Rural de Pernambuco

Reitor: Prof. Valmar Corrêa de AndradeVice-Reitor: Prof. Reginaldo BarrosPró-Reitor de Administração: Prof. Francisco Fernando Ramos CarvalhoPró-Reitor de Atividades de Extensão: Prof. Delson LaranjeiraPró-Reitora de Ensino de Graduação: Profª. Maria José de SenaPró-Reitora de Pesquisa e Pós-Graduação: Profª Antonia Sherlânea Chaves VérasPró-Reitor de Planejamento: Prof. Romildo Morant de HolandaPró-Reitor de Gestão Estudantil: Prof. Valberes Bernardo do NascimentoCoordenação Geral de Ensino a Distância: Profª Marizete Silva Santos

Produção Gráfica e EditorialCapa e Editoração: Rafael Lira, Italo Amorim e Everton FelixRevisão Ortográfica: Elias VieiraIlustrações: Allyson Vila NovaCoordenação de Produção: Marizete Silva Santos

Page 3: Programação iii   volume 3 v23

Sumário

Apresentação ................................................................................................................. 5

Conhecendo o Volume 3 ................................................................................................ 6

Capítulo 1 – Uma visão geral sobre bancos de dados ...................................................... 8

1. Introdução ...................................................................................................................8

2. Por que os Bancos de Dados existem? ........................................................................9

3. Arquitetura Geral de um SGBD ..................................................................................12

3.1 Banco de Dados e uma visão em camadas ........................................................13

4. A modelagem e a representação de dados em um SGBD .........................................14

4.1 O Modelo Relacional ..........................................................................................15

4.2 Um modelo relacional para o sistema bancário .................................................17

Capítulo 2 – A linguagem estruturada de consulta: SQL ................................................ 23

1. Introdução .................................................................................................................23

2. Classificação de comandos SQL .................................................................................24

3. Instalação e utilização do HyperSQL ..........................................................................25

4. Comandos DDL ..........................................................................................................27

4.1 Criando tabelas ..................................................................................................27

4.2 Alterando tabelas ...............................................................................................31

4.3 Apagando tabelas ..............................................................................................31

5. Comandos DML e DQL ...............................................................................................32

5.1 Inserindo dados em tabelas ...............................................................................32

5.2 Visualizando dados das tabelas..........................................................................35

5.3 Alterando dados em tabelas ..............................................................................41

5.4 Removendo registros ........................................................................................42

Page 4: Programação iii   volume 3 v23

Capítulo 3 – A especificação JDBC ................................................................................ 47

1. Introdução .................................................................................................................47

2. JDBC ...........................................................................................................................48

3. Configurando eclipse para usar JDBC ........................................................................50

4. Utilizando a API JDBC .................................................................................................55

4.1 Estabelecendo uma conexão com o banco ........................................................55

4.2 Acessando o banco de dados .............................................................................56

4.3 Parametrizando consultas ..................................................................................61

5. Transações com JDBC ................................................................................................63

6. Integrando o Banco com o Sistema de Contas ..........................................................64

Conheça o Autor .......................................................................................................... 76

Page 5: Programação iii   volume 3 v23

5

Apresentação

Caro Cursista,

Seja bem-vindo(a) ao curso de Programação III. Esta disciplina dará seqüência aos estudos iniciados na disciplina de linguagem de programação II, apresentando conceitos avançados relacionados à construção de aplicações orientadas a objetos. Este curso é composto por 4 volumes. No primeiro volume, estudamos a importância da boa organização dos sistemas, por meio dos conceitos de arquitetura de software e padrões de projeto. Em seguida, vimos como podemos criar aplicações multitarefa utilizando threads de threads, para, por fim, aprendermos a gerenciar arquivos em aplicações orientadas a objetos.

O segundo volume foi dedicado ao estudo do conceito de frameworks e à construção da interface do usuário, utilizando recursos avançados como janelas, botões, listas de seleção, e outras metáforas bastante conhecidas das aplicações modernas. Este assunto aborda ainda o chamado modelo de programação orientado a eventos.

Este terceiro volume é totalmente dedicado ao estudo da integração de aplicações com bancos de dados. Para isto, veremos primeiramente conceitos fundamentais sobre bancos de dados, para depois vermos como se dá sua integração com aplicações.

No quarto e último volume, você aprenderá a criar aplicações distribuídas utilizando duas abordagens: a de sockets e a de objetos distribuídos. Em todos estes volumes, re-utilizaremos conceitos aprendidos no curso de linguagem de programação II, e sempre que possível, utilizaremos o estudo de caso desenvolvido anteriormente, o sistema de controle bancário, para apresentar os novos conceitos de cada capítulo.

Portanto, é de fundamental importância que você tenha bem sedimentado os conceitos de orientação a objetos para dar prosseguimento a esta disciplina. No mais, espero que você aproveite este material para aprofundar-se ainda mais sobre como deve-se construir aplicações mais robustas e próximas de cenários reais do desenvolvimento de software na atualidade.

Bons estudos!

Professor Fernando Trinta

Page 6: Programação iii   volume 3 v23

6

Programação III

Conhecendo o Volume 3

Neste terceiro volume, você irá encontrar o Módulo 3 da disciplina Programação III. Para facilitar seus estudos, veja a organização deste terceiro módulo.

Módulo 3 – Uma introdução à integração de programas com bancos de dados

Carga Horária do Módulo 3: 15 h

Objetivo do Módulo 3: Introduzir os conceitos básicos de bancos de dados, sua organização interna de dados e relação com o mundo orientado a objetos. Apresentar a linguagem SQL para manipulação de dados. Apresentar a especificação JDBC para conexão de programas escritos em linguagem Java com bancos de dados.

Conteúdo Programático do Módulo 3

» Introdução a bancos de dados;

» A Linguagem SQL;

» A Especificação JDBC.

Como relatado anteriormente, o terceiro volume desta disciplina é totalmente dedicado à integração de programas como bancos de dados. Este é um assunto muito importante. Praticamente todo sistema de informação é composto por programas que interagem com bases de dados que são gerenciadas por um software específico, o sistema gerenciador de bancos de dados ou SGBD. A importância da manutenção correta destes dados é tanta, que esta é considerada uma área de pesquisa específica dentro da ciência da computação, com disciplinas inteiras dedicadas a conceitos específicos e a forma correta de se definir e manipular bancos de dados.

Não é objetivo desta disciplina abordar todos os conceitos relacionados à área de bancos de dados. Para isto, existem dentro do curso disciplinas específicas para tal. Esta é uma disciplina que busca abordar como se dá a integração entre o mundo do das aplicações escritas em linguagens de programação orientadas a objetos e o mundo dos bancos de dados. Desta forma, este volume apresenta apenas um subconjunto dos conceitos mais relevantes de bancos de dados, e que são necessários para integração entre um programa escrito em Java e uma base de dados.

Posso afirmar então que esta é uma disciplina com viés prático. Você verá no primeiro capítulo, conceitos básicos sobre bancos de dados, enfatizando sua evolução histórica e principalmente, como se dá a organização interna de um banco de dados. O segundo capítulo é dedicado à linguagem SQL. Esta é a linguagem padrão para manipulação de dados em um SGBD. No terceiro e último capítulo é apresentado o Java Database Connectivity, ou simplesmente JDBC, a especificação padrão para integração entre programas orientados a objetos escritos em Java e uma gama de bancos de dados disponibilizados pela indústria.

Page 7: Programação iii   volume 3 v23

7

Programação III

Capítulo 1

O que vamos estudar neste capítulo?

Neste capítulo, vamos estudar o seguinte tema:

» Introdução a bancos de dados.

Metas

Após o estudo deste capítulo, esperamos que você consiga:

» Aprender os conceitos relacionados com bancos de dados e suas diferenças em relação ao mundo orientado a objetos.

Page 8: Programação iii   volume 3 v23

8

Programação III

Capítulo 1 – Uma visão geral sobre bancos de dados

Vamos conversar sobre o assunto?

Caro cursista,

A importância da informática é evidente pela dependência que a sociedade contemporânea tem dos sistemas de informação (SI). Independente de seus diferentes propósitos, estas aplicações tem como característica comum a manipulação de dados de diversas formas, números e texto até conteúdo multimídia, como imagens e vídeos. Gerenciar tais dados de forma eficiente é de fundamental importância para qualquer instituição. Portanto, entender como os dados de uma aplicação devem ser representados e como interagir com tais dados é também importante para qualquer desenvolvedor de aplicações. Por conta disso, neste capítulo, vamos estudar os conceitos básicos necessários para entender a importância e o funcionamento dos bancos de dados.

1. Introdução

Um Banco de Dados (também chamado de Base de Dados) é uma coleção de dados relacionados, organizados e armazenados visando facilitar a manipulação dos dados armazenados, permitindo realizar alterações, inserções, remoções e consultas. Neste contexto, um “dado” representa um elemento que mantém a sua forma bruta (texto, imagens, sons, vídeos, etc.) e que sozinho não levará a compreender determinada situação. Já “Informação” é o conjunto de dados coletados de forma a se tornarem aplicáveis a determinada situação, ou seja, sua forma e conteúdo são apropriados para um uso específico.

A informação não existe por si só, ela é obtida através de uma interpretação realizada sobre um grupo de dados. Por exemplo, em um sistema acadêmico os dados representam as notas individuais de cada aluno, assim como seus nomes, idades, dentre outros. Destes dados pode-se extrair como informação o fato que os alunos de uma turma específica tiveram notas inferiores à média das demais turmas, e que portanto, precisam de uma atenção maior por parte da coordenação da escola.

Em nosso cotidiano, existem várias bases de dados com as quais interagimos diariamente. Verificar o saldo bancário de uma conta, consultar livros no sistema da biblioteca, fazer a matrícula em um curso na universidade; em todos estes casos temos exemplos de aplicações que manipulam dados como contatos, livros ou dados pessoais. Para facilitar a interação das aplicações com estes dados, uma categoria especial de aplicações foi desenvolvida ao longo dos anos, os chamados Sistemas Gerenciadores de Bancos de Dados (SGBDs ou DBMS – Database Management System). Estes pacotes de software foram desenvolvidos com funções específicas para manipular dados, com operações pré-definidas para inserção, remoção, atualização e consulta dos dados armazenados.

Neste capítulo vamos estudar os conceitos fundamentais sobre bancos de dados,

Page 9: Programação iii   volume 3 v23

9

Programação III

desde a motivação para sua existência, passando por a arquitetura geral destes sistemas, chegando até a forma mais comum para representação dos dados, o modelo relacional baseado em tabelas.

2. Por que os Bancos de Dados existem?

No capítulo 3 do primeiro volume desta disciplina, vimos que é possível armazenar dados em arquivos, permitindo que estas informação sejam salvas entre sessões de uso das aplicações. Historicamente, esta foi a primeira forma pela qual aplicações gerenciaram seus dados, onde cada aplicação definia a forma específica como seus dados eram manipulados. Se mais de um programa desejasse utilizar os dados definidos em uma aplicação, todos os programas precisavam também compartilhar a estrutura de como tais dados eram armazenados.

Com a popularização dos sistemas de informação, as empresas começaram a enfrentar problemas relacionados à esta forma de gerenciamento. Se um determinado sistema precisasse realizar alguma mudança na estrutura de dados, todos os demais sistemas que acessassem os dados tinham que ser alterados, mesmo que a alteração ocorresse em dados não manipulados pelos outros programas. Em outra situação, se os sistemas distintos de uma mesma empresa utilizassem os mesmos dados, porém representados de forma distinta por cada sistema, estes dados precisavam ser replicados em cada sistema. Esta replicação trazia uma série de consequências negativas, dentre as quais pode-se destacar:

» Alta possibilidade de inconsistência de dados replicados: Caso um dado replicado fosse atualizado em uma aplicação, o mesmo deveria ser atualizado em todos os demais sistemas. Caso contrário, um mesmo dado apresentaria valores distintos para a empresa, ocasionando inconsistência de informações. Por exemplo: Uma empresa que tivesse dois sistemas, um de controle de pessoal e outro financeiro precisaria manter informações replicadas sobre seus funcionários. Se o endereço de um funcionário fosse atualizado em um sistema, precisaria também ser atualizado no outro;

» Maior custo de armazenamento: Os mesmos dados seriam armazenados diversas vezes, o que além de ocupar mais recursos, levaria a procedimentos extras para replicar as atualizações sobre os dados. No mesmo exemplo anterior, se houvessem mais sistemas que necessitassem das informações de um funcionário, os dados precisariam ser replicados para cada sistema.

Com o passar dos anos e o aumento da importância dos sistemas de informação e também de sua complexidade, buscou-se uma solução para tais problemas. O cenário vivido então à época motivou à criação de um modelo onde dados fossem isolados de aplicações. Os dados, então representados por arquivos, seriam responsáveis unicamente por guardar as informações que fossem manipuladas pelos programas dos sistemas, representando assim a própria base de dados (BD). Todo acesso ao BD passa a ser realizado de forma centralizada e unificada por um sistema intermediário, o Sistema Gerenciador de Banco de Dados - SGBD.

O SGBD funciona como um sistema intermediário, que atua como ponte entre cada aplicação e a base de dados representada pelos arquivos onde os dados são persistidos fisicamente, como visto na Figura 1.

Page 10: Programação iii   volume 3 v23

10

Programação III

Figura 1 – A visão de um SGBD como intermediário entre aplicações e dados

Dentre as vantagens desta abordagem tem-se:

» O acesso e manipulação de dados entre diferentes sistemas passa a ser feito de uma forma padronizada, que pode ser então compartilhadas entre várias aplicações;

» Diferentes sistemas podem compartilhar os mesmos dados, delegando ao SGBD a intermediação de acesso entre aplicações e dados;

» Os sistemas passariam a enxergar apenas os dados que lhes interessam, criando a idéia de visões. Por exemplo, se um banco de dados guarda todas as informações de um determinado funcionário, como dados pessoais, salário, cargo, dentre outras, um SGBD pode definir que uma determinada aplicação só consiga enxergar uma parte destes dados;

» Os sistemas não precisariam conhecer os detalhes de como seus dados estão gravados fisicamente;

» Os sistemas não precisariam ser modificados se a estrutura de dados que utilizam não for modificada;

» Funcionalidades importantes como auditoria, cópias de segurança (backups) e segurança de acesso podem ser centralizadas e delegadas aos SGBD, abstraindo tais questões para as aplicações que utilizam os dados.

Com tais vantagens, o uso de sistemas gerenciadores de bancos de dados foi rapidamente aceito e se tornou um padrão na indústria. Hoje é difícil imaginar um sistema comercial que não faça uso de SGBDs. Com o passar dos anos e a evolução de tais sistemas, o SGBD passou de programas simples para complexos sistemas de programas com função de habilitar desenvolvedores a criar e manter um banco de dados. Dentre suas funções principais estão:

» Definição do banco de dados: Envolve especificar estruturas e tipos de dados para serem gravados no banco de dados, com uma descrição detalhada de cada tipo de dado.

» Construção do banco de dados: Processo de consistir e gravar inicialmente dados no banco de dados.

» Manipulação de um banco de dados: Inclui funções como consulta por dados específicos e atualização para refletir as alterações no mundo real.

Hoje existem vários bancos de dados disponíveis no mercado, que possuem um núcleo comum de funcionalidades, mas variam em relação ao suporte a quantidade de usuários simultâneos, funcionalidades extras, eficiência e robustez. Obviamente, de acordo com estes critérios, um SGBD pode custar até centenas de milhares de dólares. Porém, é possível encontrar ótimas opções gratuitas disponíveis na Internet para serem utilizadas como SGBDs. Alguns dos principais SGBDs disponíveis no mercado nos últimos anos são:

» Oracle - Sistema comercial, mas possui versões gratuitas para uso acadêmico e/

Page 11: Programação iii   volume 3 v23

11

Programação III

ou doméstico (em casa). Ele foi o primeiro Banco de Dados Corporativo (cliente/ servidor) possuindo grande variedade de distribuições (para Macintosh, Windows, Linux, FreeBSD, Unix) e para computadores de grande porte. Foi um dos primeiros a fazer parte do mercado Web. A participação do Oracle no mercado de Banco de Dados é bastante acentuada, principalmente em grandes empresas e em conjunto com sistemas de médio e grande porte. É um SGBD robusto e seguro, quando bem administrado. A Oracle também desenvolve uma suíte de desenvolvimento chamada de Oracle Developer Suite, utilizada na construção de programas de computador que interagem com a sua base de dados. Site Oficial em http://www.oracle.com/us/products/database/index.htm

» Microsoft SQL Server – é o banco de dados comercial da Microsoft. Ele é um dos principais concorrentes do Oracle. Tem como uma das vantagens o fato de, por ser um produto Microsoft, se integrar nativamente com produtos e linguagens da Microsoft (talvez seja esse o fator que o popularizou!). As versões atuais são independentes e operam exclusivamente sobre Windows. É um software proprietário e pago, como a maioria dos produtos Microsoft. Algumas empresas que usam o MS SQL Server e são consideradas casos de sucesso no Brasil são o Hipercard, o Banco Itaú e a ANEEL (vide: http://www.microsoft.com/brasil/servidores/sql/default.mspx). Site Oficial em http://www.microsoft.com/sqlserver/2008/en/us/

» MySQL - é, atualmente, um dos bancos de dados mais populares, com mais de 10 milhões de instalações pelo mundo. Ele possui versões para Windows, Solaris, Unix, FreeBSD, Linux e é gratuito para uso não-comercial. Algumas das empresas mais famosas que fazem uso deste banco estão: NASA, Banco Bradesco, Dataprev, HP, Nokia, Sony e Lufthansa. O MySQL é usado principalmente para desenvolvimento WEB como servidor de dados para comércio eletrônico. Passou a ser considerado um SGBD de verdade (com conceito de transação) a partir da versão 5. Site Oficial em http://www.mysql.com/

» PostgreSQL - é um sistema gerenciador de banco de dados objeto relacional (SGBDOR), desenvolvido como projeto de código aberto. Ele é um dos SGBDs (Sistema Gerenciador de Bancos de Dados) de código aberto mais avançados, é grautito e tem uma boa aceitação no mercado. Originalmente concebido para rodar em Linux, ele possui versões para Windows. É usando, principalmente, para comércio eletrônico juntamente com linguagem PHP. O PostgreSQL é um projeto open source coordenado pelo PostgreSQL Global Development Group. Site Oficial em http://www.postgresql.org/

» Microsoft Access: é um banco de dados da Microsoft para uso em micros desktops e não em servidores. Esta é a principal diferença dele para os demais bancos SGBD como o Oracle, SQL Server e MySQL, por exemplo. Contudo, ele tem sido muito usado em pequenas e médias empresas para armazenamento de dados em pequenas quantidades. Agora, o MS Access não é considerado um SGBD completo, por não possuir todas as características de um. Mas ele permite o desenvolvimento rápido de aplicações que envolvem tanto a modelagem e estrutura de dados como também a interface a ser utilizada pelos usuários. A linguagem de programação disponível no access é a Microsoft Visual Basic for Applications, igualmente a outros produtos da série Microsoft Office. Maiores informações em: http://office.microsoft. com/pt-br/access/default.aspx

Dos SGBDs listados acima vale ressaltar que o SQL Server e o Oracle tem versões gratuitas, porém limitadas. O PostgreSQL é open source e e o MS Access é pagos (sendo que o Access já vem em algumas versões do pacote Microsoft Office) e o MySQL é gratuito para

Page 12: Programação iii   volume 3 v23

12

Programação III

desenvolvimento, mas pago para produção.

A escolha de qual SGBD usar depende muito do projeto sendo desenvolvido e do quanto a empresa está disposta a investir para armazenamento dos seus dados. Um SGBD gratuito e muito popular nos dias de hoje é o PostGreSQL e vários sistemas de instituições públicas vêm adotando o mesmo. Já no mundo corporativo o Oracle tem sido um dos mais adotados, por ser um dos mais robustos.

3. Arquitetura Geral de um SGBD

Apesar de existir vários SGBD’s, uma arquitetura genérica para estes sistemas é apresentada na literatura. Esta arquitetura apresenta os componentes citados na Figura 2.

Figura 2 - Arquitetura Geral de um SGBD

De acordo com esta arquitetura, os componentes funcionais de um sistema de banco de dados incluem:

» Gerenciador de arquivos, que gerencia a alocação do espaço na armazenagem do disco e as estruturas de dados usadas para representar a informação armazenada no disco;

» Gerenciador do banco de dados, que fornece a interface entre os dados de baixo nível armazenados no disco e os programas aplicativos e de consulta submetidos ao sistema;

» Processador de consultas, que traduz os comandos numa linguagem de consulta para instruções de baixo nível que o gerenciador do banco de dados pode interpretar. Além disso, o processador de consultas tenta transformar uma requisição do usuário em uma forma compatível e mais eficiente com respeito ao banco de dados, encontrando uma boa estratégia para a executar a consulta;

» Pré-compilador da DML (Data Manipulation Language), que converte comandos da DML embutidos em um aplicativo para chamadas de procedimento normal na linguagem hospedeira. DML é uma linguagem para manipular dados e será melhor vista no capítulo 2 deste volume. O pré-compilador precisa interagir com o

Page 13: Programação iii   volume 3 v23

13

Programação III

processador de consultas pra gerar o código apropriado;

» Compilador da DDL (Data Definition Language), que converte comandos da DDL em um conjunto de tabelas contendo metadados ou “dados sobre dados”. DDL permite que a estrutura de um banco seja criado, e assim como DML, será melhor abordado no capítulo 2;

Adicionalmente, diversas estruturas de dados são requeridas como parte da implementação do sistema físico, incluindo:

» Arquivos de dados, que armazenam o banco de dados propriamente dito;

» Dicionário de dados, que armazena metadados sobre a estrutura do banco de dados. O dicionário de dados é usado com freqüência. Assim, deve-se dar grande ênfase no desenvolvimento de um bom projeto e implementação eficiente do dicionário;

» Índices, que fornecem acesso rápido aos itens de dados guardando determinados valores.

3.1 Banco de Dados e uma visão em camadas

Uma outra forma de se enxergar a arquitetura de um banco de dados é por meio de uma visão em camadas. Nesta visão, um banco de dados é composto de três camadas, como descrito na figura abaixo.

O nível físico ou nível interno é aquele que está mais próximo ao armazenamento físico dos dados. Este nível físico é descrito por meio de um esquema interno que define como registros, campos e índices são representados, principalmente em relação à sequência física dos dados.

Já o nível lógico situa-se acima do físico e é responsável por descrever como dados são representados e relacionamentos entre os próprios dados. Esta representação é mais abstrata que os nível físico. Pode-se dizer que esta visão possibilita representar os dados como estes realmente são, sem restrições que um usuário possa ter para visualizá-los, por conta de uma linguagem de programação em particular.

O nível do usuário é aquele em que os usuários possuem diferentes visões dos dados, onde muitas vezes não é necessário o acesso a todos os dados da base. Neste nível estão os programadores e usuários finais das aplicações.

Page 14: Programação iii   volume 3 v23

14

Programação III

4. A modelagem e a representação de dados em um SGBD

Pela arquitetura e visão em camadas de um banco de dados, é importante perceber que bancos de dados tem um modelo específico de como os dados são representados e armazenados. Os modelos de dados utilizados em bancos de dados correspondem a como dados, relacionamentos, suas restrições e seus significados são descritos. Existem diferentes classificações para os modelos de dados, de acordo com diferentes critérios de separação. A seguir, apresentam-se cinco modelos de representação dos dados.

O modelo hierárquico foi a primeira proposta para a representação de dados, onde os dados são representados por uma estrutura de árvore semelhante àquelas estudadas na disciplina de estrutura de dados. Dados são acessados segundo um sequência hierárquica com uma navegação do topo (raiz) para as folhas (nós sem folhas) e da esquerda para a direita dentro de um mesmo nó ou registro.

Figura 3 - Modelo Hierárquico de Dados

Apesar de representar uma primeira tentativa na padronização da representação de dados, o modelo hierárquica sofreu críticas por necessitar replicar dados em virtude do modelo de árvore utilizado, o que também levava a desperdício de espaço de armazenamento e à possibilidade de inconsistência de dados.

O modelo em rede, assim como o modelo hierárquico, utiliza apontadores entre os dados. Porém, as associações entre os dados não necessariamente segue um modelo de árvore top-down como anteriormente.

Figura 4 - Modelo de dados em rede

Na abordagem em rede não aparecem as anomalias de manutenção da base de dados que aparecem em modelos hierárquicos. Porém, a necessidade que o programador conheça as várias ligações existentes na base cria uma forte dependência da implementação. As consultas são complicadas pois o programador é forcado a pensar em termos de ligações

Page 15: Programação iii   volume 3 v23

15

Programação III

e, em como percorrê-las para obter as informações de que necessita. A independência de dados fica prejudicada, pois a criação ou eliminação de ligações implica em alteração dos programas.

O modelo relacional utiliza a teoria dos conjuntos e álgebra relacional para definir dados em função de tabelas, e relacionamentos entre os campos que formam os dados de cada tabela. Falaremos mais sobre o modelo relacional ao final deste capítulo.

Figura 5 - Modelo Relacional de Dados

O modelo objeto é aquele que procura utilizar os conceitos já conhecidos do paradigma orientado a objetos para representar as informações da base. Os dados são representados através de classes que apresentam dados membros. Campos são instâncias destas classes.

Figura 6 - Modelos de dados como objetos

Os bancos de dados orientados a objetos tem como objetivo integrar a orientação a objeto com as aptidões dos bancos de dados, disponibilizando mais abstração da estrutura de dados do que os bancos de dados convencionais. Existem atualmente ainda poucos produtos no mercado.

De todos estes modelos, o relacional são os de maior sucesso comercial até hoje, e portanto, serão melhor estudados na próxima seção e servirão de base para os exemplos de nosso volume.

4.1 O Modelo Relacional

Este modelo foi o primeiro modelo de dados efetivamente utilizado em aplicações comerciais. Este foi apresentado em 1970 por Codd em seus estudos na IBM sobre a

Page 16: Programação iii   volume 3 v23

16

Programação III

modelagem de dados. É o modelo que possui a base mais formal entre os modelos de dados, entretanto é o mais simples e com estrutura de dados mais uniforme.

A estrutura fundamental do modelo relacional é a relação, mas conhecido como tabela. Na verdade, o modelo é composto por uma coleção de tabelas de nomes únicos. Cada relação ou tabela é constituída por uma ou mais colunas chamadas de atributos (campos) que são os tipos dos dados contidos na relação. O conjunto de valores passíveis de serem assumidos por um atributo será intitulado de domínio. Cada linha da relação é chamada de tupla (registro). O esquema de uma relação nada mais é do que os campos (colunas) existentes em uma relação ou tabela. Já a instância da relação consiste no conjunto de valores que cada atributo assume em um determinado instante. Na intersecção de uma linha com uma coluna encontra-se o dado. Para exemplificar tais conceitos, a figura abaixo apresenta uma tabela para representar a entidade funcionário, com os campos CPF, Nome, Idade e Salário. Nesta tabela, três registros são apresentados para representar os dados de três funcionários.

Figura 7 – Exemplo de uma tabela em um SGBD relacional

As relações não podem ser duplicadas (por exemplo, não podem existir dois estados de Pernambuco, no conjunto de estados brasileiros) e a ordem de entrada de dados no Banco de Dados não deverá ter qualquer importância para as relações, no que concerne ao seu tratamento. Diferentemente dos modelos que o precederam o modelo relacional não tem caminhos pré-definidos para se fazer acesso aos dados. Os relacionamentos entre as tabelas são feitos através do uso de valores de atributos. Para exemplificar este relacionamento, as relações funcionário e departamento representam as informações sobre funcionários e departamentos que são relacionados pelo campo depto. Este relacionamento permite identificar quais são os departamentos que cada funcionário trabalha.

Figura 8 - Exemplo de relacionamento entre duas tabelas

Page 17: Programação iii   volume 3 v23

17

Programação III

Para trabalhar com tabelas, algumas restrições precisaram ser impostas para evitar aspectos indesejáveis, como: Repetição de informação, incapacidade de representar parte da informação e perda de informação. Essas restrições são: integridade referencial e chaves.

Em relação as chaves, existem três principais tipos usadas em SGBDs relacionais: as chaves primárias, as chaves candidatas e as chaves estrangeiras. A chave estrangeira é aquela que consegue identificar de forma unívoca um registro. Por exemplo, os campos CPF e registro nas tabelas apresentadas anteriormente são exemplos campos que não se repetem ( ou que não devem se repetir), e portanto, seriam chaves primárias de suas respectivas tabelas. Em geral, toda tabela deve possuir uma chave primária. Chaves primárias não podem ter valores nulos e tendem a ser mínimas.

Chaves candidatas possuem a mesma característica que uma chave primária, mas que por questões funcionais não foram escolhidas como chave primária. Chaves estrangeiras são valores que correspondem a chaves primárias de outras tabelas ou até mesma da própria tabela, e que estabelecem relacionamentos entre entidades.

Já a integridade referencial remete à garantia que haja integridade de dados, através dos relacionamentos entre entidades. Por exemplo, na relação entre funcionários e departamentos há uma garantia que para um funcionário ser incluído na base, ele deve referenciar departamento válido. Ao mesmo tempo, um registro de um departamento não pode ser excluído se algum registro de funcionário fizer menção a tal departamento.

4.2 Um modelo relacional para o sistema bancário

Para ilustrar um modelo relacional, vamos criar um modelo de tabelas para armazenar as informações do sistema bancário, utilizado como estudo de caso em nossa disciplina. Lembrando um pouco deste sistema, em nosso banco, correntistas possuem uma conta, que pode ser tanto uma conta convencional, uma conta especial ou uma poupança. O diagrama de classes abaixo mostra as principais entidades de nosso sistema.

Figura 9 - Diagrama de Classes do sistema bancário

Uma coisa importante é entender que o mundo OO é diferente do mundo de bancos de dados relacionais. Enquanto o primeiro trabalha com objetos, polimorfismo, herança, o segundo é constituído de tabelas, chaves e restrições. Porém, alguns elementos conseguem ser relacionados, como atributos de objetos mapeados em campos de uma tabela. Boa parte das classes de um diagrama de classes é diretamente mapeada em uma tabela em um modelo relacional. Porém, outros conceitos precisam adaptações. Veja por exemplo, a relação de herança entre as classes Conta, ContaEspecial e Poupanca. Os dados de um objeto Conta podem ser mapeados para uma tabela também chamada CONTA, onde os atributos da classe sejam mapeados em campos na tabela. Em geral, os bancos possuem

Page 18: Programação iii   volume 3 v23

18

Programação III

tipos semelhantes à maioria das linguagens de programação. Além disso, o campo numero pode ser marcado como uma chave primária, pois toda conta deve ter um número único que a identifica na base.

Esta mesma abordagem poderia ser utilizada para mapear as classes ContaEspecial e Poupanca. Porém, isto acarretaria problemas para certas operações, como por exemplo, fazer uma consulta do saldo de uma conta, sem conhecimento de seu tipo. Seria necessário fazer consultas nas três tabelas, além de se replicar informações, o que dificultaria, por exemplo, a introdução ou remoção de campos.

Para isto, pode-se utilizar a integridade referencial para melhorar a modelagem relacional de tabelas. A Figura 10 mostra uma outra abordagem.

Figura 10 – Mapeando herança com integridade referencial e chaves estrangeiras

Page 19: Programação iii   volume 3 v23

19

Programação III

Nesta nova abordagem, os elementos comuns a todo tipo de campo são representadas na tabela CONTA. É introduzido um campo para identificar que tipo de conta se trata um registro na tabela. As tabelas CONTA_ESPECIAL e POUPANCA fazem referencia à tabela CONTA por meio de uma chave estrangeira que aponta para os dados comuns, enquanto armazenam informações específicas sobre seus tipos. Com isto, acaba-se a replicação de informações e centraliza-se as modificações no modelo de dados.

Com a idéia das chaves estrangeiras também é possível mapear os relacionamentos existentes entre as classes do modelo OO. Por exemplo, uma conta possui um relacionamento um objeto Pessoa que representa o correntista da conta. O correntista por sua vez, possui dois relacionamentos com objetos do tipo Endereco, para representar os endereços comercial e residencial do correntista. A figura abaixo mostra o modelo para o mapeamento das classes Pessoa e Conta.

Neste caso, as tabelas PESSOA e ENDERECO devem representar os objetos Pessoa e Endereco. Para criar os mapeamentos é necessário as chaves em cada tabela. Para a tabela PESSOA, a chave primária é facilmente identificada. Cada pessoa é identificada por seu cpf, que é único para cada correntista. Porém, no caso do endereço, não há uma chave explícita. Neste caso, a solução é criar um campo que assuma a função de chave e sirva para identificar cada registro da tabela. Nestas situações, por via de regra, este campo é preenchido automaticamente a cada inserção de um novo registro na tabela.

Em certos casos, o simples relacionamento por meio de chaves estrangeiras não é viável para representar o mapeamento de classes. Veja por exemplo a situação descrita no diagrama UML a seguir.

Page 20: Programação iii   volume 3 v23

20

Programação III

Figura 11 – Diagrama de classes representando o relacionamento entre professores e disciplinas

O diagrama busca representar a situação onde um professor pode lecionar várias disciplinas, e uma disciplina pode ser lecionada por diferentes professores. Nesta relação de M:N (muitos para muitos) a solução passa pela criação de uma tabela intermediária. Esta tabela cria mapeamentos de chave estrangeira para as tabelas que representam as duas classes. Desta forma, é possível representar este tipo de relacionamento de dados, como representado pela figura abaixo.

Figura 12 - Mapeamento M:N no modelo relacional

Neste exemplo, a tabela PROFESSOR_DISCIPLINA possui a chave primária composta pela união dos campos MATRICULA e CODIGO. O primeiro é chave estrangeira para a tabela PROFESSOR, enquanto o segundo é chave estrangeira para a tabela DISCIPLINA. Pelos dados do exemplo, o professor Fernando Trin ta (matrícula 1) leciona as disciplinas matemática (código 55) e física (código 100).

Exercícios

1. Descreva as motivações para o surgimento de sistemas gerenciadores de bases de dados. Cite exemplos de seu dia a dia que utilizam bases de dados com muitos dados (milhares de registros).

2. Busque na Internet, bancos de dados que sigam os seguintes modelos de dados: (a) hierárquico, (b) em rede, (c) relacional e (d) orientado a objetos.

3. Utilizando o modelo de dados relacional, crie modelos para bancos de dados que precisem guardar informações sobre:

a) Uma biblioteca que permita empréstimos de livros a seus usuários;

Page 21: Programação iii   volume 3 v23

21

Programação III

b) Um sistema acadêmico que represente alunos, professores e disciplinas;

c) Uma loja de produtos eletrônicos.

Vamos Revisar?

Resumo

Neste capítulo, você aprendeu conceitos básicos sobre bancos de dados, desde a motivação para seu surgimento, passando por sua arquitetura e os principais modelos de representação e organização de dados existentes na literatura. A maior importância deste capítulo é apresentar o modelo relacional, pois este é o modelo dominante das aplicações modernas. Entender como objetos são mapeados para tabelas é fundamental para entender como sistemas escritos em linguagens orientadas a objetos podem persistir suas informações em bases de dados relacionais.

Page 22: Programação iii   volume 3 v23

22

Programação III

Capítulo 2

O que vamos estudar neste capítulo?

Neste capítulo, vamos estudar o seguinte tema:

» A Linguagem SQL de manipulação de dados.

Metas

Após o estudo deste capítulo, esperamos que você consiga:

» Entender os principais comandos da linguagem SQL;

» Aprender a utilizar um banco de dados simples chamado HyperSQL;

» Realizar operações no HyperSQL com alguns dos principais comandos SQL.

Page 23: Programação iii   volume 3 v23

23

Programação III

Capítulo 2 – A linguagem estruturada de consulta: SQL

Vamos conversar sobre o assunto?

Caro cursista, depois de estudar e ter uma visão geral sobre como os bancos de dados são organizados, é preciso então entender como os dados são manipulados por usuários e aplicações. Para isto, este capítulo é dedicado totalmente ao estudo da linguagem SQL, o padrão da indústria para a realização de operações em bases de dados relacionais.

1. Introdução

Em qualquer base de dados, uma questão importante é como são realizadas as operações sobre os dados armazenados. Em geral, todo SGBD fornece uma linguagem de consulta por meio da qual usuários obtêm informações do banco de dados. Em geral, estas linguagens são de nível mais alto que as linguagens de programação tradicionais, e são categorizadas como procedurais ou não-procedurais (também referenciadas como declarativas). Em uma linguagem procedural, o usuário deve “ensinar” ao sistema a realização de uma seqüência de operações no banco de dados para obter o resultado desejado. Em uma linguagem não-procedural, o usuário descreve a informação desejada sem fornecer um procedimento específico para a obtenção dessas informações. Neste contexto, uma linguagem que precisa ser estudada para manipulação de SGBDs relacionais é a linguagem SQL.

SQL (Linguagem de Consulta Estruturada, do inglês Structure Query Language) é uma linguagem declarativa utilizada para manipulação de bases de dados relacionais, desenvolvida no início da década de 70 nos laboratórios da IBM, originalmente com o nome de SEQUEL (acrônimo para “Structured English Query Language” - Linguagem de Consulta Estruturada em Inglês). O propósito da concepção de SEQUEL era demonstrar a viabilidade do modelo relacional proposto por Codd. SQL

O projeto de SEQUEL foi divulgado nos meios acadêmico por meio de conferências e revistas especializadas, o que permitiu que seus conceitos pudessem ser implementados por terceiros.

No final da década de 70, duas outras companhias além da IBM iniciaram desenvolvimento de produtos similares, que vieram a ser Oracle e Ingres. A Oracle Corporation introduziu a primeira implementação de SQL comercialmente disponível, e é hoje líder no mercado de servidores de bancos de dados. A IBM também implementou SQL em seus sistemas de banco de dados DB2 e SQL/DS. Entre os anos 80 e 90, os produtos com SQL se multiplicaram e hoje SQL é largamente implementada e aceita como o padrão de facto da indústria para linguagem de acesso a bancos de dados, desde sistemas desktop como o Microsoft Access para Windows até sistemas de gerenciamento e servidores de bancos de dados de médio e grande porte em Unix, NT e mainframes.

Hoje, SQL é padronizada por instituições normativas como a ANSI (Instituto

Page 24: Programação iii   volume 3 v23

24

Programação III

Americo de Normas, do inglês American National Standards Institute) e ISO (Organização de Padrões Internacionais, do inglês International Standard Organization). Porém, certos fabricantes introduzem extensões e variações específicas a seus produtos, o que acaba por comprometer a portabilidade de programas que utilizem tais extensões para manipular SGBDs.

No capítulo que segue, você aprenderá comandos básicos da linguagem SQL que serão necessários para a manipulação de dados em seus programas escritos em Java. Para que você veja na prática o uso destes comandos, você também aprenderá a manipular um SGBD simples chamado HyperSQL, que também será utilizado nos exemplos do próximo capítulo, quando finalmente virmos a integração de Java com este SGBD.

2. Classificação de comandos SQL

SQL oferece uma série de comandos para realizar suas operações e tem como principais características a simplicidade e facilidade de uso. As operações em SQL podem tanto definir o modelo de dados da base, quanto inserir, alterar ou apagar registros. De forma a melhor organizar o conjunto completo de comandos, SQL classifica tais comandos em cinco grupos:

» DDL (Linguagem de Definição de Dados, do inglês Data Definition Language): subconjunto que permite ao desenvolvedor definir tabelas novas e elementos associados. Fazem parte deste grupo os comandos CREATE, ALTER TABLE e DROP.

» DML(Linguagem de manipulação de dados, do inglês Data Manipulation Language): subconjunto da linguagem da SQL que são utilizados para realizar inclusões, consultas, exclusões e alterações de dados presentes em registros. Incluem o comandos INSERT, UPDATE e DELETE.

» DQL(Linguagem de consulta de dados, do inglês Data Query Language): formada por apenas um comando, é a parte mais utilizada de SQL. Por meio do comando SELECT, é possível realizar consultas dos mais diversos tipos, de acordo com cláusulas e opções que podem ser utilizadas no comando.

» TCL(Linguagem de controle de transações, do inglês Transaction Control Language): Conjunto de operações utilizados para tratamento de transações. Uma transação é um conjunto de operações individuais que devem ser agrupadas e tratadas como uma só. Fazem parte desta classe os comandos START TRANSACTION, COMMIT e ROLLBACK.

» DCL(Linguagem de controle de dados, do inglês Data Control Language): Este conjunto de comandos controla os aspectos de autorização de dados e permissões de usuários para controlar quem tem acesso para ver ou manipular dados dentro do banco de dados. Dois dos principais comandos são o GRANT e o REVOKE, que respectivamente concedem e retiram permissões de usuários.

Em nossa disciplina, nós veremos principalmente os comandos DDL, DML e DQL. Para isto, os comandos serão apresentados no contexto da criação de um banco de dados para o sistema de controle bancário que nos acompanha desde a disciplina de linguagem de programação II. Para que podemos ver na prática o uso de SQL, utilizaremos um banco bem simples disponível na Internet chamado HyperSQL (http://hsqldb.org/). O HyperSQL é um SGBD relacional escrito em Java que implementa as funcionalidades básicas necessárias para nossos exemplos. Mas que isso, é um banco extremante portável, já que o seu executável ocupa menos que 2Mb.

Page 25: Programação iii   volume 3 v23

25

Programação III

3. Instalação e utilização do HyperSQL

Para instalar e executar o HyperSQL é preciso ter a plataforma Java instalada e baixar o aplicativo do site http://hsqldb.org/. Na sua distribuição, o HyperSQL é um arquivo compactado, que quando descompactado cria uma árvore de diretórios contendo scripts de execução, documentação, exemplos, código-fonte do banco, dentre outros, como apresentado na Figura abaixo.

Figura 13 - Estrutura do HyperSQL, depois de descompactado

Porém, apesar de todas essas informações, o único arquivo necessário para executar o banco é o arquivo hsqldb.jar, localizado na pasta lib. Este arquivo representa o banco, assim como a interface de administração e o driver JDBC que será necessário para integrar os programas Java com o HyperSQL, a ser visto no próximo capítulo.

O HyperSQL é um programa Java. Logo, para executá-lo basta usar o interpretador java para executar a aplicação, cuja classe principal é a org.hsqlbd.server.Server. Logo, a forma mais simples para isso é abrir uma janela para disparar o comando:

java -cp .;<<caminho completo para o arquivo hsqldb.jar>> org.hsqlbd.server.Server

Ao fazer isto, o processo é iniciado e são criados arquivos que representam os dados no diretório onde o interpretador Java foi chamado. Na própria estrutura definida na descompactação, já é sugerida a pasta Data para este fim. Logo, é sugerido que você execute o processo dentro desta pasta, como apresentado na Figura abaixo.

Figura 14 - Rastro de execução do HyperSQL

Page 26: Programação iii   volume 3 v23

26

Programação III

Ao fazer isso, o processo fica em execução. Para finalizar a execução do processo, basta pressionar [CRTL] + [C]. Ao executar o banco na forma mais simples, o HyperSQL cria uma série de arquivos, padronizados pelo prefixo test, como visto na Figura.

Figura 15 - Arquivos gerados na execução do HyperSQL

Estes arquivos representam os dados da base. É possível armazenar mais de uma base, assim como criar arquivos com nomes específicos para cada banco. Tais informações podem ser obtidas na documentação do HyperSQL. Nos exemplos utilizados neste livro, utilizaremos o padrão fornecido para facilitar a didática.

Uma vez o banco estando em execução, é necessário interagir com a base. Para isto, o HyperSQL fornece uma aplicação simples, baseada em uma janela Swing, que permite visualizar o conteúdo do banco, bem como executar comandos a partir de uma janela de interação. Esta aplicação também é fornecida com o arquivo hsqldb.jar, por meio da classe org.hsqldb.util.DatabaseManagerSwing. Logo para executá-la, basta disparar o comando:

java -cp .;<<caminho completo para o arquivo hsqldb.jar>> org.hsqldb.util.DatabaseManagerSwing

Este comando pode ser disparado de qualquer diretório e a figura abaixo apresenta a tela principal da aplicação.

Figura 16 - Tela de Abertura da aplicação DatabaseManagerSwing

Page 27: Programação iii   volume 3 v23

27

Programação III

Na execução do aplicativo, é apresentada uma janela com uma série de opções para conexão com o banco. Para a correta conexão, devem ser mantidas as opções apresentadas na Figura, e então pressionado o botão OK. Feito isso, você estará conectado ao Banco e pronto para executar operações. Nas próximas seções, utilizaremos esta interface para ver na práticas exemplos de vários comandos SQL.

4. Comandos DDL

Os primeiros comandos que precisam ser realizados para se manipular um banco de dados são aqueles que permitem a criação das tabelas, índices, chaves, e demais elementos que compõem a base de dados. São três os principais comandos DDL existentes em SQL:

» CREATE: comando que permite que sejam criados as tabelas, seus campos, chaves, além dos relacionamentos entre tais elementos;

» ALTER TABLE: comando que permite que sejam feitas alterações em elementos já existentes da base de dados;

» DROP: comando que apaga um elemento da base de dados.

Vamos começar a ver tais comandos e a criar nossa base de testes. Primeiramente, vamos criar a base.

4.1 Criando tabelas

Vamos começar pelo comando CREATE, que tem sua sintaxe simplificada da seguinte forma:

CREATE <<NOME_TABELA> (<<CAMPO1>> <<TIPO_CAMPO1>> <<OPÇÕES_CAMPO1>>,

<<CAMPO1>> <<TIPO_CAMPO1>> <<OPÇÕES_CAMPO1>>,

...

<<CAMPON>> <<TIPO_CAMPON>> <<OPÇÕES_CAMPON>>

FOREIGN KEY(<<CAMPO>>) REFERENCES <<TABELA>> (<<CAMPO_CHAVE>>)

Nesta definição, dá-se um nome para uma tabela e em seguida faz-se a definição de cada um de seus campos. Cada campo tem obrigatoriamente um nome e um tipo, além de cláusulas a mais que definem se um campo é chave primária, se ele pode ou não ter valores nulos.

Em relação aos tipos, os bancos definem tipos primários para valores de seus campos, da mesma forma que linguagens de programação definem seus tipos básicos. Embora SQL defina valores padrões para os principais de dados como inteiros, alfanuméricos, booleanos, dentre outros, é importante verificar a documentação da base de dados a ser utilizada, para verificar os tipos definidos por tal SGBD.

No caso do HyperSQL, os principais tipos que serão usados neste volume são apresentados na tabela a seguir:

Page 28: Programação iii   volume 3 v23

28

Programação III

Tipo Descrição

INTERGE Valores numéricos inteiros

DOUBLE Valores numéricos de ponto flutuante

VARCHAR (TAMANHO) Valores alfanuméricos com tamanho definido

BOOLEAN Valores lógicos

IDENTITY Valores inteiros pré-definidos em uma sequência

Tabela 1 - Tipos de Dados do HyperSQL

É possível observar que com exceção do tipo IDENTITY, os demais são equivalentes a tipos já vistos na linguagem Java, como int, double, String e boolean. O tipo IDENTITY é uma opção muito útil para definirmos uma chave primária de uma tabela cujo valor seja gerado automaticamente pelo próprio banco. Veremos mais sobre o tipo IDENTITY logo a seguir.

Existem ainda opções que podem ser associadas a cada campo. Uma das principais opções é identificar se um determinado campo é a chave primária da tabela. Para isto, utiliza-se a expressão PRIMARY KEY associado ao campo que será a chave primária da tabela. Se for uma chave composta, cada campo da chave composta deve ser marcada com este atributo. Também é possível determinar se um campo pode ou não ter valores nulos em algum registro da tabela. Caso isso não seja permitido, pode-se adicionar à definição do campo a expressão NOT NULL.

Para exemplificar, vamos utilizar o comando CREATE para criar a tabela Endereco do sistema de controle bancário. Para isto, utilizamos então o seguinte comando:

CREATE TABLE ENDERECO (ID_ENDERECO IDENTITY PRIMARY KEY, RUA VARCHAR(255) NOT NULL, NUMERO VARCHAR(255) NOT NULL, BAIRRO VARCHAR(255) NOT NULL, CIDADE VARCHAR(255) NOT NULL, ESTADO VARCHAR(20) NOT NULL, CEP VARCHAR(8) NOT NULL)

Neste comando, está sendo criada uma tabela chamada ENDERECO com 7 campos. Todos os campos, com exceção de ID_ENDERECO são alfanuméricos. O campo ESTADO pode ter valores com no máximo 20 caracteres, enquanto o campo CEP pode ter valores com no máximo 8 caracteres. Os demais podem ter o valor máximo de 255 caracteres.

O campo ID_ENDERECO é definido como um IDENTITY. Além disso, este campo também é definido como chave primária da tabela, que será utilizado para o relacionamento com os correntistas. Lembrando: cada correntista possui dois endereços: um comercial e outro residencial. O número associado a cada registro de endereço será utilizado para efetuar esta relação. Para que o desenvolvedor não se preocupe com a geração dos valores deste campo, o tipo IDENTITY é utilizado. A figura abaixo mostra a execução do comando na aplicação de gerenciamento do HyperSQL.

Page 29: Programação iii   volume 3 v23

29

Programação III

Figura 17 – Execução do Comando CREATE no HyperSQL

Para executar o comando basta digitá-lo, e depois pressionar o botão ‘Execute SQL’. Note que no painel esquerdo, após a realização do comando, a tabela ENDERECO é listada.

Continuando a criação do nosso banco, é necessário criar a tabela PESSOA, que guardará os dados dos correntistas do banco. Esta tabela deve possuir os campos CPF, NOME, SEXO, além de chaves estrangeiras para a tabela endereço. Estas chaves representarão os endereços comercial e residencial de cada correntista. Para criar a tabela pessoa utiliza-se então o comando CREATE da seguinte forma:

CREATE TABLE PESSOA (CPF VARCHAR(11) PRIMARY KEY NOT NULL, NOME VARCHAR(255) NOT NULL, SEXO VARCHAR(1) NOT NULL, END_RESIDENCIAL INTEGER NOT NULL, END_COMERCIAL INTEGER NOT NULL, FOREIGN KEY(END_RESIDENCIAL) REFERENCES ENDERECO(ID_ENDERECO), FOREIGN KEY(END_COMERCIAL) REFERENCES ENDERECO(ID_ENDERECO))

Veja que cada campo utiliza a sintaxe já explicada anteriormente. O campo CPF é um candidato natural a chave primária da tabela, uma vez que o CPF de uma pessoa nunca deve ser igual ao de outra. A novidade é a definição de duas chaves estrangeiras pelos campos END_RESIDENCIAL e END_COMERCIAL. Veja que inicialmente eles são definidos como dois campos inteiros convencionais. Porém, nas duas últimas linhas, a cláusula FOREIGN KEY faz uma associação entre estes campos, e o campo ID_ENDERECO da tabela ENDERECO. Desta forma, esta associação indica que para serem válidos, os dois campos devem conter valores válidos para o campo ID_ENDERECO da tabela ENDERECO. Esta regra será reforçada quando começarmos a colocar dados nas tabelas. Porém, este mesmo tipo de raciocínio vale para a criação da tabela CONTA.

Toda conta possui seu número e saldo. Mas além disso, precisa estar relacionada a uma pessoa que represente o correntista da conta. Logo, é necessário também a definição

Page 30: Programação iii   volume 3 v23

30

Programação III

de uma chave estrangeira para estabelecer este relacionamento. O comando apresentado a seguir cria a tabela PESSOA:

CREATE TABLE CONTA (NUMERO INTEGER PRIMARY KEY NOT NULL, TIPO INTEGER NOT NULL, SALDO DOUBLE NOT NULL, CPF VARCHAR(11) NOT NULL, FOREIGN KEY(CPF) REFERENCES PESSOA(CPF))

Observe que o número da conta é utilizado como chave primária da tabela, pois não devem haver contas com o mesmo número. O campo CPF é definido e, por fim, definido como chave estrangeira relacionada com a tabela PESSOA.

Note que existe um campo a mais na definição da tabela CONTA: o campo tipo. Lembre-se que uma conta pode ser uma conta normal, uma conta especial ou mesmo uma poupança. Logo é preciso determinar este tipo na tabela. Além disso, nos casos da conta especial e da poupança, é necessário ainda armazenar informações extras. Por isso, são definidas novas tabelas: CONTA_ESPECIAL e POUPANCA. Cada uma delas possui uma relação com a tabela CONTA por meio de uma chave estrangeira, e suas definições são apresentadas a seguir:

CREATE TABLE CONTA_ESPECIAL (NUMERO INTEGER PRIMARY KEY NOT NULL, LIMITE FLOAT NOT NULL, FOREIGN KEY(NUMERO) REFERENCES CONTA(NUMERO))

CREATE TABLE POUPANCA (NUMERO INTEGER PRIMARY KEY NOT NULL, DIA_ANIVERSARIO INTEGER NOT NULL, TAXA_JUROS DOUBLE NOT NULL, FOREIGN KEY(NUMERO) REFERENCES CONTA(NUMERO))

A Figura abaixo mostra o banco de dados, após todas a tabelas terem sido criadas.

Figura 18 - Visão do SGBD após criação das tabelas

Page 31: Programação iii   volume 3 v23

31

Programação III

Percebe-se que existe uma ordem natural na criação das tabelas no banco. Primeiro, deve-se criar sem nenhum tipo de dependência com outras tabelas, para então se partir para aquelas com maior número de referências a outras tabelas do banco.

4.2 Alterando tabelas

Uma vez criada uma tabela, é possível fazer modificações na estrutura da mesma. Tais ações são fundamentais para alterações em bancos de dados que sofram modificações para atender novos requisitos, como surgimento de novos campos ou novos relacionamentos com outras tabelas. Para isto, utiliza-se o comando ALTER TABLE, cuja sintaxes básicas são apresentadas a seguir:

ALTER TABLE <<TABELA>> {ADD COLUMN <<CAMPO>> <<TIPO>> <<OPÇÕES>>} {ADD FOREIGN KEY<<CAMPO>> REFERENCES <<TABELA(CAMPO)>>} {DROP COLUMN <<CAMPO>>} {ALTER COLUMN <<CAMPO>> RENAME TO <<NOVO_NOME_CAMPO>>}

ou

ALTER TABLE <<TABELA>> RENAME TO <<NOVO_NOME_TABELA>>

Seguem alguns exemplos do uso do comando ALTER TABLE:

» Para adicionar um campo ESCOLARIDADE na tabela pessoa, utiliza-se o comando:

ALTER TABLE PESSOA ADD COLUMN ESCOLARIDADE VARCHAR NOT NULL

» Para remover o campo criado:

ALTER TABLE PESSOA DROP COLUMN ESCOLARIDADE

» Para mudar o nome do campo END_COMERCIAL para ENDERECO_COMERCIAL na tabela PESSOA:

ALTER TABLE PESSOA ALTER COLUMN END_COMERCIAL RENAME TO ENDERECO_COMERCIAL

O comando ALTER TABLE permite fazer outras mudanças nas tabelas, como mudar o tipo de um campo ou seu tamanho, que porém nesta disciplina não serão abordados. Uma coisa importante é que fazer alterações em tabelas que não possuam dados é muito menos restritivo que naquelas que já possuem informações. Por exemplo, se quiséssemos criar uma campo com a restrição NOT NULL em uma tabela que já possua 10 registros, isso não seria possível, pois ao criar o campo, todo registro possuiria um campo sem nenhum valor. Neste caso, a solução seria primeiro criar o campo, depois atribuir valores para cada registro existente, para só então aplicar a restrição sobre valores nulos.

4.3 Apagando tabelas

Por fim, o último comando DDL é o DROP. Este comando permite apagar uma tabela da base de dados, e sua sintaxe é bem sucinta:

DROP TABLE <<TABELA>>

É importante observar que apesar de sua simplicidade, existem ressalvas ao se apagar uma tabela. Por exemplo, se você tentar apagar a tabela CONTA do banco de dados

Page 32: Programação iii   volume 3 v23

32

Programação III

obterá uma mensagem de erro, como demonstrado abaixo.

Figura 19 - Erro ao se apagar uma tabela que possui referências a outras tabelas

A razão para este erro são os relacionamentos que uma tabela pode ter com outras tabelas, que impossibilitam que a mesma seja removida do banco. No nosso caso, as tabelas CONTA_ESPECIAL e POUPANCA referenciam a tabela CONTA, ou seja, são dependentes de informações contidas nesta última tabela. Portanto, a tabela CONTA não pode ser removida sem que antes, tais relacionamentos sejam desfeitos. No caso, ou as relações são desfeitas, ou as tabelas CONTA_ESPECIAL e POUPANCA são removidas primeiramente.

5. Comandos DML e DQL

Uma vez a base criada, o banco se vê pronto para receber dados em cada tabela. Em SQL as instruções DML (Data Manipulation Language) são usadas para manipulação de dados e consiste nas operações de inserir dados (insert into), alterar dados (update) e excluir dados (delete), e são consideradas operações essenciais e de grande aplicação nas operações com banco de dados. Já o único comando DQL é o SELECT, que é utilizado para retornar os resultados de consultas. Estes comandos serão apresentados em conjunto para que a medida que dados sejam alterados, tais modificações possam ser visualizadas. Vamos ver cada um deles a seguir.

5.1 Inserindo dados em tabelas

Vamos começar com o comando INSERT INTO, cuja sintaxe é apresentada abaixo:

INSERT INTO <<TABELA>> [( <<COLUNA1>>, <<COLUNA2>>,...,<<COLUNA_N>>] ) ] {

VALUES(<<VALOR1>>, << VALOR 2>>,...,<< VALOR _N>>)}

O comando insert into permite que sejam criados registros em uma tabela. De acordo com esta sintaxe, um exemplo para inserção de dados na tabela ENDERECO seria como descrito a seguir.

INSERT INTO ENDERECO(RUA, NUMERO, BAIRRO, CIDADE, ESTADO, CEP) VALUES(‘Avenida Recife’,’213’,’Boa Viagem’, ‘Recife’, ‘Pernambuco’, ‘51020021’);

Page 33: Programação iii   volume 3 v23

33

Programação III

Note que há uma correspondência entre cada campo e seu respectivo valor, e que os valores alfanuméricos são delimitados por aspas simples. No caso específico da tabela ENDERECO, você deve se lembrar que existe o campo ID_ENDERECO, definido como do tipo identidade e que também é a chave primária da tabela. Relembrando, o valor deste campo é determinado pelo próprio banco de dados como uma sequência de valores inteiros que começa com 0 (zero) e que a cada novo registro é adicionado uma unidade.

Ao executar este comando no HyperSQL, obtém-se como resultado o número de registros afetados pelo comando. Neste caso, o valor obtido é 1(um), como ilustrado a seguir.

Figura 20 - Comando Insert no HyperSQL

Um detalhe importante na inserção de dados é a atenção em relação as dependências entre as tabelas. Por exemplo, para inserir dados na tabela PESSOA, são necessárias informações dos endereços comercial e residencial de cada correntista. Logo, é necessário que estas informações estejam previamente cadastradas. Tome como exemplo, o cadastro de uma pessoa com os seguintes dados:

Dados Valor

Nome Fernando Trinta

CPF 111111111-11

Sexo Masculino

Endereço Residencial Avenida Recife, 213, Boa Viagem, Recife – Pernambuco, CEP: 51020021

Endereço Comercial Avenida Caxangá, 23, Cordeiro, Recife – Pernambuco, CEP: 51040031

O endereço residencial já foi cadastrado anteriormente. Mas para cadastrar a pessoa, é necessário cadastrar seu endereço comercial. Logo, primeiro tem-se que executar o seguinte comando:

INSERT INTO ENDERECO(RUA, NUMERO, BAIRRO, CIDADE, ESTADO, CEP) VALUES(‘Avenida Caxangá’,’23’,’Cordeiro’, ‘Recife’,’Pernambuco’, ‘51040031’);

Assume-se que ao criar este segundo endereço, o valor de seu campo ID_ENDERECO será igual a 1 (um), pois o primeiro registro iniciou com 0 (zero). Agora com os dois endereços cadastrados, é possível cadastrar o registro da pessoa na tabela PESSOA.

Page 34: Programação iii   volume 3 v23

34

Programação III

INSERT INTO PESSOA(CPF, NOME, SEXO, END_RESIDENCIAL, END_COMERCIAL) VALUES(‘11111111111’,’Fernando Trinta’, ‘M’, 0, 1)

Se por acaso, ao tentar incluir uma pessoa, se utiliza-se um valor para os endereços que não fosse válido, ou seja, inexistente, a operação seria invalidada. Veja por exemplo, o que acontece ao tentar inserir o registro passando o valor 3 para END_COMERCIAL.

Figura 21 - Erro ao inserir um registro sem as devidas dependências satisfeitas

Isso atesta o fato que é necessário ter atenção em se manter as relações de integridade referencial entre as tabelas. Por exemplo, para inserir um registro na tabela CONTA, é necessária a informação sobre o correntista. No caso, é necessário o CPF de uma pessoa cadastrada na tabela PESSOA. O comando abaixo cria um registro de uma conta cujo correntista é a pessoa de CPF igual a 11111111111, com número da conta igual a 1 e saldo de R$ 1000,00.

INSERT INTO CONTA(NUMERO, SALDO, TIPO, CPF) VALUES (1, 1000, 1, ‘11111111111’);

Em relação ao campo tipo, o valor estabelecido é determinado pelo desenvolvedor. No caso desta base de dados, será utilizada a seguinte tabela:

Tipo da Conta Valor do Campo Tipo

Normal 0

Especial 1

Poupança 2

Logo, a conta criada anteriormente é uma conta especial. Sendo uma conta especial, é necessário também inserir informações na tabela CONTA_ESPECIAL, como por exemplo, com o comando a seguir.

INSERT INTO CONTA_ESPECIAL(NUMERO, LIMITE) VALUES(1, 300);

Veja que neste caso, os valores dos campos NUMERO nas duas tabelas são iguais, estabelecendo a ligação entre os dois registros. Neste caso, estabelece-se que a conta especial de número 1 tem como limite o valor de R$ 300,00.

É importante também ressaltar que as restrições sobre dados nulos e chaves

Page 35: Programação iii   volume 3 v23

35

Programação III

primárias são verificadas na inserção de novos dados. Caso se tente burlar algumas destas regras, uma mensagem de erro é exibida, como no caso abaixo, onde tenta-se cadastrar uma conta com número igual a de outra conta já cadastrada na base de dados.

Figura 22 - Erro ao se tentar incluir um registro com uma chave primária já existente

5.2 Visualizando dados das tabelas

Até então, a certeza sobre a inserção dos dados nas tabelas só poderia ser verificada pelas respostas dos comandos INSERT realizados. Porém, uma maneira mais segura seria verificar se os dados constam ou nas tabelas. Para isto, devemos utilizar o comando SELECT, cuja sintaxe simplificada é apresentada a seguir.

SELECT { EXPRESSÃO | TABELA.CAMPO | * } [, ... ]

FROM <<LISTA DE TABELAS>

[WHERE <<EXPRESSÃO>>]

[ORDER BY <<CAMPO>> [{ASC | DESC}] ]

[GROUP BY <<EXPRESSÃO>> ]

Esta é uma versão simplificada do comando SELECT. Existem outras cláusulas que não serão apresentas aqui por questões de escopo da disciplina, e também porque as cláusulas aqui citadas serão suficientes para nossos exemplos.

Na sua versão mais simples, é possível listar todos os registros e campos de uma determinada tabela. Por exemplo, caso você queira ver todos os registros da tabela ENDERECO, deve usar o comando a seguir:

SELECT * FROM ENDERECO

Este comando indica que queremos visualizar todos os campos de todos os registros da tabela ENDERECO. Porém, é possível especificar que queremos ver apenas um conjunto dos campos, listando-os individualmente no comando SELECT, como exemplificado abaixo.

SELECT RUA FROM ENDERECO

Page 36: Programação iii   volume 3 v23

36

Programação III

Veja na Figura abaixo, o resultado da execução do comando SELECT no banco criado até então:

Note que há outros registros. Estes registros foram inseridos propositadamente por meio de outros comandos INSERT. Note também que como havíamos dito, o campo ID_ENDERECO tem valores numéricos que são atribuídos automaticamente para cada registro. Com isto, podemos utilizar o comando SELECT para visualizar qual o valor gerado para um registro de um endereço e depois utilizá-lo para inserir uma pessoa.

Caso se queira refinar uma busca, é possível utilizar a cláusula WHERE. Esta cláusula define uma condição de busca que deve ser satisfeita por um registro para que ele seja retornado como resposta ao SELECT. Para definir esta condição, SQL utiliza operadores lógicos e relacionais já conhecidos das linguagens de programação. A tabela a seguir apresenta os operadores lógicos presentes em SQL.

Tabela 2 – Operadores lógicos de SQL

Operador Descrição

AND“E” lógico, avalia duas condições e retorna verdadeiro apenas se ambas forem verdadeiras

OR“OU” lógico, avalia duas condições e retorna verdadeiro se alguma das condições for verdadeira

NOT Negacão lógica, retorna o valor contrário da expressão avaliada

Em relação aos operadores relacionais, estes são usados para realizar comparações entre valores. A tabela abaixo apresenta os principais operadores relacionais presentes em SQL.

Page 37: Programação iii   volume 3 v23

37

Programação III

Tabela 3 – Operadores relacionais em SQL

Operador Descrição

< Menor

> Maior

<= Menor ou igual

>= Maior ou igual

= Igual

!= Diferente

BETWEEN Utilizado para especificar um intervalo de valores

LIKEUtilizado na comparação de um modelo e para especificar registros de um banco de dados.”Like” + extensão % vai significar buscar todos resultados com o mesmo início da extensão

Para ilustrar sua utilização, vamos apresentar alguns exemplos. Para listar todas as ruas de endereço localizados no bairro de Boa Viagem:

Para listar todas as ruas que começam com a palavra ‘AVENIDA’:

Para listar todas as ruas que começam com a palavra ‘AVENIDA’ e que o ID_ENDERECO estejam entre 1 e 3.

Page 38: Programação iii   volume 3 v23

38

Programação III

A cláusula ORDER BY permite que os resultados apresentados sejam ordenados de acordo com seus valores. É possível definir tanto a ordenação crescente (que é o padrão), quanto decrescente de valores. Por exemplo, para listar os nomes de todas as ruas em ordem decrescente, utilizaríamos o seguinte comando

SELECT RUA FROM ENDERECO ORDER BY RUA DESC

Veja a execução do comando na figura abaixo

Antes de valor sobre a última cláusula, a GROUP BY, é necessário dizer que SQL fornece também um conjunto de funções para agregação de valores. A cláusula GROUP utiliza estas funções para que os resultados de sua chamada sejam aplicadas em um conjunto de registros que são agrupados por um critério comum. A tabela a seguir apresenta as principais funções de agregação.

Tabela 4 - Operadores escalares em SQL

Função Descrição

AVG Utilizada para calcular a média dos valores de um campo determinado

COUNT Utilizada para devolver o número de registros da seleção

SUM Utilizada para devolver a soma de todos os valores de um campo determinado

MAX Utilizada para devolver o valor mais alto de um campo especificado

MIN Utilizada para devolver o valor mais baixo de um campo especificado

Page 39: Programação iii   volume 3 v23

39

Programação III

Para exemplificar o uso das funções de agregação e da cláusula GROUP BY, tome com exemplo a tabela CONTA, com os seguintes dados cadastrados.

Imagine então que se queira saber qual é a média dos saldos entre todas as contas cadastradas. Para isso, utiliza-se a função AVG, como apresentado a seguir.

Caso se queira descobrir o maior e o menor saldo de uma conta, utiliza-se as funções MAX e MIN, como ilustrado a seguir.

A cláusula GROUP BY permite que as funções de agregação sejam agrupadas. Para ilustrar, imagine a consulta onde se queira saber qual o menor e maior saldo das contas de

Page 40: Programação iii   volume 3 v23

40

Programação III

cada correntista, dado que existem pessoas com mais de uma conta no banco. Para isto, utiliza-se a cláusula GROUP BY, como exemplificado a seguir.

Note que nesta última consulta, utiliza-se um novo recurso. Cada campo utilizado na consulta pode ter um ‘apelido’. No caso, utiliza-se a palavras ‘AS’ como uma ligação entre o valor do campo e seu apelido. Este novo identificador é utilizado na resposta da consulta, como visto na Figura.

Por fim, deixamos as consultas mais complexas. Imagine a situação onde você queira visualizar o nome de todos os correntistas que tenham contas especiais. Neste caso há um problema, pois a informação sobre o tipo da conta é armazenada na tabela CONTA, e o nome de cada correntista fica na tabela PESSOA. A princípio, teríamos que fazer duas consultas. Primeiro, descobrir quais são os cpfs das contas especiais na tabela CONTA, e depois utilizar estes valores para criar uma segunda consulta na tabela PESSOA.

Porém, é possível fazer esta consulta com um único comando SQL, por meio da junção de tabelas. Veja que tanto a tabela CONTA, quanto a tabela PESSOA possuem um campo em comum: o CPF. Logo, este campo servirá de ligação entre as duas tabelas em uma consulta. A consulta que pretendemos é apresentada a seguir

SELECT NOME FROM CONTA AS C, PESSOA AS P WHERE (TIPO = 1) AND (C.CPF = P.CPF)

Nesta consulta, verifica-se que podem-se ser obtidos todos os campos das tabelas citadas na cláusula FROM, no caso, as tabelas CONTA e PESSOA. No caso, só estamos querendo retornar o nome. Este é um campo que só existe na tabela PESSOA, então o compilador SQL não vai se perder para obter esta informação. Caso houvessem dois campos com o mesmo nome em tabelas distintas, é necessário especificar qual campo de que tabela se deseja retorna. Para isto, deve-se utilizar o nome da tabela (ou seu apelido), seguido por um ponto “.” e o nome do campo.

Note que da mesma forma que campos podem ser renomeados, tabelas também. Neste exemplo, CONTA passa ser referenciada por “C”, e PESSOA por “P”. Esta substituição é para diminuir o tamanho das cláusulas SQL. A junção das tabelas se dá na cláusula WHERE. Nela, além de se filtrar as contas pelo seu tipo, faz uma ligação entre as duas tabelas através dos números de CPF. A Figura a seguir apresenta a execução desta consulta no HyperSQL.

Page 41: Programação iii   volume 3 v23

41

Programação III

Outro exemplo de junção mais complexo seria: Qual o estado do endereço residencial do correntista cuja conta seja a de número 1?

Neste caso, é necessário fazer a junção de 3 tabelas: CONTA, PESSOA e ENDERECO, pois o estado é parte da tabela ENDERECO, enquanto o número da conta está na tabela CONTA. A única ligação entre as duas tabelas é a tabela PESSOA. A figura abaixo apresenta o comando SELECT e sua execução no HyperSQL.

5.3 Alterando dados em tabelas

Uma vez que já existam dados, o comando UPDATE permite que os valores de um ou vários campos de um ou mais registros sejam alterados. Sua sintaxe é descrita a seguir.

UPDATE <<TABELA>> SET <<COLUNA1>> = <<VALOR1>>,

<<COLUNA2>> = <<VALOR2>>

[WHERE <<EXPRESSÃO>>]

A idéia do comando UPDATE é especificar novos valores para campos que devem ser alterados de acordo com uma condição de pesquisa. Por exemplo, imagine que se queira modificar o nome do correntista ‘Fernando Trinta’ para ‘Fernando Antonio Mota Trinta’. Neste caso, utiliza-se o seguinte comando:

UPDATE PESSOA SET NOME = ‘Fernando Antonio Mota Trinta’ WHERE CPF = ‘11111111111’

Page 42: Programação iii   volume 3 v23

42

Programação III

Note que neste caso, a condição indica que apenas o registro cujo CPF seja igual a ‘11111111111’ tenha o nome alterado para ‘’Fernando Antonio Mota Trinta’. De acordo com os dados cadastrados até então, só existe um registro que satisfaça esta condição, e este será o único registro alterado. Se não houvesse nenhum registro que satisfizesse a condição, nenhum registro seria alterado.

É importante salientar que se nenhum condição de seleção for especificada na cláusula WHERE, a atualização se dá em todas os registros da tabela. Por exemplo, caso se queira aumentar o saldo de todas as contas em 50%. Neste caso, não há necessidade de se definir a cláusula WHERE, como ilustrado na figura abaixo.

Duas observações importantes neste último exemplo. Primeiro, é possível utilizar expressões matemáticas para atualizações, assim como nos critérios de busca bem como nos resultados das consultas. Segundo, o resultado do comando UPDATE é o número de registros afetados por sua execução. No nosso exemplo, cinco linhas da tabela foram modificadas pelo comando UPDATE.

5.4 Removendo registros

O último dos comandos DML é aquele que permite a exclusão de registros, o comando DELETE. A sintaxe simplificada do comando é apresentada a seguir

DELETE FROM <<TABELA>> [ WHERE CONDIÇÃO ]

Page 43: Programação iii   volume 3 v23

43

Programação III

O comando DELETE remove todas os registros de uma determinada tabela que obedeçam ao critério de busca definido na cláusula WHERE. Se não for definido nenhum critério, todas as linhas da tabela são removidas, o que indica que o comando deve ser utilizado com cuidado.

É importante também frisar que para que as linhas de uma coluna sejam removidas, as condições de integridade referencial entre os dados da tabela devem ser satisfeitos. Por exemplo, na tabela CONTAS, existem registros que são referenciados por outras tabelas, com as contas especiais ou poupanças.

A tentativa de se remover um registro nestas condições ocasionará um erro, como apresentado pela figura abaixo.

Logo, para efetivamente se apagar um registro, deve-se seguir a ordem correta de remoção de dependência entre registros que estejam relacionados. No caso de nosso exemplo, primeiro deve-se apagar o registro na tabela CONTA_ESPECIAL, para então remover o registro na tabela CONTA, como apresentado a seguir.

Note que os dois comandos (assim como mais de um comando) podem ser disparados na mesma janela. Estes são executados na ordem em que são digitados na ferramenta.

Page 44: Programação iii   volume 3 v23

44

Programação III

Exercícios

1. Utilizando o banco de dados HyperSQL, crie o seguinte esquema de tabelas para representar um banco de dados para uma discoteca de mídias (DVDs e CDs).

Neste esquema, as chaves primárias são automáticas (tipo IDENTITY).

2. Utilizando comandos INSERT, cadastre os dados apresentados na figura

3. Utilizando o comando UPDATE, modifique o preço do DVD com nome “Berimbau Metalizado” para 39,90.

4. Utilizando o comando DELETE, tente remover o registro na tabela CANTOR que identifica a cantora “Ivete Sangalo”. Qual o resultado obtido?

5. Usando o comando SELECT, realize a seguintes consultas:

1) Quais são os nomes e o tempo das músicas cantadas por Michael Jackson?

2) Qual a soma total do tempo das músicas do DVD “berimbau metalizado”?

3) Quem são os cantores que não tem mídias cadastradas na base?

4) Qual o CD mais caro e o mais barato da base?

5) Qual a média de preço de CDs?

6) Qual a música mais longa de Ivete Sangalo?

Page 45: Programação iii   volume 3 v23

45

Programação III

Vamos Revisar?

Resumo

A linguagem SQL é o padrão para manipulação de bases de dados. Conhecê-la é imprescindível para que se possa realizar ações nos SGBDs atuais. Este capítulo apresentou de forma resumida, alguns dos principais comandos da linguagem. No próximo capítulo, veremos como podemos utilizar a linguagem SQL dentro de classes de sistemas escritos em Java.

Page 46: Programação iii   volume 3 v23

46

Programação III

Capítulo 3

O que vamos estudar neste capítulo?

Neste capítulo, vamos estudar o seguinte tema:

» A especificação Java Database Connectivity.

Metas

Após o estudo deste capítulo, esperamos que você consiga:

» Entender as principais classes e interfaces da API JDBC;

» Criar programas que permitam interagir com um banco de dados.

Page 47: Programação iii   volume 3 v23

47

Programação III

Capítulo 3 – A especificação JDBC

Vamos conversar sobre o assunto?

Caro cursista, Nos capítulos anteriores deste volume vimos os conceitos básicos sobre bancos de dados e a sintaxe básica da linguagem SQL, padrão para manipulação de bancos de dados relacionais. Este capítulo utiliza estas informações para apresentar como aplicações escritas em uma linguagem de programação, no caso Java, possa interagir com um SBGD, de modo a persistir suas informações.

1. Introdução

Por tudo que já foi visto neste volume, é fácil perceber a importância que os bancos de dados tem para os sistemas e aplicações modernas. Como meio padrão para guardar as muitas informações geradas pelos sistemas de informação, estas aplicações se tornaram imprescindíveis para praticamente qualquer tipo de sistema atualmente.

SQL é a linguagem padrão para acesso a banco de dados e se estabeleceu como tal no mercado. No entanto, ela não é adequada para o desenvolvimento de aplicações e na prática é dependente de SGDB e de plataforma. O que viu-se então foi a necessidade de permitir que aplicações escritas em linguagens de programação convencionais pudessem interagir com os SBGDs. O que aconteceu foi que cada SBGD apresentava sua forma de permitir tal acesso. Em geral, as abordagens permitiam que comandos SQL pudessem ser executados a partir de programas externos.

Apesar desta similaridade, cada SGBD possui arquitetura interna e detalhes de implementação que lhes são particulares. Neste contexto, cada um destes SGBDs possui uma forma específica de permitir a comunicação de programas externos com suas bases de dados, como ilustrado na figura abaixo.

É fácil perceber que este cenário traz uma série de problemas em relação à portabilidade das aplicações. Se por um acaso uma aplicação precisasse mudar o SGBD, seriam necessárias mudanças no código de suas aplicações, gerando um série problema de manutenção e flexibilidade para as empresas. Os desenvolvedores também sofriam por ter que aprender diferentes formas e APIs para conexão com cada SGBD diferente, criando um pesadelo para os programadores.

Estava claro que este era um cenário que não poderia continuar a existir. No caso da linguagem, a solução está na adição de uma camada adicional que procura abstrair e

Page 48: Programação iii   volume 3 v23

48

Programação III

padronizar o acesso a qualquer SGBD. Esta camada, proposta pela Sun Microsystems, é chamada de Java Database Conectivity (JDBC), e tem como proposito básico, uniformizar o acesso a banco de dados relacionais, de modo a aumentar a flexibilidade de sistemas desenvolvidos em Java.

Com o uso de JDBC, o acesso ao SGBD é padronizado por meio de chamadas SQL. Este capítulo vai apresentar JDBC, suas principais classes e interfaces. Através dos exemplos, você vai aprender como criar programas em Java que disparem consultas em bancos de dados relacionais.

2. JDBC

Na prática, JDBC é uma API de baixo nível, formada por um conjunto de classes e interfaces escritas em Java que faz o envio de instruções SQL para qualquer banco de dados relacional. Na realidade, JDBC é uma especificação, o que indica que os fornecedores de SGBDs podem seguir as regras desta especificação para implementar o acesso aos seus produtos para desenvolvedores em Java. O segredo para o correto funcionamento de SGBD é sua arquitetura em camadas, como ilustrado em sua arquitetura na figura abaixo.

Figura 23 - Visão de múltiplas camadas de JDBC

Nesta figura, no nível mais baixo, ressalta-se que cada banco de dados possui um elemento chamado Driver, um componente de software que fornece a conexão ao banco de dados e implementa o protocolo para transferir a consulta e o resultado entre cliente e banco de dados. O Driver JDBC é quem realmente estabelece e gerencia conexões com o banco de dados. Este Driver mascara todo detalhe que é específico de um banco de dados particular. Cada fornecedor implementa e distribui este driver para que os programadores Java possam integrar seus programas com um SGBD em particular. Na realidade, JDBC permite que seus drivers sejam implementados de 4 formas distintas, como apresentado a seguir.

Page 49: Programação iii   volume 3 v23

49

Programação III

Figura 24 - Modos de funcionamento para JDBC

A primeira forma é utilizando uma ponte JDBC-ODBC. ODBC (acrônimo de Open Data Base Connectivity) é uma outra abordagem para acesso a bases de dados. Neste tipo de abordagem, é necessário configurar a máquina cliente para estabelecer a conexão com o banco, como no caso dos sistemas operacionais da família Windows. A segunda opção é aquela em que as chamadas JDBC são convertidas em chamadas nativas do Banco, o que garante ganhos de desempenho, porém menor portabilidade. A terceira abordagem é a utilização de um middleware, que converte chamadas JDBC em um protocolo de rede independente do SGBD e comunicam-se com um intermediário (gateway) que traduz estas requisições para o protocolo específico do SGBD. Esta abordagem possibilitam o uso de diferentes SGBDs, sendo a mais flexível alternativa dentre as alternativas apresentadas. Sua utilização também permite que os bancos sejam acessados pela Internet, porém com cuidados adicionais com a segurança. Por fim, a quarta alternativa é aquela em que as chamadas JDBC são convertidas em um protocolo de rede usado diretamente pelo SGBD. Esta é uma solução puramente escrita em Java e que não requer nenhum código adicional no cliente.

Independente de qual seja a estratégia utilizando, acima do driver há um componente chamado DriverManager, que gerencia os drivers disponíveis para as aplicações e que serve de ponte para que a API JDBC possa disparar comandos SQL nos programas escritos em Java.

O ponto de contato do programador Java e JDBC é através da API JDBC, que define um conjunto de classes da linguagem Java que representam conexões à bancos de dados, consultas SQL, resultados de consultas, informações sobre bancos de dados, etc., ou seja, permite ao programador Java consultar bancos de dados e manipular os resultados das consultas. As interfaces classes de JDBC são agrupadas no pacote java.sql e são representadas na figura abaixo.

Page 50: Programação iii   volume 3 v23

50

Programação III

Figura 25 - API JDBC

Neste capítulo veremos como usar as principais classes desta API, que são elas:

» DriverManager - gerencia o driver e cria uma conexão com o banco;

» Connection - é a classe que representa a conexão com o banco de dados.

» Statement - controla e executa uma instrução SQL ;

» PreparedStatement - controla e executa uma instrução SQL, por meio de parâmetros. Em geral, seu uso é mais indicado que do Statement;

» ResultSet - contém o conjunto de dados retornado por uma consulta SQL.

Antes disso, vamos preparar o nosso ambiente de desenvolvimento para trabalhar com JDBC.

3. Configurando eclipse para usar JDBC

Antes de começarmos a ver como se utiliza na prática a API JDBC, é preciso configurar o ambiente para tal utilização. Como visto na seção anterior, para acessar um SGBD, é necessário possuir um driver de acesso para tal banco. Estes drivers são disponibilizados nos sites das empresas e fornecedores dos SGBDs como um arquivo JAR.

Em nossos exemplos, o banco utilizado será o HyperSQL, cujo driver JDBC é o próprio arquivo hsqldb.jar, que também representa o próprio banco. Para que o driver seja utilizado pela aplicação, é necessário adicionar o arquivo JAR no classpath da aplicação ou no momento da chamada do interpretador Java. Quando se utiliza ambientes integrados de desenvolvimento, como Eclipse ou Netbeans, é necessário incluir este arquivo nas configurações dos projetos. Nesta disciplina, para manter a compatibilidade com os capítulos anteriores, será utilizado o Eclipse, e os próximos parágrafos apresentam os passos necessários para a configuração de um projeto que utilize JDBC para acessar o HyperSQL.

Primeiramente é necessário ter um projeto Java criado. Neste projeto é interessante criar uma pasta para guardar o arquivo do driver que será utilizado. Esta abordagem permite que quando se queira copiar ou mudar o projeto de localização, os arquivos do driver sejam levados também, evitando a quebra de dependência da aplicação. Para criar uma pasta, basta clicar com o botão direito sobre o projeto, seguido pela opção New. Esta opção abrirá um outro sub-menu. Neste sub-menu, deve-se escolher a opção Folder, como ilustrado na

Page 51: Programação iii   volume 3 v23

51

Programação III

Figura abaixo.

Figura 26 - Configurando o eclipse para usar JDBC (Passo 1)

Ao selecionar esta opção, uma janela será apresentada para que a nova pasta seja criada. Deve ser selecionado o nome do projeto para que a nova pasta seja criada na raiz do próprio projeto. Por padrão, o nome desta pasta é definido como lib (do inglês, libraries). Esta pasta representa todas as bibliotecas necessárias para correta execução dos programas do projeto. O driver JDBC pode ser também visto como uma destas bibliotecas. Digitado o nome, pode-se clicar sobre o botão finish.

Figura 27 - Configurando o eclipse para usar JDBC (Passo 2)

Page 52: Programação iii   volume 3 v23

52

Programação III

A pasta pode ser visualizada no projeto. Porém ainda falta copiar o driver para dentro deste diretório.

Figura 28 - Configurando o eclipse para usar JDBC (Passo 3)

A cópia do arquivo pode ser feita tanto diretamente pelo sistema operacional, quanto pelo Eclipse. Para isto, deve-se utilizar o gerenciador de arquivos do sistema (no windows é o Explorer), selecionar o arquivo hsqldb.jar no diretório onde o mesmo esteja armazenado e com o botão direito, utilizar a opção “copiar”. O mesmo procedimento pode ser feito com as conhecidas teclas de atalho <control> + <C>. Com o arquivo copiado, deve-se selecionar a pasta lib no eclipse, e com o botão direito, escolher a opção “Paste” (Colar, em inglês).

Figura 29 - Configurando o eclipse para usar JDBC (Passo 4)

Page 53: Programação iii   volume 3 v23

53

Programação III

Este procedimento copiará o arquivo para dentro do projeto do Eclipse, com pode ser observado na figura abaixo.

Figura 30 - Configurando o eclipse para usar JDBC (Passo 5)

Para finalizar o processo de configuração é necessário fazer com que o projeto Java entenda que o arquivo hsqldb.jar é um arquivo que deve ser considerado quando da compilação das classes codificadas no projeto. Para fazer isto, deve modificar as propriedades do projeto, clicando com o botão direito sobre o nome do projeto, e escolhendo a última opção do menu, com apresentado na figura abaixo.

Figura 31 - Configurando o eclipse para usar JDBC (Passo 6)

Ao fazer esta operação é apresentada uma janela com várias opções em relação ao

Page 54: Programação iii   volume 3 v23

54

Programação III

projeto. A opção que procuramos é Java Build Path, localizada à esquerda da Janela, e que permite incluir novos arquivos no classpath do projeto. Para isto, deve-se selecionar a aba libraries na parte superior da janela. Selecionada esta aba, clique sobre o botão add jars.

Figura 32 - Configurando o eclipse para usar JDBC (Passo 7)

Depois de clicar sobre o botão, deve ser apresentada uma janela com os arquivos que fazem parte do projeto e que podem ser incluídos como bibliotecas. Selecione o arquivo hslqbd.jar e pressione o botão OK.

Figura 33 - Configurando o eclipse para usar JDBC (Passo 8)

Page 55: Programação iii   volume 3 v23

55

Programação III

Feito isso, o projeto estará configurado para criar classes que utilizem a API JDBC para acessar o banco HyperSQL. Caso seja necessário acessar um outro banco qualquer, a diferença será o arquivo a ser utilizado, porém os passos necessários para configuração do eclipse serão basicamente os mesmos.

4. Utilizando a API JDBC

Configurado o projeto, podemos agora utilizar a API JDBC para executar operações sobre o banco. Nos exemplos deste capítulo, utilizaremos a base de dados do sistema de controle bancário, criada no HyperSQL. Portanto, além dos passos de configuração realizados na seção anterior, é também requisito que a base de dados com as tabelas CONTA, PESSOA, ENDERECO, CONTA_ESPECIAL e POUPANCA tenham sido criadas seguindo os passos do capítulo anterior, bem como que o banco ativo durante a execução dos programas deste capítulo

4.1 Estabelecendo uma conexão com o banco

Para a aplicação Java se comunicar com um banco de dados, uma conexão com o banco deve ser estabelecida. Esta conexão é estabelecida de seguinte forma. Primeiro, o driver JDBC do banco em questão deve ser carregado. Em seguida, deve ser criada uma conexão com o banco para realização de consultas e atualizações.

Para se carregar um driver utiliza-se a construção Class.forName(“Nome do Driver”). Cada forneceder possui uma classe específica que implementa o driver JDBC de acesso ao seu banco de dados. Esta classe deve estar contida dentro do arquivo JAR incluído no classpath da aplicação. É necessário ter acesso a documentação do driver JDBC para saber qual o nome completo desta classe. No caso do HyperSQL, a classe que implementa do driver é a org.hsqldb.jdbc.JDBCDriver. Sendo assim, para carregar o driver do HyperSQL, basta utilizar o trecho de código descrito abaixo.

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”);} catch (ClassNotFoundException e) { e.printStackTrace();}

Note que a chamada ao método Class.forName precisa tratar a exceção de ClassNotFoundException, pois pode acontecer se tentar carregar um driver não existente.

Com o driver existente e disponível, deve-se então utilizar o Driver Manager para manipulá-lo. A classe DriverManager implementa esta gerencia dos drivers disponíveis. Porém, é necessário identificar cada banco individualmente. Para isto, utiliza-se uma URL, cujo formato é descrito abaixo:

Cada aplicação utiliza o subprotocolo para identificar e selecionar o driver a ser instanciado, enquanto o dsn é o nome que o subprotocolo utilizará para localizar um

Page 56: Programação iii   volume 3 v23

56

Programação III

determinado servidor ou base de dados. Desta forma é possível utiliza dois esquemas distintos em uma mesma base de dados. Esta sintaxe é dependente de cada fabricante. Por exemplo, o Oracle utiliza a seguinte sintaxe:

jdbc:oracle:thin:@200.206.192.216:1521:exemplo

No caso do HyperSQL, a URL de acesso é dada de acordo com o modo de uso do banco. No modo que está sendo usado nos exemplos deste livro, a URL de acesso é dada por:

jdbc:hsqldb:hsql://localhost/

A classe DriverManager é uma classe estática, que possui métodos para registrar, remover e listar drivers JDBC. É através do DriverManager que se consegue estabelecer uma conexão válida com o banco. Para isto, utiliza-se o método getConnectio, que possui várias versões sobrecarregadas. A mais comum delas, é que recebe a URL de acesso ao banco, o nome do usuário do banco e sua senha. No caso do HyperSQL, para se acessar o banco criado deve-se utilizar então a chamada:

Connection con = DriverManager.getConnection( “jdbc:hsqldb:hsql://localhost/”, “SA”, “”);

O retorno desta chamada é um objeto do tipo Connection, que representa uma conexão com o banco de dados, e que permite que as operações realizadas por outras classes da API sejam repassadas ao SGBD. Sendo assim, o trecho de código padrão para abertura de conexão com um banco é descrito abaixo.

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); Connection con = DriverManager.getConnection(“jdbc:hsqldb:hsql://localhost/”, “SA”, “”);} catch (ClassNotFoundException e) { e.printStackTrace();} catch (SQLException e) { e.printStackTrace();}

Note que agora, uma nova exceção precisa ser tratada, a SQLException. Esta exceção está presente em praticamente toda operação de manipulação com o banco, pois estas ações estão sujeitas a problemas. Por exemplo, o banco não estar em execução ou indisponível. Erros também podem acontecer em operações de bancos válidos, como ao tentar acessar uma tabela que não exista. Portanto, praticamente toda operação de manipulação com o banco, como a abertura de uma conexão, deve tratar a exceção SQLException.

4.2 Acessando o banco de dados

Com a conexão estabelecida, é possível interagir com o banco de várias formas, como:

» Criar tabelas e outros elementos;

» Adicionar, alterar e remover registros de tabelas existentes;

» Buscar registros.

Para realizar estas operações, utilizaremos as interfaces Statement,

Page 57: Programação iii   volume 3 v23

57

Programação III

PreparedStatement e ResultSet. As duas primeiras são utilizadas para realizar as operações no banco, enquanto a última serve para obter o resultado de uma consulta sobre uma tabela.

Vamos primeiro utilizar a interface Statement. Um objeto Statement é obtido a partir de um objeto Connection válido que representa uma declaração SQL que deve ser executada no SGBD por meio do método createStatement(). As ações possíveis para o Statement são executadas por métodos específicos para consultas, atualizações. Dentre os principais métodos estão:

» executeUpdate(): usado para operações que causam alteração na base de dados (create, insert , update, etc);

» executeQuery(): usado em operações de consulta à base de dados.

Ambos recebem como parâmetro uma string com o comando SQL a ser executado na base. A classe a seguir ilustra como exemplo um programa para inserir um novo endereço na tabela ENDERECO criada no capítulo passado.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement;

public class ExemploJDBC {

public static void main(String[] args) {

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); Connection con = DriverManager.getConnection( “jdbc:hsqldb:hsql://localhost/”, “SA”, “”); Statement stmt = con.createStatement(); String sql = “INSERT INTO ENDERECO(RUA, NUMERO, “ + “BAIRRO, CIDADE, ESTADO, CEP) “ + “VALUES(‘Avenida Washington Soars’,’1233’,’Edson Queiroz’, “ + “’Fortaleza’,’Ceará’, ‘60800876’)”; stmt.executeUpdate(sql); stmt.close(); con.close();} catch (ClassNotFoundException e) { e.printStackTrace();} catch (SQLException e) { e.printStackTrace();} } }

Ao final da execução e uso dos objetos Statement e Connection, o método close() deve ser invocado. Da mesma forma como os streams, manter conexões com o banco de dados requer que recursos do sistema sejam alocados. O método close() faz com que estes recursos sejam liberados para o sistema e possam ser utilizados para outros fins. É muito importante fazer esta reposição dos recursos para que o programa não cause problemas de desempenho, ou mesmo com falta de memória para.

Page 58: Programação iii   volume 3 v23

58

Programação III

Para verificar se o programa funcionou corretamente, você pode acessar o banco pela aplicação DatabaseManagerSwing, utilizada no capítulo passado e realizar uma consulta, como ilustrado a seguir.

Este mesmo princípio pode ser utilizado para realizar comandos outros comandos DML como UPDATE, DELETE, SELECT, bem como comandos DDL, como CREATE TABLE, ALTER TABLE e DROP TABLE. O programa a seguir mostra uma atualização sobre a tabela CONTA, utilizando o comando UPDATE para aumentar em 50% o valor do saldo de cada conta.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement;

public class ExemploJDBC {

public static void main(String[] args) {

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); Connection con = DriverManager.getConnection( “jdbc:hsqldb:hsql://localhost/”, “SA”, “”); Statement stmt = con.createStatement(); String sql = “UPDATE CONTA SET SALDO = SALDO * 1.5”; int registros = stmt.executeUpdate(sql); System.out.println(“Registros afetados: “+registros); stmt.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace();

Page 59: Programação iii   volume 3 v23

59

Programação III

} catch (SQLException e) { e.printStackTrace(); }} }

Note que a execução do método executeUpdate retorna o número de linhas afetadas pelo comando SQL, e que este valor pode ser utilizado no programa Java.

No caso de consultas, a recuperação de dados do BD é trivial e é feita através da execução de uma consulta SQL utilizando o método executeQuery, onde os dados são encapsulados em um objeto do tipo ResultSet, retornada na execução da consulta.

Este objeto é uma estrutura que mantem os dados resultantes de um consulta à base de dados, representando um cursor para os registros retornadas na consulta, onde pode-se navegar pelos resultados e recuperar as informações armazenadas nas colunas. Quando esta estrutura é retornada, o cursor de navegação é posicionado antes do primeiro registro retornado. A partir daí, pode-se utilizar os seguintes métodos para navegação:

Tabela 5 - Métodos da interface ResultSet

next()Posiciona o ResultSet um registro a frente. Caso esteja além da última linha retornará false.

previous()Posiciona o ResultSet um registro atrás. Caso esteja além da primeira linha retornará false.

first() Posiciona o ResultSet no primeiro registro.

last() Posiciona o ResultSet no último registro.

Os métodos descritos acima navegam sobre os registros. Para acessar as colunas de cada registro por meio de métodos getInt(), getString, getFloat e outros métodos que mapeiam os tipos dos campos em tipos da linguagem Java. Cada um destes métodos recebe como parâmetro o nome da coluna do banco. A tabela abaixo apresenta alguns métodos utilizados para recuperação de informações com o ResultSet.

Figura 34 - Métodos getXXX da interface ResultSet

Page 60: Programação iii   volume 3 v23

60

Programação III

Para melhor exemplificar o uso de consultas, a listagem abaixo apresenta uma classe que lista todos os registros da tabela CONTA.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;

public class ExemploJDBC {

public static void main(String[] args) {

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); Connection con = DriverManager.getConnection( “jdbc:hsqldb:hsql://localhost/”, “SA”, “”); Statement stmt = con.createStatement(); String sql = “SELECT * FROM CONTA”; ResultSet rs = stmt.executeQuery(sql); while(rs.next()){ String cpf = rs.getString(“CPF”); int num = rs.getInt(“NUMERO”); double saldo = rs.getDouble(“SALDO”); System.out.println(“Conta: “+num); System.out.println(“Saldo: “+saldo); System.out.println(“CPF correntista: “+ cpf); System.out.println(“----------------------------”); } rs.close(); stmt.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }} }

A Figura a seguir apresenta a execução da classe.

Page 61: Programação iii   volume 3 v23

61

Programação III

Figura 35 – Execução do programa de listagem de contas

4.3 Parametrizando consultas

Os exemplos descritos até agora neste capítulo tem utilizado consultas SQL prontas, que são passadas para o objeto Statement e, posteriormente executadas. Porém, em seu uso prático, a integração entre programas e JDBC indica que as consultas receberão valores do programa para construir dinamicamente as consultas que deverão ser executadas no SGBD.

Com o uso de Statement, isso é feito simplesmente concatenando o valor ao comando SQL a ser executado, como exemplificado no código abaixo. Neste exemplo, o usuário fornece o número da conta da qual deseja as informações.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Scanner;

public class ExemploJDBC {

public static void main(String[] args) {

try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); Connection con = DriverManager.getConnection( “jdbc:hsqldb:hsql://localhost/”, “SA”, “”); Statement stmt = con.createStatement(); Scanner sc = new Scanner(System.in); System.out.println(“Digite o número da conta:”);

Page 62: Programação iii   volume 3 v23

62

Programação III

int numero = sc.nextInt(); String sql = “SELECT * FROM CONTA WHERE NUMERO = “ + numero; ResultSet rs = stmt.executeQuery(sql); while(rs.next()){ String cpf = rs.getString(“CPF”); int num = rs.getInt(“NUMERO”); double saldo = rs.getDouble(“SALDO”); System.out.println(“Conta: “+num); System.out.println(“Saldo: “+saldo); System.out.println(“CPF correntista: “+ cpf); System.out.println(“----------------------------”); } rs.close(); stmt.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }} }

Apesar de correta, esta forma não é a melhor e nem a mais indicada, pois acarreta em um trabalho de concatenação extremamente entediante e tendencioso a erros, principalmente na construção de SQLs maiores. Além disso, SQLs maliciosos podem ser embutidos e gerar problemas de segurança. Por tais questões, uma alternativa é o uso da interface PreparedStatement.

Um PreparedStatement representa um comando SQL pré-compilado, o que também representa uma otimização de recursos, principalmente quando se tem várias consultas similares com parâmetros distintos.

Em um PreparedStatement, as cláusulas SQL são criadas com pontos de interrogação (?) no lugar dos parâmetros da consulta. Para criar um PreparedStatement, a consulta parametrizada deve ser passada ao método prepareStatement.

Com o objeto PreparedStatement, cada parâmetro é inserido com métodos do tipo set<<Tipo>>, onde os tipos são semelhantes àqueles definidos no ResultSet. Cada método set<<Tipo>> recebe dois parâmetros: o número do parâmetro a ser substituído de acordo com a ordem da consulta e o valor a ser substituído na consulta.

O trecho de código abaixo representa como seria uma versão do programa anterior utilizando PreparedStatement.

Scanner sc = new Scanner(System.in); System.out.println(“Digite o número da conta:”); int numero = sc.nextInt(); String sql = “SELECT * FROM CONTA WHERE NUMERO = ?”; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setInt(1, numero); ResultSet rs = pstmt.executeQuery(); while(rs.next()){ String cpf = rs.getString(“CPF”);

Page 63: Programação iii   volume 3 v23

63

Programação III

int num = rs.getInt(“NUMERO”); double saldo = rs.getDouble(“SALDO”); System.out.println(“Conta: “+num); System.out.println(“Saldo: “+saldo); System.out.println(“CPF correntista: “+ cpf); System.out.println(“----------------------------”); }

Os parâmetros, com PreparedStatement, podem ser passados directamente com o próprio tipo que o dado foi definido, evitando assim ter que ficar formatando ou convertendo campos de data e decimais, além de poder passar inclusive valores nulos.

5. Transações com JDBC

Os comandos SQL executados em um banco de dados realizam ações individuais sobre as tabelas. Dependendo do tipo de processamento, uma determinada funcionalidade de um sistema precisa realizar mais de uma ação em várias tabelas do banco de dados. Se estas ações modificarem o conteúdo das tabelas, apenas a correta execução de todos os comandos garantirá a consistência da base de dados. Porém, em algum momento no tempo, todo sistema computacional apresenta uma falha. Se esta falha ocorrer no meio da realização de um conjunto de comandos SQLs relacionados a uma única funcionalidade, e apenas alguns dos comandos forem executados, um problema está criado.

Para exemplificar, um clássico exemplo é a realização de uma transferência entre duas contas. A transferência implica que um determinado valor precisa ser debitado de uma conta e creditado em outra conta. Falando em termos de SQL, dois comandos UPDATE precisam ser realizados em dois registros distintos. Se por um azar qualquer, uma falha ocorrer depois que o UPDATE de débito tiver sido realizado, mas antes que o UPDATE de crédito seja efetivado, a operação de transferência irá gerar uma inconsistência na base.

A solução para questões semelhantes está no conceito de transações. Uma transação equivale a agrupar um conjunto de operações e tratá-las com uma só, de tal forma que se a transação for executada, haja garantia que todos sejam executados. Em contrapartida, se a transação apresentar falha, nenhuma de suas ações deve ser realizada. Resumidamente, é o princípio do “tudo ou nada”, ou seja, ou se executa todas as operações de uma transação ou o efeito é o mesmo que nenhuma tenha sido executada.

O controle transacional é feito por meio da interface Connection. Toda conexão aberta com o banco possui um modo de operação para transações ou modo de auto commit. Commit é um termo muito utilizado na área de banco de dados para se ilustrar que uma operação deve ser efetivada realmente no SGBD. O padrão de operação do modo auto commit de uma conexão é aquele em que toda operação de atualização são processadas imediatamente após serem recebidas. Porém, é possível modificar este modo de operação. Por meio do método setAutoCommit(false), o desenvolvedor pode fazer com que a efetivação de um conjunto de operações seja acumlado, deixando que este momento seja definido explicitamente pelo desenvolvedor.

Para isto, dois métodos adicionais da interface Connection devem ser utilizados: o commit e o rollback. O primeiro serve para efetivar o conjunto de instruções acumulados em um conexão até um dado instante, enquanto a segunda é utilizada para desfazer o processo, caso algum erro seja detectado no meio de uma transação.

A listagem abaixo exemplifica o tratamento de uma transação para se fazer uma

Page 64: Programação iii   volume 3 v23

64

Programação III

transferência entre duas contas do banco. Note que só ao final da realização de todas os statements, os comandos são realmente efetivados no banco por meio da chamada ao método con.commit. Caso aconteça alguma erro e a exceção SQLException seja levantada, os comandos até então realizados são desfeitos através do método con.rollback.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Scanner;

public class ExemploTransação {

public static void main(String[] args) { Connection con = null; try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); con = DriverManager.getConnection(“jdbc:hsqldb:hsql://localhost/”, “SA”, “”); con.setAutoCommit(false); String sql1 = “UPDATE CONTA SET SALDO = SALDO - 30 WHERE NUMERO = 2”; String sql2 = “UPDATE CONTA SET SALDO = SALDO + 30 WHERE NUMERO = 11”; Statement stmt1 = con.createStatement(); Statement stmt2 = con.createStatement(); stmt1.executeUpdate(sql1); stmt2.executeUpdate(sql2); con.commit(); stmt1.close(); stmt2.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); if (con != null) try { con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } } }

6. Integrando o Banco com o Sistema de Contas

Com o conhecimento da API, a integração do sistema bancário com o HyperSQL tende a ser tranquila. Lembrando nossa aplicação, o banco manipular um conjunto de contas que pode ser armazenado em diferentes estruturas de dados. Estas estruturas devem seguir

Page 65: Programação iii   volume 3 v23

65

Programação III

o contrato definido pela interface ConjuntoContas, listada a seguir.

package ufrpe.contas; public interface ConjuntoContas {

public void inserir(Conta conta) throws RepositorioException, ContaJaCadastradaException;

public void atualizar(Conta conta) throws RepositorioException, ContaInexistenteException;

public void remover(int numero) throws RepositorioException, ContaInexistenteException;

public Conta procurar(int numero) throws RepositorioException, ContaInexistenteException;

public void transferir(int contaOrigem, int contaDestino, float valor) throws RepositorioException, ContaInexistenteException, SaldoInsuficienteException;

public Conta[] getContas() throws RepositorioException;

}

É importante notar que o primeiro passo é a interface está modificada em relação às versões anteriores apresentadas em outros capítulos de nossa disciplina. Agora, todos os métodos além de levantarem exceções específicas como ContaInexistenteException ou ContaJaCadastradaException, levantam também a exceção RepositorioException. Esta exceção representa todo e qualquer problema que possa acontecer com o armazenamento das informações no meio de persistência encontrado. No caso do SGBD, tais problemas podem ocorrer pelo fato do banco não estar ativo, as tabelas não estarem criadas, dentre outros fatores. Logo, todo vez que este tipo de problema acontecer, a exceção RepositorioException precisará ser levantada. A criação desta exceção visa abstrair também o meio de armazenamento utilizado. Por exemplo, caso se opte por usar arquivos, o erro de gravação ou outro motivo qualquer também devem levantar RepositorioException. A classe RepositorioException é uma classe de exceção convencional, como listado a seguir.

package ufrpe.contas;

public class RepositorioException extends Exception {

public RepositorioException(String message) { super(message); }

}

A etapa mais trabalhosa é a criação de um conjunto de contas que utilize o SBGD para armazenar os dados da aplicação. Para isto, deve-se então definir uma classe que implemente os métodos da interface ConjuntoContas, como a classe ConjuntoContasBD, listada a seguir. Esta é uma classe razoavelmente extensa, em que explicaremos seus principais métodos.

Page 66: Programação iii   volume 3 v23

66

Programação III

package ufrpe.contas;

import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List;

import ufrpe.pessoas.Endereco; import ufrpe.pessoas.Pessoa;

public class ConjuntoContasBD implements ConjuntoContas {

private Connection con = null;

public ConjuntoContasBD() { try { con = criaConexao(); } catch (SQLException e) { System.out.println(“Problemas ao conectar com SGBD...”); System.out.println(“Encerrando aplicação...”); System.exit(0); } }

private Connection criaConexao() throws SQLException { try { Class.forName(“org.hsqldb.jdbc.JDBCDriver”); } catch (ClassNotFoundException e) { System.out.println(“Problemas ao carregar Driver JDBC...”); System.out.println(“Encerrando aplicação...”); System.exit(0); } return DriverManager.getConnection(“jdbc:hsqldb:hsql://localhost/”, “SA”, “”); }

public Conta procurar(int numero) throws ContaInexistenteException, RepositorioException {

Conta c = null; try { String sql = “SELECT * FROM CONTA, PESSOA WHERE (NUMERO = ?) AND (CONTA.CPF = PESSOA.CPF)”; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setInt(1, numero); ResultSet rs = pstmt.executeQuery();

Page 67: Programação iii   volume 3 v23

67

Programação III

if (rs.next()) { float saldo = (float) rs.getDouble(“SALDO”); int tipo = rs.getInt(“TIPO”); String cpf = rs.getString(“CPF”); String nome = rs.getString(“NOME”); char sexo = rs.getString(“SEXO”).charAt(0); int idEndCom = rs.getInt(“END_COMERCIAL”); int idEndRes = rs.getInt(“END_RESIDENCIAL”); rs.close(); pstmt.close(); String sql2 = null; PreparedStatement pstmt2 = null; ResultSet rs2 = null; double limite = 0; int diaAniv = 0; switch (tipo) { case 1: sql2 = “SELECT * FROM CONTA_ESPECIAL WHERE NUMERO = ?”; pstmt2 = con.prepareStatement(sql2); pstmt2.setInt(1, numero);/ rs2 = pstmt2.executeQuery(); if (rs2.next()) { limite = rs2.getDouble(“LIMITE”); } rs2.close(); pstmt2.close(); break;

case 2: sql2 = “SELECT * FROM POUPANCA WHERE NUMERO = ?”; pstmt2 = con.prepareStatement(sql2); pstmt2.setInt(1, numero); rs2 = pstmt2.executeQuery(); if (rs2.next()) { diaAniv = rs2.getInt(“DIA_ANIVERSARIO”); } rs2.close(); pstmt2.close(); break; } String sqlEnd = “SELECT * FROM ENDERECO WHERE ID_ENDERECO = ?”; pstmt2 = con.prepareStatement(sqlEnd); pstmt2.setInt(1, idEndRes); rs2 = pstmt2.executeQuery(); String ruaRes = null, numRes = null, bairroRes = null, cidadeRes = null, estadoRes = null, cepRes = null; if (rs2.next()) { ruaRes = rs2.getString(“RUA”); numRes = rs2.getString(“NUMERO”); bairroRes = rs2.getString(“BAIRRO”);

Page 68: Programação iii   volume 3 v23

68

Programação III

cidadeRes = rs2.getString(“CIDADE”); estadoRes = rs2.getString(“ESTADO”); cepRes = rs2.getString(“CEP”); } pstmt2 = con.prepareStatement(sqlEnd); pstmt2.setInt(1, idEndCom); rs2 = pstmt2.executeQuery(); String ruaCom = null, numCom = null, bairroCom = null, cidadeCom = null, estadoCom = null, cepCom = null;

if (rs2.next()) { ruaCom = rs2.getString(“RUA”); numCom = rs2.getString(“NUMERO”); bairroCom = rs2.getString(“BAIRRO”); cidadeCom = rs2.getString(“CIDADE”); estadoCom = rs2.getString(“ESTADO”); cepCom = rs2.getString(“CEP”); } rs2.close(); pstmt2.close(); Endereco endRes = new Endereco(ruaRes, numRes, bairroRes, cidadeRes, estadoRes, cepRes); Endereco endCom = new Endereco(ruaCom, numCom, bairroCom, cidadeCom, estadoCom, cepCom); Pessoa p = new Pessoa(nome, cpf, sexo, endRes, endCom); switch (tipo) { case 0: c = new Conta(numero, saldo, p); break; case 1: c = new ContaEspecial(numero, saldo, p, (int) limite); break; case 2: c = new Poupanca(numero, saldo, p, diaAniv); break; } } catch (SQLException e) { throw new RepositorioException(e.getMessage()); } if (c == null) throw new ContaInexistenteException(numero); return c; } public void atualizar(Conta conta) throws ContaInexistenteException, RepositorioException { procurar(conta.getNumero()); String sql = “UPDATE CONTA SET SALDO = ? WHERE (NUMERO = ?)“; PreparedStatement pstmt; try { pstmt = con.prepareStatement(sql); pstmt.setDouble(1, conta.getSaldo());

Page 69: Programação iii   volume 3 v23

69

Programação III

pstmt.setInt(2, conta.getNumero()); pstmt.executeUpdate(); } catch (SQLException e) { throw new RepositorioException(e.getMessage()); } } }

public Conta[] getContas() throws RepositorioException { String sql = “SELECT NUMERO FROM CONTA”; List<Conta> contas = new ArrayList<Conta>();

Statement stmt; try { stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { int num = rs.getInt(“NUMERO”); Conta c = procurar(num); contas.add(c); } rs.close(); stmt.close(); } catch (SQLException e) { throw new RepositorioException(e.getMessage()); } catch (ContaInexistenteException e) { } return (Conta[]) contas.toArray(); }

public void inserir(Conta conta) throws ContaJaCadastradaException, RepositorioException { try { procurar(conta.getNumero()); throw new ContaJaCadastradaException(conta); } catch (ContaInexistenteException e) { try { String sqlConsultaCPF = “SELECT * FROM PESSOA WHERE CPF = ?”; PreparedStatement pstmtConsultaCPF = con. prepareStatement(sqlConsultaCPF); System.out.println(conta.getCorrentista().getCpf()); pstmtConsultaCPF.setString(1, conta.getCorrentista().getCpf()); ResultSet rsConsultaCPF = pstmtConsultaCPF.executeQuery(); if (!rsConsultaCPF.next()) { throw new RepositorioException(“Erro de Integridade Referencial. CPF do correntista não cadastrado”); } rsConsultaCPF.close(); pstmtConsultaCPF.close(); String sqlConta = “INSERT INTO CONTA(NUMERO, TIPO, SALDO, CPF) VALUES (?,?,?,?)”; PreparedStatement pstmtConta = con.prepareStatement(sqlConta);

con.setAutoCommit(false);

Page 70: Programação iii   volume 3 v23

70

Programação III

pstmtConta.setInt(1, conta.getNumero()); int tipo = 0; if (conta instanceof ContaEspecial) { tipo = 1; } else if (conta instanceof Poupanca) { tipo = 2; } pstmtConta.setInt(2, tipo); pstmtConta.setFloat(3, conta.getSaldo()); pstmtConta.setString(4, conta.getCorrentista().getCpf()); pstmtConta.executeUpdate(); pstmtConta.close(); switch (tipo) {

case 1: String sqlContaEspecial = “INSERT INTO CONTA_ESPECIAL(NUMERO, LIMITE) VALUES (?,?)”; PreparedStatement pstmtContaEspecial = con. prepareStatement(sqlContaEspecial); pstmtContaEspecial.setInt(1, conta. getNumero()); pstmtContaEspecial.setDouble(2, ((ContaEspecial) conta).getLimite()); pstmtContaEspecial.executeUpdate(); pstmtContaEspecial.close(); break;

case 2: String sqlPoupanca = “INSERT INTO POUPANCA(NUMERO, DIA_ANIVERSARIO, TAXA_JUROS) VALUES (?,?,?)”; PreparedStatement pstmtPoupanca = con. prepareStatement(sqlPoupanca); pstmtPoupanca.setInt(1, conta.getNumero()); pstmtPoupanca.setInt(2, ((Poupanca) conta). getDiaAniversario()); pstmtPoupanca.setDouble(3, 10); pstmtPoupanca.executeUpdate(); pstmtPoupanca.close(); break;

} con.commit(); con.setAutoCommit(true); } catch (SQLException e3) {try { if (con != null) con.rollback(); } catch (SQLException e1) { throw new RepositorioException(e.getMessage()); } throw new RepositorioException(e.getMessage()); }

Page 71: Programação iii   volume 3 v23

71

Programação III

} }

public void remover(int numero) throws ContaInexistenteException, RepositorioException {

Conta c = procurar(numero); try { con.setAutoCommit(false);

String sql = null; PreparedStatement pstmt = null; if (c instanceof ContaEspecial) { sql = “DELETE FROM CONTA_ESPECIAL WHERE (NUMERO = ?) “; pstmt = con.prepareStatement(sql); pstmt.setInt(1, numero); pstmt.executeUpdate(); } if (c instanceof Poupanca) { sql = “DELETE FROM POUPANCA WHERE (NUMERO = ?) “; pstmt = con.prepareStatement(sql); pstmt.setInt(1, numero); pstmt.executeUpdate(); } sql = “DELETE FROM CONTA WHERE (NUMERO = ?) “; pstmt = con.prepareStatement(sql); pstmt.setInt(1, numero); pstmt.executeUpdate(); con.commit(); pstmt.close(); con.setAutoCommit(true); } catch (SQLException e) { throw new RepositorioException(e.getMessage()); } }

public void transferir(int contaOrigem, int contaDestino, float valor) throws ContaInexistenteException, RepositorioException { procurar(contaOrigem); procurar(contaDestino); try { con.setAutoCommit(false);

String sql = “UPDATE CONTA SET SALDO = SALDO - ? WHERE (NUMERO = ?) “; PreparedStatement pstmt; pstmt = con.prepareStatement(sql); pstmt.setDouble(1, valor); pstmt.setInt(2, contaOrigem); pstmt.executeUpdate(); sql = “UPDATE CONTA SET SALDO = SALDO + ? WHERE (NUMERO = ?) “; pstmt = con.prepareStatement(sql); pstmt.setDouble(1, valor);

Page 72: Programação iii   volume 3 v23

72

Programação III

pstmt.setInt(2, contaDestino); pstmt.executeUpdate(); con.commit(); pstmt.close(); con.setAutoCommit(true); } catch (SQLException e) { try { con.rollback(); } catch (SQLException e1) { throw new RepositorioException(e.getMessage()); } throw new RepositorioException(e.getMessage()); } } }

Ao criar a classe, o construtor já estabelece uma conexão com o SGBD. Para evitar que a conexão seja aberta e fechada o tempo todo, o objeto con representa a conexão que vai ser utilizada pela classe enquanto ela estiver ativa. Para isto, o método criarConexao() é definido, cuja função única é estabelecer a conexão com o banco.

O método procurar é aquele que busca do banco as informações necessárias para se criar um objeto Conta. A busca é feito de acordo com o número da conta. Para se criar o objeto conta é necessário obter todas as informações necessárias para se criar um objeto Conta. Portanto, primeiramente é feita uma busca na tabela conta, procurando por algum registro cujo o número da conta seja igual àquele passado pelo método. Caso não seja encontrado, é levantada a exceção ContaInexistenteException. Caso contrário, as informações sobre o correntista e seus endereços são obtidas das tabelas PESSOA e ENDERECO, respectivamente. Se forem necessárias, informações adicionais devem ser obtidas das tabelas CONTA_ESPECIAL e POUPANCA. Quando todas as informações forem obtidas, é possível criar o objeto para retorno do método. É importante frisar que, de acordo com o tipo da conta, deve-se instanciar contas convencionais, especiais ou poupanças.

O método procurar é reaproveitado por outros métodos, como o remover, atualizar, transferir ou mesmo o getContas. Isto porque todos estes métodos precisam verificar antes se uma ou mais contas existem para realizar atualizações nas tabelas. No caso do método remover, um cuidado especial é que deve-se utilizar transações para remover contas especiais e poupanças. Esta necessidade se dá porque a remoção deste tipo de conta requer que seja removidos registros de mais de uma tabela. Por isso, no início do método o modo de operação da conexão é modificada para autocommit false. No final do método, se por acaso forem necessários dois comandos DELETE, é feito um commit da transação. Procedimento semelhante é utilizado para a operação de transferir, onde são feitos duas atualizações, como já explicado anteriormente.

No caso da operação de atualização, por questões de simplificação, o único campo que pode ser alterado de uma conta é o seu saldo. Em outras palavras, uma conta uma vez criada não pode mudar seu correntista ou tipo. Isto é uma restrição apenas para facilitar a compreensão deste exemplo.

O método mais longo é o inserção de contas, o inserir. O método pega as informações do objeto conta repassado e realiza operações de inserção nas tabelas CONTA, CONTA_ESPECIAL (quando necessário) e POUPANCA (quando necessário). Para cadastrar uma conta, é necessário que as informações sobre o correntista e os endereços deste correntista já tenham sido cadastradas no banco. Isso remete a idéia que o sistema deve

Page 73: Programação iii   volume 3 v23

73

Programação III

fornece cadastros independentes para informações sobre pessoas e endereços, o que é um projeto comum nas aplicações atuais. Primeiro cadastra-se as informações menos dependentes para depois as mais dependentes. Logo, ao se cadastrar uma conta, verifica-se se existe na tabela PESSOA se existe um registro com o cpf do correntista do objeto Conta. Caso isso não ocorra, é levantada uma exceção RepositorioException, com a descrição de erro de integridade referencial. Se não houver problemas, são realizados operações de inserção nas tabelas relativas a dados de contas. Novamente nestes casos, estas inserções são tratadas como transações, deixando para o programador a responsabilidade de efetivar a inclusão dos registros ao final do método.

A alteração para utilização de SGBD requer também que alterações sejam feitas em outras classes. A classe que utiliza o conjunto de contas, por exemplo, realiza operações que precisam ser efetivadas na base. Tome como exemplo a operação de debitar, listada a seguir

private ConjuntoContas conjunto = new ConjuntoContasDB();

public void atualizar(Conta conta) throws ContaInexistenteException, RepositorioException { conjunto.atualizar(conta); }

public void debitar(int numero, float valor) throws SaldoInsuficienteException, ContaInexistenteException, RepositorioException { Conta c = procurar(numero); if (c != null) { c.debitar(valor); atualizar(c); } }

Veja que a realização da operação de débito é feita apenas no objeto Conta. Logo, para que sua modificação seja também realizada no banco, é necessário atualizar a conta. Isto é válido para todas aquelas operações que anteriormente eram realizadas apenas nos objetos em memória.

Exercícios

1. O Diagrama de classes abaixo representa os conceitos para o sistema de controle de mídias do capítulo 2.

Page 74: Programação iii   volume 3 v23

74

Programação III

Para um sistema em Java que controle a persistência dos dados, 3 interfaces foram projetadas:

public interface ConjuntoCantor {

// Insere um cantor na base de dados public void inserir(Cantor cantor);

// Retorna um objeto que representa o cantor, baseado no código do mesmo public Cantor procurar(int numero) throws CantorInexistenteException;

// Retorna um objeto que representa o cantor, baseado no nome do mesmo public Cantor procurar(String nome) throws CantorInexistenteException;

}

public interface ConjuntoMusica {

// Insere uma música na base public void inserir(Musica musica);

// Permite alterar o nome e o tempo de uma música na base public void atualizar(Musica musica) throws MusicaInexistenteException;

// Retorna um objeto que representa uma música, baseado no código da mesma public Musica procurar(int numero) throws MusicaInexistenteException;

}

public interface ConjuntoMidia {

// Insere uma música na base public void inserir(Midia midia);

//Remove uma mídia e todas as suas músicas da base public void remover(int numero) throws MidiaInexistenteException;

//Retorna uma mídia da base, de acordo com seu número public Midia procurar(int numero) throws MidiaInexistenteException;

}

De acordo com as interfaces e o diagrama de classes apresentados, implemente versões para persistência das informações utilizando o banco de dados HyperSQL. Baseie-se no exemplo apresentado no final deste capítulo para realizar este exercício.

Vamos Revisar?

Page 75: Programação iii   volume 3 v23

75

Programação III

Resumo

Neste último capítulo, vimos como é possível integrar nossos programas com bases de dados, por meio da API JDBC. Atualmente, existem abordagens mais flexíveis e que procuram deixar o mapeamento entre objetos e tabelas mais transparente. A especificação JPA (Java Persistence API) é a principal referência para a modelagem objeto-relacional na atualidade. Com os conhecimentos iniciados neste volume, você tem toda uma base que lhe servirá para estudos mais avançados sobre a integração entre SGBDs e sistemas orientados a objetos.

Page 76: Programação iii   volume 3 v23

76

Programação III

Conheça o Autor

Fernando Trinta

Sou professor de ciência de computação, formado pela Universidade Federal do Maranhão. Tenho Mestrado e Doutorado em Ciência da Computação pelo Centro de Informática da Universidade Federal de Pernambuco, com ênfase na área de Sistemas Distribuídos. Durante minha pós-graduação, estive envolvido com os temas de objetos distribuídos e educação à distância no Mestrado, e jogos digitais, middleware e computação ubíqua no Doutorado. Trabalhei no desenvolvimento de sistemas em várias empresas, privadas e públicas. Atualmente faço parte do corpo docente do Centro de Informática da Universidade Federal de Pernambuco. Além da informática, gosto muito de esportes em geral e cinema. Mas nos últimos anos, duas novas paixões tomaram conta do meu mundo: Ian e Ananda.