Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Definio do Problema
um fato corriqueiro que pessoas que possuem uma biblioteca particular emprestem livros aos
amigos. E infelizmente bastante comum que os amigos dificilmente devolvam as obras, e as
pessoas terminam mesmo por esquecer a quem foram emprestadas. Pensando nisso, o autor prope
o desenvolvimento de um pequeno aplicativo que registre as obras mantidas em uma biblioteca
pessoal. E, para evitar o esquecimento, que os dados dos emprstimos sejam tambm registrados.
Anlise do Problema
O primeiro passo na anlise identificar o domnio do problema, que consiste em definir as classes
e seus relacionamentos. A partir da descrio do problema, pode-se inferir que existem as entidades
Livro e Emprestimo. Sabendo-se que um Livro possui pelo menos um autor, determinaremos
tambm como parte do domnio, a entidade Autor. Quanto aos relacionamentos, pode-se afirmar
que um livro tem um ou mais autores e um autor pode escrever mais de um livro. Define-se tambm
que um livro pode ser emprestado mais de uma vez. Dessa maneira criamos um modelo do domnio,
mostrado na Figura 1.
Utilizamos a UML (Unified Modeling Language) para criar o diagrama de classes que ir
representar o modelo do domnio do problema. Assim, conforme a Figura 1, foram representadas
as classes, seus relacionamentos e multiplicidades. Note que a multiplicidade do relacionamento
entre Autor e Livro determina que um autor pode ter um ou mais livros publicados e um livro deve
ter pelo menos um autor. Um livro pode ter um ou muitos emprstimos e um emprstimo feito
para um nico livro.
2/19
O passo seguinte consiste em definir dados relevantes em cada uma das classes identificadas. Na
classe Autor, por exemplo, consideramos como importante o nome do mesmo. Na classe Livro,
identificamos os campos isbn (um nmero identificador nico para livros), titulo, ano de edio,
edio e editora. Nome do emprestador, data do emprstimo e data da devoluo so dados
importantes a serem registrados na classe Emprestimo.
Na etapa seguinte do processo ser apresentada uma possvel soluo. o que podemos chamar de
um projeto.
Soluo Proposta
Antes de projetar a soluo do problema deve-se pensar na persistncia dos dados, e uma
alternativa muito utilizada pelos desenvolvedores o padro DAO (Data Access Object). DAO
um padro de persistncia que permite separar as regras de negcio das regras de acesso ao banco
de dados. Ele funciona tanto para pegar os objetos Java, e envi-los ao banco de dados usando
comandos SQL, quanto para buscar os dados do banco e convert-los em objetos que possam ser
tratados pela aplicao.
Para se conseguir implementar esse padro, uma das tarefas do processo mapear as classes para
entidades do modelo relacional. Mas falaremos sobre isso com mais detalhes em seguida. Por ora
apenas consideramos necessrio fazer uma modificao no modelo do domnio, para que o
mapeamento seja feito adequadamente. A modificao consiste em mudar o relacionamento entre
Autor e Livro para agregao, e adicionar um atributo do tipo Set para receber os autores na classe
Livro. Essa modificao visa facilitar a implementao da soluo, pois um relacionamento muitos-
para-muitos requer a criao de uma entidade associativa no banco de dados, para representar o
relacionamento. Dessa forma, quando salvarmos os dados de um Livro, a classe persistente se
encarregar tambm de salvar os autores na tabela associativa. Isso ficar mais claro quando forem
apresentadas as implementaes das classes persistentes. Na Figura 2 mostramos esse novo
modelo, j includos os atributos.
Observe que, alm dos atributos identificados anteriormente, foi definido na classe Livro o campo
situacao, responsvel para indicar se um livro est emprestado ou no. Ser considerado o seguinte:
se o livro estiver emprestado, situacao ter o valor 2, caso contrrio, assumir o valor 1.
Agora que j se tem um modelo de classes, podemos definir como ser o modelo ER (Entidade
Relacionamento) correspondente ao mapeamento objeto-relacional.
Para obter esse novo modelo, precisamos rever algumas estratgias do mapeamento objeto-
relacional, de uma perspectiva bastante simples, voltada para a soluo do nosso problema atual:
Um atributo de uma classe pode ser mapeado para zero ou mais colunas em um banco de dados
relacional. O mais comum o mapeamento um-para-um, no entanto, pode-se citar dois
extremos. Um caso quando se tem um atributo de classe que no persistente, tal como a
mdia de compras da classe Cliente. Visto que esse campo pode ser calculado, ento no
precisa ser salvo no banco de dados. O outro extremo quando um atributo um objeto, tal
como Endereco na classe Cliente. Isso levaria a uma associao entre duas classes e
consequentemente seria necessrio mapear os atributos de Endereco;
Em geral cada classe mapeada para uma tabela. No entanto existem casos em que isso no
acontece, tal como nos relacionamentos de generalizao/especializao, que no estudaremos
neste artigo;
Nos relacionamentos um-para-muitos, o mapeamento feito definindo-se uma chave
estrangeira na tabela muitos. Na situao do problema ora exposto, as classes Livro e
Emprestimo so um exemplo disso. Dessa forma, no modelo relacional, a tabela Emprestimo
ter uma chave estrangeira referenciando a tabela Livro;
4/19
Aps essas consideraes pode-se ento apresentar o modelo ER, mostrado na Figura 3, derivado
do diagrama de classes da Figura 2.
Pode-se observar na Figura 3 que foram definidas chaves primrias para as entidades, pois essa
tambm uma tarefa do mapeamento objeto-relacional. Nesse exemplo, preferimos escolher
nmeros sequenciais sem significado nas regras do negcio para as chaves. A outra alternativa
escolher chaves naturais atributos que fazem parte do objeto tal como isbn da classe Livro. Um
artigo, citado nos links, discute essa questo de escolher a chave primria das entidades.
O passo seguinte no projeto corresponde a definir as classes necessrias para implementar a
persistncia na aplicao. Uma soluo muito utilizada o padro DAO (Data Access Object).
DAO implementa o encapsulamento e a centralizao do acesso a fontes de dados banco de
dados, arquivos texto, XML, entre outros. Tal implementao permite que se separe a lgica de
negcio da persistncia. A camada de lgica de negcio consiste das classes do sistema, que no
presente exemplo correspondem a Autor, Livro e Emprestimo. J a camada de persistncia se
refere s classes responsveis pelo acesso s fontes de dados.
No entanto, deixaremos essa soluo para outra matria em virtude da sua complexidade, posto
que isso nos foraria a expor toda a sua teoria antes de apresentar a soluo. Por ora, ento, ser
utilizada uma proposta apresentada no artigo Solucionando problemas usando Java, publicado na
Easy Java Magazine n 4.
Na soluo que foi apresentada nesse artigo foram criadas a classe Conexao, responsvel por fazer
a conexo com o banco de dados, e uma classe persistente para cada tabela no diagrama ER. Com
isso chegou-se ao diagrama de classes persistentes mostrado na Figura 4.
Veja nesta figura que foi definida uma interface denominada GenericDB, a qual servir para
determinar os mtodos que devero ser obrigatoriamente implementados pelas classes AutorDB,
LivroDB e EmprestimoDB. O leitor deve ter notado que no criamos uma classe correspondente
entidade AutorLivro que foi utilizada para implementar o relacionamento muitos-para-muitos
5/19
entre Livro e Autor. Esta deciso foi tomada porque a gravao/alterao dos dados na tabela
AutorLivro ser feita atravs dos mtodos correspondentes da classe LivroDB.
Para concluir esta fase, definiremos que a interface com o usurio ser construda usando Swing e
o banco de dados ser o Apache Derby. A opo pelo Derby se deve ao fato de que esta uma
aplicao simples e de uso pessoal, no exigindo maiores recursos, e que pode ser implementada
utilizando um banco de dados embutido.
Codificao da Soluo
Antes de comear a implementao necessrio preparar o ambiente, que consiste
fundamentalmente em obter o driver JDBC para o Apache Derby e depois configur-lo no IDE que
ser utilizado que neste caso ser o Eclipse. Se o leitor estiver acompanhando o desenvolvimento,
importante destacar que qualquer banco de dados pode ser usado em substituio ao Derby, desde
que voc obtenha o driver JDBC correspondente.
Acesse o endereo do Apache Derby que consta na relao de links, baixe o pacote do banco de
dados e instale-o no diretrio de sua preferncia.
Em seguida crie um projeto Java no Eclipse, denominando-o BibliotecaPessoal, por exemplo.
Aps o projeto ter sido criado, precisamos adicionar a biblioteca do banco de dados. Para isso, com
o projeto selecionado, pressione ALT + ENTER. Essa ao ir abrir a janela de Propriedades do
Projeto, de acordo como mostra a Figura 5.
Nessa janela, selecione na caixa do lado esquerdo o item Java Build Path e, logo aps, escolha a
aba Libraries. Feito isso, clique no boto Add External JARs. Localize ento o arquivo Derby.jar
sob a pasta lib localizada no diretrio onde a biblioteca do banco de dados foi instalada. Por fim,
pressione o boto Abrir e depois o boto OK para confirmar a incluso da biblioteca.
Concluda esta parte podemos partir para o desenvolvimento propriamente dito. Inicialmente
vamos dividir nosso projeto em trs pacotes: br.com.biblioteca.dominio,
br.com.biblioteca.persistencia e br.com.biblioteca.ui. Portanto, crie esses trs pacotes no menu
de contexto do projeto. Para fazer isso, pressione o boto direito do mouse sobre o nome do projeto
e selecione New | Package. Digite o nome do pacote e pressione Finish.
Criao das classes de domnio
As primeiras classes a serem criadas so as classes do domnio do problema: Autor, Livro e
Emprestimo. Na Listagem 1 mostramos o cdigo da classe Autor. A criao desta classe deve ser
feita no pacote br.com.biblioteca.dominio. Portanto, acesse o menu de contexto sobre esse pacote
6/19
e escolha New | Class. Na janela que ser aberta digite o nome da classe e pressione Finish. Em
seguida digite o cdigo da Listagem 1 no editor.
public Autor() {}
@Override
public String toString() {
return "Autor [id=" + id + ", nome=" + nome + "]";
}
@Override
public int hashCode() {
return this.nome.hashCode();
}
7/19
@Override
public boolean equals(Object obj) {
return true;
}
Note que foi definido o campo id de tipo int. Isso importante para que o mapeamento funcione
adequadamente, visto que esta tabela no banco de dados relacional possuir uma chave primria que
corresponder a esse atributo no objeto. As demais classes do domnio do problema igualmente
incluiro um atributo chamado id com o mesmo objetivo.
A classe Autor tambm implementa a interface Comparable que define o mtodo compareTo()
e sobrescreve os mtodos hashCode() e equals() de Object. A justificativa para isso ser dada
mais adiante.
Aps isso, repete-se o mesmo procedimento para criar as classes Livro e Emprestimo, cujas
implementaes so apresentadas nas Listagens 2 e 3, respectivamente.
import java.util.HashSet;
import java.util.Set;
public Livro() {
this.autores = new HashSet<Autor>();
}
public Livro(int id, String isbn, String titulo, int anoEdicao, int edicao,
String editora, Set<Autor> autores, char situacao)
{
this.id = id;
this.isbn = isbn;
this.titulo = titulo;
this.anoEdicao = anoEdicao;
this.edicao = edicao;
this.editora = editora;
this.autores = new HashSet<Autor>();
this.situacao = situacao;
}
@Override
public String toString() {
8/19
Na classe Livro precisa ser destacado o campo autores do tipo Set. A explicao para isso iniciou
com a apresentao da Figura 2, onde falamos como seria implementado o relacionamento muitos-
para-muitos. A deciso de escolher a interface Set porque este tipo de conjunto no permite
objetos duplicados, e no queremos que um livro tenha autores repetidos.
Essa caracterstica de Set, no entanto, requer que a classe dos objetos que so adicionados a essa
collection implemente a interface Comparable para definir como dois objetos sero comparados.
Alm disso, visto que se optou por usar a implementao HashSet da interface Set, deve-se definir
os mtodos hashCode() e equals(). Isso fundamental porque HashSet precisa do cdigo hash
para inserir e depois localizar objetos na collection.
Observando a classe Autor, vemos que o cdigo dos mtodos citados muito simples. No entanto,
essa simplicidade se deve ao fato de que a classe tem apenas o atributo nome do tipo String. Por
causa disso, apenas usamos os mtodos correspondentes j disponveis em String. Mas, deve-se
destacar que o Eclipse oferece assistentes para implementar equals() e hashCode(), no menu
Source | Generate hashCode and equals. Sugere-se usar esses assistentes, caso o leitor precise
implementar tais mtodos em classes que possuam mais atributos.
Consulte a Easy Java Magazine n 1 para estudar um pouco mais sobre collections e obter mais
detalhes sobre a interface Set e sua implementao HashSet.
import java.util.Date;
public Emprestimo() {}
@Override
public String toString() {
return "Emprestimo [nomePessoa=" + nomePessoa + ", dataEmprestimo="
+ dataEmprestimo + ", dataDevolucao=" + dataDevolucao + "]";
}
}
9/19
A classe Emprestimo pode ser vista na Listagem 3, mas nada merece ateno especial nesse
cdigo.
Criao das classes persistentes
O passo seguinte no processo de desenvolvido constitui-se da criao da interface e classes
necessrias para a persistncia: Conexao, GenericDB, AutorDB, LivroDB e EmprestimoDB.
Essas classes devem ser criadas no pacote br.com.bibliotecapessoal.persistencia.
Na Listagem 4 pode ser vista a classe Conexao, responsvel por fazer a conexo com o banco de
dados. Os membros desta classe so definidos como static de maneira que no seja necessrio criar
um objeto para que o mtodo criarConexao() seja executado. O atributo URL define a url de
conexo com o Derby, onde, alm do driver JDBC, informamos o nome do banco de dados a ser
criado biblioteca. A propriedade create=true define que o banco ser criado na primeira conexo,
se ele no existir. Visto que o caminho do banco no foi informado na url, o banco de dados ser
criado no diretrio corrente. Nesse caso, no prprio diretrio do projeto Eclipse.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
Com o objetivo de definir o contrato que as classes persistentes devero cumprir, optou-se por
criar uma interface, a qual denominamos GenericDB. Tal interface declara quais so os mtodos
que devero ser implementados pelas classes AutorDB, LivroDB e EmprestimoDB. O cdigo da
interface mostrado na Listagem 5.
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
importante observar que GenericDB uma interface genrica. Por isso ela foi definida com os
parmetros T e ID, onde T poder assumir qualquer classe de domnio e ID ser o tipo do id da
classe persistente correspondente. Definida dessa maneira, esta interface poder ser implementada
por qualquer classe persistente da aplicao.
import br.com.bibliotecapessoal.persistencia.Conexao;
import br.com.bibliotecapessoal.dominio.Autor;
//imports omitidos...
public AutorDB() {
try {
con = Conexao.criarConexao();
DatabaseMetaData dbmd = con.getMetaData();
// verifica se a tabela AUTOR j existe no BD
rs = dbmd.getTables(null, null, "AUTOR", new String[]{"TABLE"});
if (!rs.next()) {
stm = con.createStatement();
// se no houver uma tabela, ela criada
stm.executeUpdate("CREATE TABLE autor (id int generated always as identity, nome
VARCHAR(40))");
}
} catch (SQLException e) {
System.out.println("Erro ao criar conexo/tabela");
} finally {
// fecha os recursos e a conexo com o BD
this.fecha(rs, stm, con);
}
}
}
11/1
9
}
}
if (stm != null) {
try {
stm.close();
} catch (SQLException e){
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e){
}
}
}
}
12/1
9
A classe persistente AutorDB apresentada na Listagem 6. Veja que esta classe implementa a
interface GenericDB e deve definir os tipos genricos declarados, os quais so Autor e Integer. Os
tipos declarados correspondem respectivamente classe de domnio mapeada pela classe persistente
e o tipo de dado da chave primria da tabela. Note tambm que a chave primria foi definida como
Integer, porque em generics apenas so permitidas classes e no tipos primitivos. Portanto, mesmo
que o mtodo buscarPorID() receba um Integer, ele o trata como int devido operao unboxing,
descrita no artigo Explorando as classes Wrapper na Easy Java Magazine n 17.
Nesta classe destaca-se que o construtor de AutorDB verifica se existe uma tabela com o nome
AUTOR no banco de dados. Caso no exista, a tabela criada. A verificao da existncia da
tabela responsabilidade do mtodo getTables() da interface DatabaseMetaData. Esta interface
deve ser implementada pelo driver JDBC com o objetivo de obter informaes sobre os metadados
do sistema gerenciador de banco de dados ao qual a aplicao est conectada. Alm da relao de
tabelas definidas no banco, pode-se retornar a chave primria e as colunas criadas em cada tabela, o
nome e a verso do banco do banco de dados, entre outras.
Outra observao importante que o mtodo buscarTodos() retorna um List, que uma interface.
Isto considerado uma boa prtica de programao, pois o usurio tem a liberdade de usar a
implementao da interface que julgar adequada, quando chamar o mtodo. Por exemplo, se
quisermos usar um ArrayList como uma lista de autores na aplicao, pode-se escrever o seguinte
cdigo: ArrayList<Autor> lista = (ArrayList<Autor>) autorDB.buscarTodos(), onde autorDB
uma instncia de AutorDB. Comentrios mais detalhados sobre a implementao da classe podem
ser encontrados na prpria Listagem 6.
Listagem 7. Cdigo da classe LivroDB.
package br.com.bibliotecapessoal.persistencia;
//imports omitidos...
import br.com.bibliotecapessoal.dominio.Autor;
import br.com.bibliotecapessoal.dominio.Livro;
public LivroDB() {
try {
con = Conexao.criarConexao();
DatabaseMetaData dbmd = con.getMetaData();
// verifica se a tabela LIVRO j existe no BD
rs = dbmd.getTables(null, null, "LIVRO", new String[] { "TABLE" });
if (!rs.next()) {
stm = con.createStatement();
// se no houver uma tabela, ela criada
String s = "CREATE TABLE livro (id int not null generated always as identity primary key,
isbn varchar(20), " +
"titulo varchar(100), anoedicao int, edicao int, editora varchar(100), situacao
char(1))";
stm.executeUpdate(s);
// verifica se a tabela LIVROAUTOR j existe no BD
rs = dbmd.getTables(null, null, "AUTORLIVRO", new String[] { "TABLE" });
if (!rs.next()) {
stm = con.createStatement();
// se no houver uma tabela, ela criada
s = "CREATE TABLE AUTORLIVRO (ID_AUTOR INT REFERENCES AUTOR, "
+ "ID_LIVRO INT REFERENCES LIVRO)";
stm.executeUpdate(s);
}
}
} catch (SQLException e) {
System.out.println("Erro ao criar conexo/tabela LIVRO");
}
finally {
// fecha os recursos e a conexo com o BD
this.fecha(rs, stm, con);
13/1
9
}
}
}
catch (SQLException e) {
System.out.println("Erro ao inserir na tabela LIVRO/AUTORLIVRO");
}
finally {
this.fecha(rs, stm, con);
}
catch (SQLException e) {
System.out.println("Erro ao modificar a tabela");
}
finally {
this.fecha(rs, stm, con);
}
rs = stm.executeQuery(s);
while (rs.next()) {
int idAutor = rs.getInt("id");
String nomeAutor = rs.getString("nome");
autores.add(new Autor(idAutor, nomeAutor));
}
livro = new Livro(id, isbn, titulo, anoEdicao, edicao, editora,
autores, situacao);
}
}
catch (SQLException e) {
System.out.println("Erro ao consultar na tabela LIVRO");
}
finally {
this.fecha(rs, stm, con);
}
return livro;
}
A seguir, falaremos sobre a classe LivroDB, a qual pode ser vista na Listagem 7. O que difere
LivroDB de AutorDB que, como ser constatado, na Listagem 7 existe cdigo referente ao
tratamento dos dados dos autores de um livro. No construtor, por exemplo, so criadas se no
existirem tanto a tabela Livro quanto a tabela LivroAutor.
Visto que foi decidido que um livro composto de autores, foi preciso implementar nos mtodos
inserir(), modificar() e excluir() uma parte do cdigo responsvel por gravar/excluir os autores.
Dessa maneira garante-se que, ao excluir um livro por exemplo exclui-se tambm seus autores,
para manter a integridade do banco de dados.
Nos mtodos buscarTodos() e buscarPorID() que fazem pesquisa de livros no banco de dados
foi necessrio implementar uma parte correspondente busca dos autores de um livro. As buscas
de autores sempre retornam um Set, de acordo com o atributo definido na classe Livro. Verifique
mais detalhes da codificao nos comentrios da Listagem 7.
A classe EmprestimoDB a ltima classe persistente a ser analisada. Alm do que j foi
comentado para as classes anteriores, ressaltaremos apenas o tratamento dos campos do tipo Date.
Isso importante destacar porque o banco de dados Derby requer um campo data no formato
MM/dd/yyyy informado como caractere. Por isso foi utilizada a classe SimpleDateFormat, cuja
finalidade formatar datas. Dessa maneira, foi criado um objeto com o formato MM/dd/yyyy,
denominado dateFormat, o qual chamar o mtodo format() encarregado de efetivar a formatao
da data.
16/1
9
//imports omitidos...
import br.com.bibliotecapessoal.dominio.Emprestimo;
import br.com.bibliotecapessoal.dominio.Livro;
public EmprestimoDB() {
try {
con = Conexao.criarConexao();
DatabaseMetaData dbmd = con.getMetaData();
// verifica se a tabela EMPRESTIMO j existe no BD
rs = dbmd.getTables(null, null, "EMPRESTIMO", new String[] { "TABLE" });
if (!rs.next()) {
stm = con.createStatement();
// se no houver uma tabela, ela criada
String s = "CREATE TABLE EMPRESTIMO (ID INT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY
KEY," +
" ID_LIVRO INT REFERENCES LIVRO, NOME_PESSOA VARCHAR(40), DATA_EMPRESTIMO DATE,
DATA_DEVOLUCAO DATE)";
stm.executeUpdate(s);
}
}
catch (SQLException e) {
System.out.println("Erro ao criar conexo/tabela EMPRESTIMO");
}
finally {
// fecha os recursos e a conexo com o BD
this.fecha(rs, stm, con);
}
}
Este procedimento foi implementado tanto no mtodo inserir() quanto em modificar(). A classe
inclui alguns comentrios em seu corpo de maneira que o leitor pode obter mais informaes sobre
sua codificao.
Nas classes LivroDB e EmprestimoDB no foi apresentado o cdigo do mtodo fecha(), o qual
foi mostrado em AutorDB.
Com isso encerramos esta parte da matria, deixando para a prxima a criao da interface grfica
que far uso das classes aqui criadas.
Concluses
Costumamos insistir em nossos artigos que tratam da soluo de problemas, sobre a importncia
de ter um entendimento da questo que est sendo apresentada e a necessidade de seguirmos um
mtodo que oriente o desenvolvimento. O mtodo que empregamos neste e em outros artigos
anteriores no pretende substituir os processos de Engenharia de Software, mas um bom ponto de
partida para o desenvolvedor adquirir a prtica da anlise de requisitos.
Para o problema ora exposto, buscamos apresentar uma soluo que, longe de ser a melhor,
funciona adequadamente para os objetivos da matria, que implementar uma aplicao usando
interface grfica do usurio e que seja simples.
Nesta primeira parte do artigo buscou-se implementar as classes de domnio e as persistentes,
deixando para a prxima a criao da interface grfica. Igualmente deixamos para a parte final da
matria a integrao entre as camadas: classes de domnio, classes persistentes e classes de interface
grfica do usurio.
Links
agiledata.org/essays/mappingObjects.html
Estudo em detalhes do mapeamento objeto-relacional.
agiledata.org/essays/keys.html
Discusso sobre a escolha da chave primria para uma tabela em um banco de dados relacional.
oracle.com/technetwork/java/dataaccessobject-138824.html
Definio de DAO.
db.apache.org/derby/index.html
Site oficial do Apache Derby.
ibm.com/developerworks/webservices/library/ws-tip-objrel4/
Uso de collections para implementar relacionamentos muitos-para-muitos.