Anda di halaman 1dari 35

Sumrio

06 - Estrutura de dados: Primeiros passos com mtodos de busca


[ Paulo Afonso Parreira Jnior ]

Contedo sobre Boas Prticas

16 - Vantagens e desvantagens dos padres de projeto Singleton e Flyweight


[ Ednardo Luiz Martins e Ricardo da S. Longa ]

Contedo sobre Boas Prticas

23 - Hibernate Envers: Auditoria de dados em Java


[ Carlos Alberto Silva ]
Assine agora e tenha acesso a
todo o contedo da DevMedia:
www.devmedia.com.br/mvp

Edio 52 2015 ISSN 2173625-4

Fale com o Editor!


Atendimento ao leitor
EXPEDIENTE A DevMedia possui uma Central de Atendimento on-line, onde voc muito importante para a equipe saber o que voc est achando da revista:
que tipo de artigo voc gostaria de ler, que artigo voc mais gostou e qual
pode tirar suas dvidas sobre servios, enviar crticas e sugestes e
artigo voc menos gostou. Fique a vontade para entrar em contato com os
falar com um de nossos atendentes. Atravs da nossa central tambm editores e dar a sua sugesto!
Editor possvel alterar dados cadastrais, consultar o status de assinaturas Se voc estiver interessado em publicar um artigo na revista ou no site
e conferir a data de envio de suas revistas. Acesse www.devmedia. Easy Java Magazine, entre em contato com o editor, informando o ttulo e
Eduardo Spnola (eduspinola@gmail.com)
com.br/central, ou se preferir entre em contato conosco atravs do mini-resumo do tema que voc gostaria de publicar:
telefone 21 3382-5038.
Consultor Tcnico Diogo Souza (diogosouzac@gmail.com)
Publicidade
publicidade@devmedia.com.br 21 3382-5038 Eduardo Oliveira Spnola
Produo
eduspinola.wordpress.com
Jornalista Responsvel Kaline Dolabella - JP24185 Anncios Anunciando nas publicaes e nos sites do Grupo DevMedia,
voc divulga sua marca ou produto para mais de 100 mil desenvolvedores @eduspinola / @Java_Magazine
Capa e Diagramao Romulo Araujo de todo o Brasil, em mais de 200 cidades. Solicite nossos Media Kits, com
detalhes sobre preos e formatos de anncios.
Estrutura de dados: Primeiros passos com mtodos de busca

Estrutura de dados: Primei-


ros passos com mtodos
de busca
Aprenda a recuperar informaes em Java a partir
da implementao dos principais mtodos de
busca da estrutura de dados

E Fique por dentro


ste artigo trata da recuperao de dados a partir
de um conjunto de informaes previamente
armazenado. Em geral, no meio computacional, Este artigo til para todo desenvolvedor que pretende expandir
a informao dividida em registros e cada registro seus conhecimentos sobre mtodos de pesquisa (ou mtodos de
possui uma chave para ser usada na pesquisa e uma ou busca). Para isso, sero apresentados os conceitos bsicos sobre trs
mais informaes de interesse do usurio. Por exemplo, o conhecidos mtodos de pesquisa: pesquisa sequencial, pesquisa
registro de um aluno em uma universidade pode conter binria e pesquisa por tabela Hash. Juntamente com os mtodos
uma chave que identifica unicamente a matrcula e um de pesquisa, trechos de cdigo sero analisados, ilustrando a
conjunto de informaes sobre este aluno, como nome, implementao de tais mtodos na linguagem Java. Alm disso,
endereo, telefone, entre outros. comentrios sobre a eficincia de cada mtodo, bem como sobre
O objetivo da pesquisa encontrar uma ou mais os cenrios nos quais eles podem ser aplicados com sucesso so
ocorrncias de registros com chaves iguais chave discorridos ao longo do texto.
de pesquisa e para essa finalidade existem vrios
mtodos. A escolha do mais adequado depende, prin-
cipalmente: (i) da quantidade de dados envolvidos; e A partir disso, neste artigo analisaremos conceitos, na teoria e na
(ii) da possibilidade de o arquivo sofrer inseres e/ prtica, da pesquisa interna (ou busca interna), na qual assume-se
ou retiradas. que o conjunto de dados a ser pesquisado pequeno o suficiente
Por exemplo, diferente encontrar o registro do nome para ser carregado de uma vez na memria principal (ou memria
de um estado brasileiro no conjunto de todos os estados interna) do computador. Quando a quantidade de informaes
brasileiros e encontrar o registro de um aluno que fez o grande o suficiente a ponto de no ser possvel trat-la de uma
Exame Nacional do Ensino Mdio (ENEM). No segundo vez na memria principal, mtodos de pesquisa externa so
caso, a massa de dados muito maior. Tambm diferen- necessrios. Esse tipo de mtodo capaz de lidar com conjuntos
te procurar um registro em um conjunto de dados que de dados que esto armazenados na memria auxiliar (externa)
sofre poucas alteraes (inseres/remoes) como, por do computador, como o HD, fitas magnticas, entre outros.
exemplo, o conjunto de estados brasileiros; e procurar A prioridade de cada categoria de algoritmos diferente. Enquan-
por uma venda, a partir do seu cdigo, na base de dados to em uma pesquisa interna procura-se reduzir a quantidade
de uma grande empresa de e-commerce, cujos dados mu- de comparaes realizadas pelo mtodo escolhido, na pesquisa
dam constantemente. No primeiro caso, o importante externa, alm desse requisito, deve-se levar em considerao a
minimizar o tempo de pesquisa sem preocupao com o quantidade de consultas ao disco necessrias para se encontrar a
tempo necessrio para realizar inseres e remoes no informao pesquisada.
conjunto de dados, uma vez que o mesmo sofre poucas Abordaremos trs mtodos de busca interna: sequencial, binria
alteraes ao longo do tempo. e utilizando a tabela Hash. No tpico Conceitos Preliminares

6 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


apresentaremos o modelo de estrutura de dados que ser utilizado todos os casos tratam do mesmo propsito (a pesquisa) e devem
para a implementao dos mtodos de pesquisa. Em seguida, nos oferecer as mesmas funes aos seus usurios.
tpicos Pesquisa Sequencial, Pesquisa Binria e Pesquisa Um TAD comumente utilizado para pesquisa o dicionrio.
por Tabela Hash, sero analisados os trs principais mtodos Um dicionrio um TAD cujas operaes so responsveis por
de pesquisa existentes na literatura, destacando suas principais inicializar a estrutura de dados utilizada no dicionrio, pesquisar
caractersticas e estratgias de implementao e eficincia. um ou mais registros com determinada chave, inserir um novo
registro e remover um registro especfico.
Conceitos preliminares A implementao dos mtodos de pesquisa apresentados neste
Este tpico apresenta alguns conceitos que so fundamentais texto baseia-se no TAD dicionrio e na linguagem de programao
para o acompanhamento deste artigo, tal como o conceito de Java. Para simplificar a apresentao dos mtodos de pesquisa,
anlise da complexidade de algoritmos, que ser amplamente apenas as funes inserir e pesquisar sero discutidas. A lingua-
discutido, e o conceito de Dicionrio, como um tipo abstrato de gem Java foi escolhida porque tem sido utilizada em diversos
dados para implementao de mtodos de pesquisa. livros-textos sobre estruturas de dados, como em Goodrich (2013),
Sedgewick (2013), Ziviani (2007), alm de ser uma linguagem
A anlise da complexidade de algoritmos amplamente conhecida, bem documentada e que apresenta ca-
Um aspecto predominante na escolha de um mtodo de pesquisa ractersticas interessantes para o desenvolvimento de estruturas
o tempo gasto para realiz-las, bem como para manipular o de dados genricas.
conjunto de dados, inserindo ou removendo elementos. Para a K1Para a implementao do TAD dicionrio, as interfaces Item e
pesquisa, a medida de complexidade relevante consiste no nmero Dicionario foram criadas e so apresentadas na Listagem 1.
de comparaes entre chaves realizadas at que uma resposta
seja dada pelo algoritmo. Quanto insero/remoo, leva-se em
Listagem 1. Interfaces utilizada nas implementaes do TAD Dicionario.
considerao tambm o nmero de movimentaes (ou trocas)
necessrias para acomodar um novo item ou remover um item public interface Item {
existente do conjunto de dados.
public static final int MENOR = -1;
Assim, as medidas de complexidade analisadas para cada public static final int IGUAL = 0;
mtodo de pesquisa apresentado neste texto so C(n) e M(n), que public static final int MAIOR = 1;
public abstract int compara(Item it) throws Exception;
correspondem, respectivamente, s funes de complexidade que
descrevem o nmero de comparaes e o nmero de movimenta- }
es realizadas por cada mtodo, onde n a quantidade de itens
public interface Dicionario {
do conjunto de dados a ser pesquisado.
importante ressaltar ainda que a maioria dos mtodos de pes- public Item pesquisar(Item it) throws Exception;
quisa sensvel ordem inicial dos itens a serem pesquisados, isto public void inserir(Item it) throws Exception;

, o nmero de comparaes e/ou movimentaes realizadas por }


um mtodo pode variar caso o conjunto esteja ordenado ou no
ou se o elemento a ser pesquisado estiver no incio ou no final do
conjunto, entre outros. Assim, C(n) e M(n) devem ser considerados, A interface Item representa a abstrao de um item que ser
sempre quando possvel, para trs casos: armazenado no dicionrio. A nica operao que todo item deve
O melhor caso: corresponde ao menor nmero de comparaes/ prover a que compara dois itens distintos e diz se o primeiro
movimentaes sobre todas as possveis entradas de tamanho n; menor, maior ou igual ao segundo item mtodo compara().
O pior caso: corresponde ao maior nmero de comparaes/mo- Para melhorar a legibilidade do cdigo, constantes pblicas que
vimentaes sobre todas as possveis entradas de tamanho n; representam as possveis sadas do mtodo de comparao entre
O caso mdio (ou caso esperado): corresponde mdia do itens foram criadas, a saber: MENOR, IGUAL e MAIOR. Tam-
nmero de comparaes/movimentaes de todas as possveis bm importante salientar que o mtodo compara() pode lanar
entradas de tamanho n. uma exceo quando o programador tentar comparar itens de
tipos diferentes.
O tipo abstrato de dados Dicionrio Dito isso, uma possvel implementao da classe Item, con-
muito importante considerar mtodos de pesquisa como um siderando um item cuja chave um nmero inteiro, pode ser
Tipo Abstrato de Dado TAD, isto , uma estrutura de dados com observada no trecho de cdigo da Listagem 2.
um conjunto de operaes associado a ela. O motivo que um TAD A classe MeuItem implementa a interface Item e, consequen-
promove a independncia dos possveis tipos de implementao temente, o mtodo compara(), considerando as caractersticas
para esses mtodos de pesquisa. Por exemplo, um programador deste tipo de item. importante ressaltar que, na prtica, muitas
pode implementar o mtodo de pesquisa sequencial com vetores, vezes estamos mais interessados no contedo do item do que em
outro pode utilizar listas encadeadas, mas de qualquer forma, sua chave. Assim, a classe MeuItem deveria conter os atributos

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 7


Estrutura de dados: Primeiros passos com mtodos de busca

que representam a informao desejada. Por exemplo, no caso de atingido. Caso o elemento no exista no vetor, o valor null retor-
um aluno, deveriam haver atributos que representam os dados nado. J o mtodo inserir() simplesmente insere o item passado
de um aluno, como nome, endereo, entre outros; se fosse um por parmetro na primeira posio vazia do vetor.
produto, teramos atributos para a descrio, o preo, o prazo
de validade do produto, entre outros. Contudo, para simplificar
a apresentao dos mtodos de pesquisa neste artigo, apenas a
chave do item ser considerada.

Listagem 2. Exemplo de um tipo Item.

public class MeuItem implements Item {

private final int chave;


Figura 1. Ilustrao da execuo do mtodo de pesquisa sequencial
// informaes extras sobre o item

public MeuItem(int chave) { Listagem 3. Implementao do mtodo de pesquisa sequencial com um vetor
this.chave = chave; no ordenado.
}
1. public class ArrayDic implements Dicionario {
@Override 2.
public int compara(Item it) throws Exception { 3. private final int TAM_MAX = 100;
if (it instanceof MeuItem) { 4. private final Item itens[];
if (chave < ((MeuItem) it).chave) return MENOR; 5. private int pos;
else if (chave > ((MeuItem) it).chave) return MAIOR; 6.
else return IGUAL; 7. public ArrayDic() {
} else throw new Exception(No possvel comparar itens de 8. itens = new Item[TAM_MAX];
tipos diferentes!); 9. pos = 0;
} 10. }
11.
} 12. public boolean estahCheia() {
13. return (pos == TAM_MAX);
14. }
A interface Dicionario especifica os mtodos que qualquer 15.
16. @Override
implementao de um dicionrio deve conter, ou seja, as 17. public Item pesquisar(Item it) throws Exception {
operaes de insero e pesquisa de elementos no mesmo. 18. for (int i = 0; i < pos; i++) {
A partir desta interface, diversas alternativas de implementao 19. if (itens[i].compara(it) == Item.IGUAL) {
20. return itens[i];
de um dicionrio podem existir utilizando listas encadeadas, 21. }
vetores, rvores, entre outros. Os tpicos Pesquisa Sequencial, 22. }
Pesquisa Binria e Pesquisa por Tabela Hash apresentam 23. return null;
24. }
algumas das possveis formas de se implementar um dicion- 25.
rio a partir da interface criada, discutindo seus pontos fortes 26. @Override
e desvantagens. 27. public void inserir(Item it) throws Exception {
28. if (!estahCheia()) {
29. itens[pos] = it;
Pesquisa sequencial 30. pos++;
31. } else {
A pesquisa sequencial um dos mtodos de pesquisa mais
32. throw new Exception(Capacidade mxima atingida!);
simples que existe. Ele funciona da seguinte forma: a partir do 33. }
primeiro registro (ou do ltimo), pesquisa sequencialmente at 34. }
35. }
encontrar a chave procurada ou at chegar ao final (ou ao incio)
do conjunto de registros.
A Figura 1 ilustra os passos da execuo da busca sequencial
para encontrar o elemento g no vetor [a, b, c, d, e, f, g, h]. Algumas caractersticas da estratgia de pesquisa sequencial
A implementao de um dicionrio para a pesquisa sequencial, so:
utilizando um vetor (array) de itens no ordenados, apresentada 1. Por se tratar de um vetor no ordenado, a insero de qualquer
na Listagem 3. Os mtodos mais importantes desta listagem so elemento feita de forma eficiente, inserindo-o sempre no final do
pesquisar() e inserir(). O primeiro retorna o registro do item vetor, com um custo computacional constante, isto , M(n) = 1;
passado por parmetro, caso ele exista no vetor denominado 2. O mtodo pesquisar() executa em tempo linear, realizando,
itens. Observa-se que a pesquisa ocorre elemento por elemento, no pior caso, C(n) = n comparaes para encontrar um elemento.
at o que o mesmo seja encontrado ou at que o final do vetor seja Para o caso mdio, C (n) = n + 1 .
2

8 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Uma vantagem da pesquisa sequencial que ela bastante fle- de elementos no est ordenado). Note que h uma classe inter-
xvel, permitindo a utilizao de conjuntos de itens que possuem na, denominada Noh, para representar cada elemento da lista.
diferentes tipos de chaves e no apenas chaves numricas, bem O atributo lista, por sua vez, uma referncia para o n inicial da
como conjuntos que no estejam necessariamente ordenados. lista e cada n possui, alm da informao de interesse do usurio,
Uma maneira de melhorar a performance do mtodo pesquisar() uma referncia para o prximo n.
utilizar um registro sentinela. Trata-se de um registro contendo
a chave de pesquisa que colocado no incio do vetor de itens. Listagem 5. Implementao do mtodo de pesquisa sequencial com um vetor
Este evita que uma comparao a mais por item seja feita, para ordenado.

verificar se a busca chegou ao final do vetor (linha 18). O custo de @Override


se utilizar a sentinela a alocao de um espao de memria extra public Item pesquisar(Item it) throws Exception {
int i = 0;
no vetor de itens. A Listagem 4 apresenta como seria o mtodo
for (; i < pos; i++) {
pesquisa da Listagem 3 caso uma sentinela fosse utilizada. if (itens[i].compara(it) != Item.MENOR) break;
}
if (i == pos) return null;
Listagem 4. Implementao do mtodo pesquisar() com uma sentinela. if (itens[i].compara(it) == Item.IGUAL) return itens[i];
return null;
public Item pesquisar(Item it) throws Exception { }
int i = pos - 1;
while (itens[i].compara(it) != Item.IGUAL) i--; @Override
if (i != 0) return itens[i]; public void inserir(Item it) throws Exception {
else return null; if (!estahCheia()) {
} int i = pos;
while(i > 0 && itens[i - 1].compara(it) == Item.MAIOR) {
itens[i] = itens[i - 1];
i--;
Outra possvel implementao do mtodo de pesquisa sequencial }
itens[i] = it;
manter o vetor de itens ordenado. Uma busca sem sucesso (ou pos++;
seja, quando o elemento pesquisado no est presente no vetor) em } else {
um vetor no ordenado far o algoritmo executar n vezes, sempre. throw new Exception(Capacidade mxima atingida!);
}
J com um vetor ordenado, uma busca sem sucesso poder parar }
antes de visitar todos os n elementos. Por exemplo, considerando
Listagem 6. Implementao do mtodo de pesquisa sequencial com lista enca-
o vetor [2, 3, 5, 7] e o elemento 4 a ser pesquisado, a busca pode deada.
parar aps ter comparado 4 com 5, pois como 5 maior do que 4,
public class ListDic implements Dicionario {
no h possibilidade de o quatro estar no restante do vetor. Assim,
o nmero de comparaes C(n) para o pior caso cai para C (n) = n + 1 , private class Noh {
2
private Item item;
que o mesmo custo para o caso mdio. private Noh prox;
Porm, manter o vetor ordenado torna a insero dependente
public Noh(Item it, Noh prox) {
da quantidade de elementos existentes no vetor. Isto porque, this.item = it;
agora, o elemento no poder mais ser inserido no final do ve- this.prox = prox;
}
tor, mas sim na posio adequada para se manter a ordenao. }
Deste modo, vale a pena manter o vetor ordenado caso o nmero
private Noh lista;
de consultas seja bem maior do que a quantidade de inseres.
Neste caso, a quantidade de movimentaes M(n) exigidas ser: public ListDic() {
lista = null;
M(n) = 1 para o melhor caso; M(n) = n no pior caso; e M (n) = n + 1 }
2
para o caso mdio.
@Override
Para implementarmos a busca sequencial utilizando vetores public Item pesquisar(Item it) throws Exception {
ordenados os mtodos inserir() e pesquisar() devem ser alterados, Noh aux = lista;
while (aux != null) {
conforme pode ser visto na Listagem 5. if (aux.item.compara(it) == Item.IGUAL) {
Alguns autores investigam a utilizao de listas encadeadas, return aux.item;
}
em vez de vetores, para a implementao do mtodo de pesquisa aux = aux.prox;
sequencial. Segundo os mesmos, isso exigir mais memria, uma }
return null;
vez que um ponteiro para cada elemento do conjunto de itens }
dever ser armazenado; contudo, no haver a necessidade de
@Override
se saber antecipadamente a quantidade de elementos a serem public void inserir(Item it) throws Exception {
Noh noh = new Noh(it, lista);
inseridos no conjunto. lista = noh;
Uma implementao do dicionrio com uma lista encadeada }
}
simples apresentada na Listagem 6 (neste exemplo, o conjunto

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 9


Estrutura de dados: Primeiros passos com mtodos de busca

Pesquisa binria por meio desses parmetros que o sistema controla qual parte
Como foi visto anteriormente, manter o vetor ordenado para do vetor deve ser analisada a cada passo.
realizao de uma pesquisa sequencial pode ser til, pois o tem-
po de execuo mdio para buscas sem sucesso cai pela metade. Listagem 7. Implementao do mtodo de pesquisa binria em um vetor.
Porm, o custo para a insero de um elemento em um vetor
1. private Item pesquisaRecursiva(Item it, int e, int d) throws Exception {
ordenado significativamente maior do que para a insero em 2. if (e > d) return null;
um vetor no ordenado. 3. int m = (e + d)/2;
Ainda sobre buscas, h um mtodo de pesquisa que pode utilizar 4. if (itens[m].compara(it) == Item.IGUAL) return itens[m];
5. else if (itens[m].compara(it) == Item.MENOR)
melhor a vantagem de termos um vetor ordenado para melhoria 6. return pesquisaRecursiva(it, m + 1, d);
do tempo consumido para localizar um elemento. Esse mtodo 7. else return pesquisaRecursiva(it, e, m - 1);
conhecido como pesquisa binria e ser descrito nesta seo. 8. }
9.
A ideia da pesquisa binria dividir o conjunto de itens em 10. @Override
duas partes, determinar em qual dessas duas partes o elemento 11. public Item pesquisar(Item it) throws Exception {
pesquisado pode pertencer e ento concentrar a busca apenas 12. return pesquisaRecursiva(it, 0, pos - 1);
13. }
nessa parte. A estratgia utilizada para resoluo deste proble-
ma conhecida como dividir para conquistar e est presente
em vrios tipos de algoritmos, como os de ordenao QuickSort, A funo pesquisaRecursiva() possui dois casos base: quando
MergeSort, entre outros. A ideia reduzir um problema maior, que o elemento encontrado (linha 4); e quando o valor do parmetro
no se sabe como resolver a priori, em problemas menores, para e maior do que o valor do parmetro d (linha 2), o que significa
os quais se conhece a soluo. Depois, utilizar tal soluo para que todo o vetor foi pesquisado e que o elemento procurado no
resolver o problema maior. se encontra nele. Como passos recursivos, h duas possibilidades.
A primeira ocorre quando a chave do elemento procurado maior
Pesquisa binria Implementao com Vetor do que a chave do elemento do meio do vetor (linha 5). Neste caso,
Para saber se um elemento pertence a um vetor utilizando a a funo pesquisaRecursiva() invocada recursivamente, mas
pesquisa binria, compara-se a chave deste elemento com a chave o vetor a ser considerado aquele que vai do ndice do elemento
do elemento que est no meio do conjunto (lembrando que o vetor do meio + 1 at o ndice do ltimo elemento do vetor original.
deve estar ordenado). Se a chave for igual procurada, ento a E a segunda possibilidade ocorre quando a chave do elemento
busca termina com sucesso; caso a chave seja menor, ento o ele- procurado menor do que a chave do elemento do meio do vetor
mento procurado deve estar na primeira parte do vetor (do incio (linha 7). Neste caso, a funo pesquisaRecursiva() tambm
at o elemento do meio - 1); se a chave for maior, ento o elemento invocada recursivamente, mas o vetor a ser considerado aquele
deve estar na segunda parte do vetor, que vai do elemento que que vai do ndice do primeiro elemento do vetor original at o
est aps o elemento do meio at o ltimo elemento. Esse mesmo ndice do elemento do meio 1.
procedimento descrito deve ser repetido na metade escolhida at A quantidade de comparaes realizadas pelo algoritmo de
que se encontre o elemento ou at que todos os elementos do vetor pesquisa binria dada por: C(n) = 1 + C(n/2). Isto , o nmero de
tenham sido investigados sem sucesso. comparaes realizadas pela pesquisa binria para um vetor de
A Figura 2 ilustra os passos da execuo de uma pesquisa binria tamanho n igual a uma comparao mais o custo de encontrar
para encontrar o elemento g no vetor [a, b, c, d, e, f, g, h]. o elemento na metade do vetor, que dado por C(n/2), o que leva
seguinte funo de complexidade: C (n) = lg n + 1 .
Isto quer dizer que, para casos de sucesso ou insucesso, a pesqui-
sa binria nunca executar em mais do que lgn + 1 passos, o que
um resultado significativamente melhor do que os resultados
do mtodo de pesquisa sequencial. Por exemplo, para n = 106, o
mtodo de pesquisa sequencial gastaria 106 passos no caso de uma
busca sem sucesso, uma vez que sua complexidade do pior caso
Figura 2. Ilustrao da execuo do mtodo de pesquisa binria proporcional a n. J o mtodo de pesquisa binria executaria em
lg106, que aproximadamente igual a 20.
O trecho de cdigo da Listagem 7 apresenta uma verso recur- Entretanto, importante levar em considerao o custo para
siva do mtodo de pesquisa binria. Como podemos verificar, o manter o vetor ordenado, isto , para cada insero de um novo
mtodo pesquisar() delega o trabalho de encontrar o elemento elemento. No pior caso, n movimentaes sero realizadas. Sendo
para a funo pesquisaRecursiva(). Esta recebe, alm do elemento assim, a busca binria, da maneira como foi implementada nesta
a ser pesquisado, dois parmetros inteiros e e d que correspondem, seo, deve ser utilizada em conjuntos pouco dinmicos, em que
respectivamente, aos ndices do primeiro e do ltimo elemento a quantidade de consultas realizadas seja bem maior do que a
do vetor que est sendo analisado no momento. quantidade de inseres feitas no conjunto de elementos.

10 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


O uso de listas encadeadas para implementao da pesquisa O trecho de cdigo da Listagem 8 apresenta uma parte da im-
binria pode ser uma estratgia no muito promissora, uma vez plementao de um dicionrio com a utilizao de uma ABB.
que a eficincia da busca binria depende da capacidade de se
acessar rapidamente o elemento do meio de um conjunto e para Listagem 8. Trecho da implementao de um dicionrio com ABB.
atingir o elemento do meio utilizando uma lista encadeada, seria
public class ABBDic implements Dicionario {
necessrio percorrer, sequencialmente, todos os elementos ante-
riores a este, utilizando o encadeamento da lista. private class Noh {
private Item item;
Com o intuito de otimizar ainda mais o nmero de comparaes
private Noh esq, dir;
exigido pela pesquisa binria, podemos adotar uma heurstica
conhecida como busca interpolada. A busca interpolada baseia- public Noh(Item it, Noh esq, Noh dir) {
this.item = it;
se na ideia de que se o valor de uma chave muito pequeno, this.esq = esq;
melhor procurar mais no incio do vetor do que exatamente no this.dir = dir;
meio. Para isso, utiliza-se uma proporo entre a distncia }
}
da chave procurada para a chave inicial do vetor, com relao
distncia entre a chave final do vetor e a chave inicial do private Noh raiz;
mesmo. Essa heurstica pode aumentar bastante a eficincia da
public ABBDic() {
pesquisa binria, reduzindo o nmero mdio de comparaes de raiz = null;
lgn para lglgn. Por exemplo, enquanto lg106 20, lglg106 4. Porm, }
essa heurstica funciona apenas para registros cujas chaves so }

numricas e seus resultados so promissores apenas quando as


chaves esto uniformemente distribudas. A classe ABBDic possui um atributo denominado raiz que con-
siste em uma referncia para a raiz da rvore. Alm disso, cada
Pesquisa binria Implementao com rvores Binrias de Busca (ABBs) n, alm do item que representa o registro com as informaes
Grandes benefcios em termos da reduo do nmero de com- de interesse do usurio, contm referncias para as sub-rvores
paraes podem ser obtidos com o uso do mtodo de pesquisa esquerda e direita (atributos esq e dir, respectivamente).
binria em um vetor ordenado. Contudo, o custo para insero de O mtodo de pesquisa binria com ABBs segue a mesma lgica
elementos de forma ordenada neste vetor alto (proporcional a da pesquisa binria com vetores, s que dessa vez, em vez de
n). Para se obter boas taxas de execuo, tanto para busca quanto dividir o vetor em duas partes, reduzimos nosso espao de busca
para insero de elementos, estruturas mais sofisticadas, como ao procurar o elemento em uma das duas sub-rvores do n raiz.
as rvores de Binria de Busca (ABBs) ou rvores Binrias Para isso, devemos comparar o elemento pesquisado raiz da
de Pesquisa devem ser utilizadas. Para demonstrar isso, este rvore e decidir em qual sub-rvore devemos continuar a busca.
tpico apresenta a implementao de um dicionrio utilizando O trecho de cdigo da Listagem 9 apresenta a implementao do
uma ABB. mtodo pesquisaRecursiva() para ABBs.
Uma rvore binria definida como uma rvore vazia (sem
ns) ou uma rvore com n raiz conectado a um par de rvores Listagem 9. Implementao do mtodo de pesquisa binria em uma ABB.
binrias, as quais so denominadas sub-rvore esquerda e sub-
private Item pesquisaRecursiva(Item it, Noh raiz) throws Exception {
rvore direita. Uma ABB uma rvore binria em que, para cada
if (raiz == null) return null;
n, a seguinte propriedade verdadeira: todos os registros com else if (raiz.item.compara(it) == Item.IGUAL) return raiz.item;
chaves menores do que a chave deste n esto em sua sub-rvore else if (raiz.item.compara(it) == Item.MENOR)
return pesquisaRecursiva(it, raiz.dir);
esquerda e todos os registros com chaves maiores esto em sua
else return pesquisaRecursiva(it, raiz.esq);
sub-rvore direita. }
A Figura 3 apresenta alguns exemplos de rvores binrias,
destacando quais so e quais no so ABBs.
Com relao insero de elementos, para que ela seja realizada
na rvore, inicialmente deve-se encontrar o lugar certo a inserir
o n. A Listagem 10 apresenta o mtodo de insero de um ele-
mento em uma ABB.
Agora, para definir a complexidade dos mtodos inseriRecur-
sivo() e pesquisaRecursiva(), apresentados nas Listagens 9 e 10,
importante entender o conceito de altura de uma rvore.
A altura de uma rvore dada pelo comprimento do caminho mais
longo da sua raiz at um n folha. Os ns encontrados durante a
Figura 3. Exemplos de rvores binrias recurso dos mtodos inseriRecursivo() e pesquisaRecursiva()

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 11


Estrutura de dados: Primeiros passos com mtodos de busca

formam um caminho partindo da raiz que, no pior caso, chega ao e movimentaes constante. Porm, esse mtodo s aplicvel
n folha mais distante da raiz da rvore. Sendo assim, o nmero a chaves inteiras positivas, no duplicadas e menores do que M,
de comparaes realizadas por estes mtodos proporcional a h, onde M o tamanho da tabela de endereamento.
onde h representa a altura da rvore. Uma tabela hash pode ser descrita como uma estrutura de dados
eficaz para implementao de um dicionrio. Embora a busca por
Listagem 10. Implementao do mtodo de insero em uma ABB.
um elemento em uma tabela de espalhamento possa demorar tanto
quanto em uma lista encadeada o nmero de comparaes no
private Noh inserirRecursivo(Item it, Noh raiz) throws Exception { pior caso proporcional a n na prtica, hashing funciona bem.
if (raiz == null) return new Noh(it, null, null);
else if (raiz.item.compara(it) == Item.MENOR) Em geral, o nmero de comparaes mdio para pesquisar um
raiz.dir = inserirRecursivo(it, raiz.dir); elemento em uma tabela de espalhamento constante. Esse um
else raiz.esq = inserirRecursivo(it, raiz.esq); tipo de estrutura interessante para aplicaes (por exemplo, um
return raiz;
} compilador precisa realizar inmeras consultas em sua tabela de
smbolos durante a compilao de um programa) em que a busca
@Override
em um conjunto de dados realizada tantas vezes que um nmero
public void inserir(Item it) throws Exception {
this.raiz = inserirRecursivo(it, this.raiz); de comparaes proporcional a lgn ainda alto.
} Um mtodo de pesquisa que utiliza hashing constitudo de
duas etapas principais:
1. Computar o valor da funo de transformao ou funo
Para uma rvore perfeitamente balanceada, isto , uma rvore hashing: responsvel por transformar a chave de pesquisa em
cujos ns internos tm dois filhos e todas as folhas esto no mesmo um endereo da tabela hash;
nvel, possvel provar que h = lgn, portanto, pesquisa e insero 2. Tratar as colises: considerando que duas ou mais chaves
podem ser realizadas com C(n) = lgn comparaes. podem ser transformadas em um mesmo endereo, necessrio
Um ponto importante a ser destacado que, ao contrrio do realizar o tratamento de colises.
que acontece na implementao do dicionrio com vetores, tanto
o tempo de busca quanto o tempo de insero de um elemento Funo de transformao
no conjunto, utilizando ABB, proporcional a lgn. No caso do Uma funo de transformao responsvel por mapear chaves
dicionrio com vetores, o tempo de insero de um elemento em inteiros dentro do intervalo [0.. M 1], onde M o tamanho
proporcional a n, ou seja, bem maior do que lgn. da tabela na qual os dados estaro armazenados. A funo de
Contudo, importante ressaltar que as ABBs sofrem de um transformao ideal aquela que: (i) simples de ser computada;
problema que pode prejudicar seu tempo de execuo. Quando e (ii) para cada chave de entrada, qualquer valor entre [0..M-1]
os elementos de uma ABB so inseridos em ordem, crescente ou igualmente provvel de ocorrer.
decrescente, devido sua propriedade, os elementos formaro Vrias funes de transformao tm sido estudadas por pesqui-
uma grande lista encadeada, o que far com que a altura da rvore sadores ao longo dos anos. Uma destas funes, que bem simples
seja igual a n 1, onde n refere-se quantidade de elementos e que funciona muito bem, a que utiliza o resto da diviso por M:
inseridos na rvore. Alm disso, fcil prever que, aps vrias h(K) = K mod M, onde h(K) a chave transformada e K um inteiro
operaes de insero/remoo, uma ABB tende a ficar desbalan- correspondente chave de pesquisa. Nesta opo, o valor de M
ceada, j que essas operaes no garantem o balanceamento. Para deve ser escolhido com bastante cautela. Vejamos um exemplo
tratar desse problema, h estruturas mais sofisticadas, conhecidas para entender por qu: se M for par, ento h(K) ser par quando K
como rvores balanceadas. Alguns exemplos de rvores balance- for par e ser mpar quando K for mpar, uma vez que o resto da
adas so AVL, rvore Rubro-Negra, entre outras. diviso de um nmero par pelo outro par e o resto da diviso
de um nmero mpar pelo outro mpar. Isso leva a funo de
Pesquisa por Tabela Hash transformao a no espalhar as chaves de forma uniforme. Por
Os mtodos de pesquisa apresentados anteriormente so ba- exemplo, se 90% das chaves forem pares, esses 90% iro ocupar
seados na comparao da chave de pesquisa com as chaves do 50% da tabela de endereamento e os outros 10% ocuparo os
conjunto de dados. O mtodo de pesquisa por tabela hash (ou 50% restantes.
hashing) completamente diferente: os registros armazenados em O ideal que M seja um nmero primo, mas no qualquer primo.
uma tabela so diretamente endereados a partir de uma trans- Segundo Cormen et al. (2012), o ideal escolher um primo no
formao aritmtica realizada sobre a chave de pesquisa. muito prximo de uma potncia de 2. Por exemplo, se desejamos
O hashing uma extenso do mtodo de pesquisa indexado por criar uma tabela de espalhamento para conter aproximadamente
chave (key-indexed search method) que usa valores das chaves como 2000 chaves e no nos importamos de examinar, por exemplo, uma
ndices de um vetor, em vez de compar-las. A grande vantagem mdia de trs elementos em uma busca malsucedida, podemos
deste mtodo que inseres, remoes e consultas a elementos do escolher uma tabela de espalhamento de tamanho M = 701, uma
conjunto podem ser realizadas com um nmero de comparaes vez que 701 um primo prximo de 2000/3 e no muito prximo

12 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


de uma potncia de 2 (a menor potncia antes de 701 512 e a linear. Este mtodo conhecido tambm como endereamento
maior potncia depois de 701 1024). externo (ou encadeamento separado).
Como as transformaes sobre as chaves so aritmticas (confor- Exemplo: se a i-sima letra do alfabeto representada pelo
me o mtodo apresentado anteriormente), devemos transformar nmero i e a funo de transformao h(Chave) = Chave mod M
as chaves no numricas em nmeros. Considerando uma String utilizada para M = 7, o resultado da insero das chaves P E S Q
(conjunto de caracteres) como chave, uma alternativa seria somar U I S A na tabela hash ocorre como ilustrado na Figura 4.
o decimal de cada caractere desta String, de acordo com sua Assumindo que qualquer item do conjunto tem igual probabi-
representao na tabela ASCII. Contudo, essa no uma estrat- lidade de ser endereado para qualquer entrada, ento o compri-
gia muito boa para strings, para as quais a ordem dos elementos mento esperado de cada lista encadeada de N/M, onde N repre-
relevante. Por exemplo, utilizando essa estratgia, as strings senta o nmero de registros na tabela e M representa o tamanho
temp01 e temp10 tero a mesma representao numrica, da da tabela. Esse valor chamado de fator de carga da tabela de
mesma forma que as palavras stop, pots, spot e tops. espalhamento, que pode ser menor, igual ou maior do que 1.
A transformao de uma chave no numrica para uma chave
numrica deve levar em considerao a posio dos elementos
nesta chave. Uma alternativa escolher uma constante a > 1 e
transformar a chave no numrica da seguinte forma:

(x1 * ak-1) + (x2 * ak-2) + ... + (xn-1 * a) + xn

Por exemplo, dados a = 5, a String aab e considerando que


o valor de cada letra corresponde sua posio no alfabeto da
lngua portuguesa, temos:

(1 * 52) + (1 * 51) + 2 = 32

Caso a String fosse baa, o resultado seria diferente:

(2 * 52) + (1 * 51) + 1 = 56

O mtodo hashCode(), da linguagem Java, tem exatamente a


funo de transformar uma chave no numrica em um inteiro. Figura 4. Tabela hash com endereamento externo
Tal funo utiliza a estratgia comentada anteriormente, com
a = 31. Estudos experimentais sugerem que 31, 33, 37, 39 e 41 so Logo, as operaes de pesquisa, insero e retirada tero um
boas opes para o valor de a, quando as Strings se referem a custo proporcional a 1 + N/M, em mdia. Deste clculo, 1 repre-
palavras da lngua inglesa. senta o tempo para encontrar a entrada na tabela e N/M o tempo
para percorrer a lista encadeada. Isso significa que se o nmero de
Tratamento de colises posies da tabela de espalhamento , no mnimo, proporcional
Mesmo que se obtenha uma funo de transformao que dis- ao nmero de elementos na tabela, temos que as operaes de pes-
tribua os registros de forma uniforme entre as entradas da tabela quisa, insero e retirada tero um custo esperado constante.
de espalhamento, existe alta probabilidade de haver colises. O O pior caso para esta estratgia ocorre quando a funo de trans-
paradoxo do aniversrio diz que, em um grupo de 23 pessoas formao leva todas as chaves a uma mesma posio da tabela de
juntas ao acaso, existe uma chance maior do que 50% de que duas espalhamento, criando uma nica lista de comprimento igual a
pessoas comemorem aniversrio no mesmo dia. N. Neste caso, o pior para a busca ser proporcional a N.
Fazendo uma analogia com a questo da tabela de espalhamento O trecho de cdigo da Listagem 11 apresenta a implementao
e das colises, isso quer dizer que se for utilizada uma funo de um dicionrio por meio de uma tabela de espalhamento. Neste
uniforme que enderece 23 chaves randmicas em uma tabela de caso, para tratamento de colises, o endereamento externo com
tamanho M = 365, a probabilidade de que haja colises maior listas encadeadas utilizado.
do que 50%. Nesta implementao, a funo hash() chama a funo hashCo-
de() do Java para gerar a chave numrica utilizada na transfor-
Tratamento de colises com listas encadeadas mao. O valor de M instanciado com maxN / 5, onde maxN
Uma das formas de resolver colises simplesmente construir a quantidade de chaves que sero adicionadas pelo usurio. Isto
uma lista linear encadeada para cada endereo da tabela. Assim, feito porque queremos que cada lista contenha, em mdia, 5
todas as chaves com mesmo endereo so encadeadas em uma lista elementos, visando reduzir o tempo de uma busca sem sucesso.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 13


Estrutura de dados: Primeiros passos com mtodos de busca

Listagem 11. Implementao do mtodo de pesquisa por tabela hash (enderea-


vrios mtodos para armazenar N registros em uma tabela de
mento externo). tamanho M > N, isto , com o nmero de registros menor do que
a tabela. Estes mtodos utilizam os lugares vazios na prpria ta-
public class HashingDic implements Dicionario {
bela para resolver as colises e so chamados de endereamento
private class Noh { aberto. Em outras palavras, todas as chaves so armazenadas na
private Item item;
prpria tabela, sem a necessidade de listas encadeadas.
private Noh prox;
No entanto, diferentemente do endereamento externo, no en-
public Noh(Item it, Noh prox) { dereamento aberto a tabela de espalhamento pode ficar cheia,
this.item = it;
de tal forma que nenhuma insero adicional possa ser realizada.
this.prox = prox;
} Ou seja, o fator de carga da tabela nunca ser maior do que 1.
} Quando uma chave x endereada para uma entrada da tabela
private Noh table[];
que j esteja ocupada, uma sequncia de localizaes alternativas
private int M; h1(x), h2(x), ... escolhida. Se nenhuma posio estiver vazia, ento
a tabela est cheia e no podemos inserir a chave x.
public HashingDic(int maxN) {
M = maxN / 5; As propostas para a escolha de localizaes alternativas so
table = new Noh[M]; diversas. A mais simples chamada de hashing linear, onde a
} posio hj na tabela dada por: hj = (h(x) + j) mod M, para 1 j M
private Item pesquisarLista(Item it, Noh lista) throws Exception { 1. Em outras palavras, para inserir um elemento na tabela hash
Noh aux = lista; utilizando endereamento aberto, examinamos sucessivamente
while (aux != null) {
a tabela de espalhamento at encontrar uma posio vazia na
if (aux.item.compara(it) == Item.IGUAL) {
return aux.item; qual podemos inserir a chave. Por exemplo: se a i-sima letra do
} alfabeto representada pelo nmero i e a funo de transformao
aux = aux.prox;
h(Chave) = Chave mod M utilizada para M = 7, ento o resultado
}
return null; da insero das chaves L U N E S na tabela, usando hashing linear
} para resolver colises, o exposto na Figura 5.
private int hash(Item it, int M) {
return it.toString().hashCode() % M;
}

@Override
public Item pesquisar(Item it) throws Exception {
return pesquisarLista(it, table[hash(it, M)]);
}

@Override
public void inserir(Item it) throws Exception {
int i = hash(it, M);
table[i] = new Noh(it, table[i]);
}

A funo pesquisar() delega a busca pelo elemento funo


pesquisaLista(). Para isso, a funo pesquisar() recupera a lista
onde o elemento deve estar alocado a partir do ndice retornado
pela funo hash(). A funo inserir, da mesma forma, utiliza o Figura 5. Tabela hash com endereamento aberto
ndice retornado pela funo hash() para inserir o elemento no
incio da lista correspondente a este ndice. O hashing linear fcil de implementar, mas sofre de um proble-
ma conhecido como agrupamento. Esse fenmeno ocorre quando
Tratamento de colises com endereamento aberto a tabela comea a ficar cheia, pois a insero de uma nova chave
A estratgia do endereamento externo interessante, contudo tende a ocupar uma posio contgua a outras j ocupadas, o que
requer o uso de uma estrutura de dados auxiliar lista encadeada deteriora o tempo necessrio para novas pesquisas.
para armazenar os elementos que sofreram colises. No exemplo da figura anterior, para encontrar a chave S, tere-
Quando o nmero de registros a serem armazenados na tabela mos que realizar cinco comparaes, uma vez que S est h cinco
puder ser previamente estimado, no haver necessidade de usar posies de distncia de sua posio original (aquela retornada
listas encadeadas para armazenar os registros. Neste sentido, h pela funo de transformao). O custo mdio necessrio para

14 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


uma busca com sucesso em uma tabela de espalhamento com Pior caso Caso mdio
hashing linear : 12 1 + 1 1a , onde = N/M. Tipo de Implementao Pesquisa Pesquisa
Inserir Pesquisar Inserir
Segundo dados experimentais, para uma tabela hash com sucesso insucesso
50% de fator de carga (), em mdia, uma consulta com sucesso Vetor ordenado N N N/2 N/2 N/2
Lista encadeada ordenada N N N/2 N/2 N/2
realiza menos do que duas comparaes, o que um resultado
Vetor no ordenado 1 N 1 N/2 N
interessante.
Lista encadeada no
A Listagem 12 apresenta a verso do dicionrio que utiliza 1 N 1 N/2 N
ordenada
tabelas de espalhamento com endereamento aberto. Note que Busca binria com vetores N lgN N/2 lgN lgN
a funo de hash foi omitida, pois a mesma do cdigo da Busca binria com ABB N N lgN lgN lgN
Listagem 11. Hashing 1 N 1 1 1

Figura 6. Comparao entre as diferentes implementaes de um dicionrio


Listagem 12. Implementao do mtodo de pesquisa por tabela hash (enderea-
mento aberto).
A pesquisa binria o primeiro mtodo de busca que traz o
public class HashingLinearDic implements Dicionario {
nmero de comparaes no caso mdio abaixo de N no caso, lgN.
private final Item table[]; Com pesquisas sequenciais, o mximo que se consegue alcanar
private final int M; algo proporcional a N. A vantagem de se utilizar ABB para a
implementao da pesquisa binria est no fato de que a insero
public HashingLinearDic(int maxN) {
M = maxN * 5;
tambm pode ser realizada com lgN comparaes no pior caso,
table = new Item[M]; mas, para isso, a ABB deve estar devidamente balanceada.
} Por fim, pode-se notar que tabelas de espalhamento conseguem
atingir eficincia muito boa para buscas com e sem sucesso, no
@Override
public Item pesquisar(Item it) throws Exception {
caso mdio, chegando a apresentar um nmero de comparaes
int i = hash(it, M); constante. Contudo, essa no uma boa estrutura a ser utilizada
while (table[i] != null) { quando o objetivo recuperar os registros da tabela em ordem
if (table[i].compara(it) == Item.IGUAL) {
crescente ou decrescente.
return table[i];
} else {
i = (i + 1) % M;
} Autor
}
return null; Paulo Afonso Parreira Jnior
} paulojunior@jatai.ufg.br http://paulojunior.jatai.ufg.br
Atualmente professor do curso de Bacharelado em Cincia da
@Override Computao da Universidade Federal de Gois (Campus Jata).
public void inserir(Item it) throws Exception { aluno de doutorado do Programa de Ps-Graduao em Cincia da
int i = hash(it, M);
Computao (PPG-CC) da Universidade Federal de So Carlos (UFSCar),
while (table[i] != null) {
i = (i + 1) % M;
na rea de Engenharia de Software. mestre em Engenharia de Software pelo Depar-
} tamento de Computao da UFSCar (2011). integrante do Advanced Research Group
table[i] = it; on Software Engineering (AdvanSE) do Departamento de Computao da Universidade
} Federal de So Carlos e do Grupo de Pesquisa e Desenvolvimento de Jogos Educacionais
} Digitais (GrupJED) do Curso de Cincia da Computao da Universidade Federal de Gois
(Regional Jata). Tem experincia na rea de Cincia da Computao, com nfase em
Engenharia de Software, atuando principalmente nos seguintes temas: Manuteno
Como vantagens da utilizao de tabelas de espalhamento, de Software, Desenvolvimento de Software Orientado a Objetos, Desenvolvimento de
cita-se: (i) alta eficincia de custo de pesquisa no caso mdio; e Software Orientado a Aspectos e Informtica na Educao.
(ii) simplicidade de implementao. Como desvantagens, tem-se:
(i) o custo para recuperar os registros inseridos na tabela em ordem Links:
crescente alto, sendo necessrio orden-los; e (ii) seu pior caso
proporcional a N. Cormen, T. H. et al. (2012). Algoritmos: teoria e prtica. Traduo da 3 edio
A Figura 6 apresenta a comparao entre as diversas implemen- americana.
taes de um dicionrio (vetor ordenado, vetor no ordenado,
Goodrich, M. T. e Tamassia, R. (2013) Estruturas de Dados & Algoritmos em
lista encadeada ordenada, lista encadeada no ordenada, busca
Java. 5 edio.
binria com vetores, busca binria com ABB e hashing) quanto
quantidade de comparaes realizadas pelos mtodos inserir e Sedgewick, R. (2013). Algorithms in Java. Parts 1-4. 3 edio.
pesquisar, em seus piores casos e casos mdios. Ziviani, N. (2007) Projeto de Algoritmos com implementaes em Java e C++.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 15


Vantagens e desvantagens dos padres de projeto Singleton e Flyweight

Vantagens e desvantagens
dos padres de projeto
Singleton e Flyweight
Conhea neste artigo as vantagens, desvantagens,
quando e como utilizar os padres de projeto
Singleton e Flyweight

O Fique por dentro


que seria de ns se no existissem os padres?
A reflexo acerca deste questionamento pode
parecer sem nexo, contudo, refletir sobre os be- Este artigo til para leitores que desejam aperfeioar seus co-
nefcios da padronizao vem ao encontro com o assunto nhecimentos sobre Design Patterns. Nele, mostraremos por meio de
abordado neste artigo, quase que em sua totalidade. exemplos reais como a utilizao dos padres Singleton e Flyweight
A fim de estimular o estudo sobre padres, podemos melhora a qualidade e favorece a manuteno e evoluo do cdigo.
utilizar um exemplo: imagine se as posies das letras Alm disso, analisaremos como estes padres so empregados em
dos teclados de computadores variassem de acordo com recursos da plataforma Java EE, o que refora ainda mais nossos
cada fabricante? Para o usurio seria muito ruim, pois conhecimentos para a importante deciso de quando adot-los em
ele teria que se adaptar aos vrios tipos de teclados, nossos projetos.
tornando tarefas simples extremamente ineficientes,
como digitar este artigo, por exemplo.
Assim como os padres so fundamentais em diver- facilitar o desenvolvimento de forma padronizada, conhecidas
sas reas, no poderia ser diferente na Engenharia de como Design Patterns.
Software. Nesta rea, podemos, por exemplo, docu- Baseando-se em problemas comuns, quatro autores criaram
mentar um software de vrias maneiras, seja atravs vinte e trs solues padronizadas, catalogando-as no livro De-
de um documento de texto, um desenho em uma folha sign Patterns: Elements of reusable object-oriented software. Este livro
de papel ou at mesmo em uma planilha. No entanto, tornou-se referncia mundial quando o assunto a resoluo
quando utilizamos um padro, como a Unified Modeling de problemas especficos em projetos de software orientados a
Language (UML), propiciamos uma compreenso mais objetos, independente da linguagem utilizada. Erich Gamma,
clara e objetiva da documentao gerada. Richard Helm, Ralph Johnson e John Vlissides, mais conhecidos
Assim como a UML para a documentao, no desen- como a Gangue dos Quatro (Gang of Four - GoF), dividiram seus
volvimento de software a adoo de padres favorece a respectivos padres em trs categorias: Padres de Criao, Es-
criao de um cdigo-fonte melhor no que diz respeito truturais e Comportamentais.
compreenso, alm de colaborar para a manuteno e Com base nas experincias destes autores, ser apresentada
a evoluo do projeto. Com o propsito de alcanarmos neste artigo uma viso pragmtica acerca da utilizao dos Design
esses benefcios e apoiar o desenvolvedor na resoluo Patterns Singleton e Flyweight, com exemplos reais aplicados nas
de problemas comuns, como o acesso a diversas fontes plataformas Java Standard Edition e Enterprise Edition.
de dados, a localizao de um servio, a mediao
entre o formulrio e a classe controladora, a auditoria Introduo ao Singleton
das aes do usurio, o mecanismo de autenticao e Voc j implementou alguma classe responsvel por armazenar
autorizao, entre outros, existem tcnicas que visam e gerenciar um cache de quaisquer dados e se deparou com a

16 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


necessidade de compartilhar uma nica instncia desta por toda Listagem 1. Estrutura bsica mais conhecida de um Singleton.
a aplicao?
01. public class SuaClasseSingleton {
Sabemos que, ao utilizar a palavra reservada new, um novo 02. private static final SuaClasseSingleton INSTANCE = new SuaClasseSingleton();
objeto criado e mantido em memria at que no existam mais 03.
referncias ao mesmo, tornando-o passvel de descarte pelo 04. private SuaClasseSingleton() {}
05.
Garbage Collector. 06. public static SuaClasseSingleton getInstance() {
Na maioria dos casos comum a criao de diversos objetos 07. return INSTANCE;
de uma mesma classe, mas existem cenrios em que podemos 08. }
09.
gerar, manter e compartilhar um nico objeto. neste momento 10. public void qualquerMetodoDeNegocio() {}
que podemos nos beneficiar de um dos padres mais conheci- 11.}
dos e de fcil compreenso pelos desenvolvedores, o Singleton. Listagem 2. Estrutura bsica mais conhecida de um Singleton.
Criado pela Gangue dos Quatro, este design pattern faz parte da
categoria dos padres de criao e seu nome oriundo do jogo 01. public class SuaClasseSingleton {
02. private static final SuaClasseSingleton INSTANCE = new SuaClasseSingleton();
de baralho Singleton, mais especificamente quando resta apenas 03.
uma carta na mo. 04. private SuaClasseSingleton() {
O Singleton tem como objetivos: 05. if(INSTANCE != null){
06. }
1. Garantir a existncia de uma nica instncia da classe relacio- 07. }
nada no sistema; 08.
2. Disponibilizar um ponto de acesso central a essa instncia. 09. public static SuaClasseSingleton getInstance() {
10. return INSTANCE;
11. }
Ao garantir uma nica instncia de uma classe, podemos evitar 12.
13. public void qualquerMetodoDeNegocio() {}
situaes indesejveis, como o alto consumo de memria quando
14. }
precisamos de um mesmo objeto repetidas vezes. Por exemplo,
ao criarmos um objeto responsvel por recuperar valores de um
arquivo de propriedades, podemos mant-lo e reutiliz-lo sempre importante lembrar que o recurso de serializao tem aces-
que uma propriedade for solicitada. so, inclusive, aos construtores privados, para a instanciao de
novos objetos, e justamente neste ponto que precisamos ficar
Implementando o Singleton atentos.
A implementao do Singleton considerada simples, poden- Felizmente, o mecanismo de desserializao invoca um mtodo
do ser realizada em apenas uma classe e com poucas linhas de (no esttico) da classe do objeto a ser desserializado, chamado
cdigo. Podemos visualizar, na Listagem 1, a estrutura mais readResolve(), aps a desserializao. Ao implementarmos esse
famosa de um Singleton. O primeiro detalhe a ser observado mtodo, retornamos a nica instncia do Singleton que deve existir
que a classe detentora de sua nica instncia, criada e man- na aplicao e que est referenciada no atributo privado e esttico
tida no atributo esttico INSTANCE na primeira chamada INSTANCE. Veja um exemplo disso na Listagem 3.
ao mtodo getInstance(). A partir deste momento, qualquer Ao implementarmos um Singleton em seu formato tradicional,
chamada a este mtodo implica que a mesma instncia ser nos deparamos com alguns riscos que pem em jogo seus prprios
retornada. Vale ressaltar ainda que, para evitar a criao de objetivos. No entanto, esses riscos foram minimizados a partir
uma instncia externa, o construtor deve ser definido com o do Java 5, com a incluso dos Enums. Com este recurso tornou-se
modificador de acesso private. mais simples e seguro implementar este padro (veja como faz-
No entanto, sua classe ainda corre o risco de ter vrias instn- lo na Listagem 4).
cias devido aos recursos do Java de reflexo e desserializao, Ao criar um Singleton a partir de um enum no haver mais a
analisados a seguir: possibilidade de se criar outras instncias via reflexo ou seria-
Via reflexo podemos invocar qualquer construtor privado. lizao de objetos, visto que enums no so instanciveis, ou seja,
Assim, se for necessrio se proteger deste recurso da linguagem, no possuem mtodos construtores.
lance uma exceo no mtodo construtor caso a instncia j esteja
criada, conforme a Listagem 2; Onde o Singleton utilizado no universo Java EE?
Ao realizar a desserializao do objeto que deveria ser nico, Agora que temos uma boa base do padro Singleton e suas
uma nova instncia ser criada. A fim de evitarmos a existncia peculiaridades, fica fcil entender sua implementao e como
de duas instncias, necessrio definir os atributos do Singleton funciona a especificao JSR-318: Enterprise JavaBeans 3.1, que
como transient (isto far com que esses atributos no sejam seria- rege a implementao e o gerenciamento do ciclo de vida dos EJBs
lizados) e implementar o mtodo readResolve() (invocado pelo na plataforma Java Enterprise Edition 6.
processo de desserializao), retornando a instncia j existente Uma das novidades da especificao mencionada o componen-
em memria no atributo INSTANCE. te Singleton SessionBean, reconhecido atravs do uso da anotao

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 17


Vantagens e desvantagens dos padres de projeto Singleton e Flyweight

@javax.ejb.Singleton, demonstrada na Listagem 5. Sua principal Vejamos um exemplo desta situao. Suponha que sua aplicao
diferena em relao ao Stateful SessionBean que existir uma no executada em um ambiente distribudo e seu Singleton
nica instncia do objeto por JVM. SessionBean tenha sido modelado com o intuito de compartilhar
o estado. No entanto, algum tempo depois, voc decide tornar a
Listagem 3. Singleton protegido do retorno de uma nova instncia na desserializao. sua aplicao distribuda, acarretando em novas instncias do
Singleton de acordo com os ns do cluster (j que existir uma
import java.io.ObjectStreamException;
import java.io.Serializable;
instncia por JVM). Diante disso, como podemos compartilhar
esse estado desses objetos entre as diversas instncias geradas?
public final class SuaClasseSingleton implements Serializable { Para solucionar esse problema, uma tcnica que pode ser adotada
private static final long serialVersionUID = 1L;
compartilhar o estado do Singleton atravs de um sistema de
private static final SuaClasseSingleton INSTANCE = new SuaClasseSingleton(); cache de segundo nvel com suporte a replicao de dados
entre os ns do ambiente clusterizado como, por exemplo, o
private SuaClasseSingleton() {}
Infinispan.
public static SuaClasseSingleton getInstance() { Alm da facilidade de criao de um Singleton SessionBean, a
return INSTANCE;
especificao Enterprise JavaBeans 3.1 oferece outros benefcios,
}
a saber:
public void qualquerMetodoDeNegocio() {} Uso da anotao @Startup para inicializar o Singleton junto ao
carregamento da aplicao;
/*
* Mtodo que ser invocado pelo Java no processo de desserializao. Uso da anotao @DependsOn para definir a ordem de inicia-
* Garantimos a unicidade retornando o objeto referenciado pelo lizao entre diversos Singletons;
* atributo esttico INSTANCE.
*/
Uso da anotao @ConcurrencyManagement para especificar
private Object readResolve() throws ObjectStreamException { quem controlar o acesso concorrente ao objeto, se o prprio bean
return INSTANCE; ou o container.
}
}
Essas anotaes so parte da Java Enterprise Edition 6. Nessa
Listagem 4. Estrutura simples e pouco conhecida de um Singleton. plataforma, alm do EJB 3.1, temos tambm o CDI (Context and
public enum SuaClasseSingletonEnum { Dependency Injection, especificado pela JSR-299), que define um
comportamento diferente para a anotao @Singleton. Ao uti-
INSTANCE;
lizarmos EJBs, somos beneficiados pelo controle transacional,

public void qualquerMetodoDeNegocio() {} concorrncia no acesso ao objeto e distribuio do Singleton Ses-
} sion Bean. Porm, quando precisamos criar um objeto nico mais
Listagem 5. Exemplo de Singleton SessionBean.
simplificado, utilizamos o CDI, pois dessa forma o servidor de
aplicao fica responsvel apenas por gerenciar o ciclo de vida do
import javax.ejb.Singleton; Singleton, no havendo preocupao com transaes, concorrncia
@Singleton de acesso ou distribuio do objeto criado.
public class SeuEJBSingleton {

}
public void qualquerMetodoDeNegocio() {} Introduo ao Flyweight
Assim como o Singleton, o padro Flyweight que em Portugus
significa peso-mosca, a categoria mais leve do boxe tambm
Ao traar um paralelo com a implementao do Singleton, faz parte do catlogo de Design Patterns do GoF, pertencendo
por sua vez, comum pensarmos que existir apenas uma categoria de padres Estruturais. O Flyweight utilizado para
instncia do Singleton SessionBean por aplicao, mesmo em compartilhar objetos otimizando o uso da memria e o desempe-
um ambiente distribudo em vrios ns. Porm, a especificao nho em relao ao tempo de execuo do seu cdigo. Seu objetivo
estabelece que caso sua aplicao esteja em execuo em um estabelecer um padro para cachear objetos muito utilizados
ambiente clusterizado, por exemplo, com dez ns, existiro na aplicao e facilitar a manipulao de grandes quantidades
dez instncias diferentes do Singleton SessionBean, uma em de dados.
cada JVM. Dessa forma, se seu Singleton foi projetado como Conceitualmente, o uso eficiente do Flyweight ocorre quando
um servio que mantm o estado de objetos em memria, se- possumos muitas instncias de uma classe e quando parte
ro dez instncias do mesmo, uma para cada n (JVM). Nesse destas instncias podem ser substitudas por poucos objetos
momento ento, temos um problema, pois cada n mantm as compartilhados.
informaes de forma independente, gerando divergncias Para entendermos melhor o funcionamento do Flyweight, vamos
que podem comprometer o funcionamento e a consistncia trazer esse conceito para um problema real: considere que precisa-
da aplicao. mos modelar a construo de uma loja que vende exclusivamente

18 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


computadores. Como caractersticas mais
relevantes da nossa loja, destacam-se os
seguintes aspectos:
Vendemos um nico modelo de compu-
tador de uso pessoal (desktops), com uma
especificao e preo padro;
A especificao e preo obedecem a um
padro nico;
Vendemos um tipo de computador que
tem alto desempenho, utilizado como
servidor de aplicao; Figura 1. Carrinho de compras sem e com o Flyweight
Vendemos um computador exclusivo
para ser utilizado como servidor de banco
de dados;
A venda mensal de computadores de uso
pessoal cerca de 100 vezes maior que o
servidor de banco de dados.

De acordo com estes requisitos, podemos


concluir que apesar de todas as entidades
envolvidas serem computadores, elas
podem assumir estados e configuraes
diferentes. Alm disso, um pequeno gru-
po (desktops) pode ser representado por
um nico objeto imutvel compartilhado.
Esse, portanto, um timo cenrio para
aplicarmos o Flyweight.
Com o objetivo de ilustrar o benefcio do
compartilhamento de objetos imutveis,
observe a Figura 1, que possui a represen-
tao de dois carrinhos de compras em for-
Figura 2. Diagrama de classes do exemplo
ma de listas, contendo os objetos criados
para cada tipo de computador com e sem
a utilizao do pattern Flyweight. tao das principais classes da soluo que define nosso objeto do mundo real:
Considerando que nosso cliente possui do nosso carrinho de compras. Para isso, o computador desktop. esta classe que
em seu carrinho quatro computadores utilizaremos os requisitos da nossa loja ter apenas uma instncia criada e com-
desktop, um servidor de aplicao e um de computadores. partilhada no sistema;
servidor de banco de dados, na verso Como primeiro passo, para facilitar Na Listagem 8 temos as classes Applica-
sem o Flyweight podemos observar um o entendimento, observe a Figura 2, tionServer e DataBaseServer. Elas repre-
desperdcio de recursos, pois todas as ve- que mostra o diagrama de classes da sentam os tipos de computadores menos
zes em que um desktop for adicionado ao soluo com as abstraes e as relaes vendidos, ou seja, com poucas instncias
carrinho de compras ser criada uma nova entre elas. criadas em nosso carrinho de compras, no
instncia. Por sua vez, analisando a opo Dado esse diagrama, podemos traar atingindo os requisitos necessrios para
com o Flyweight, baseado na premissa um paralelo de como nossas classes serem um Concret Flyweight;
da imutabilidade, teremos apenas uma esto relacionadas ao Flyweight: Na Listagem 9 temos a classe Compu-
instncia criada e compartilhada todas A interface Computer, apresentada terFactory, nossa fbrica de instncias dos
as vezes que um computador desktop for na Listagem 6, representa o contrato computadores e tambm responsvel por
adicionado lista de compras. que todos os computares implementam, garantir a criao e compartilhamento da
ou seja, para ser um computador no nica instncia de DesktopImpl;
Implementando o Flyweight sistema uma classe deve implementar Por fim, observamos na Listagem 10 a
Agora que entendemos mel hor os essa interface; classe Client, que solicita a Computer-
conceitos do Flyweight, nosso desafio A classe concreta DesktopImpl, apre- Factory as instncias dos computadores
aplicar esse Design Pattern na implemen- sentada na Listagem 7, a abstrao adicionados ao carrinho de compras.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 19


Vantagens e desvantagens dos padres de projeto Singleton e Flyweight

Listagem 6. Cdigo da interface Computer. Listagem 8. Cdigo das classes ApplicationServer e DataBaseServer.

public interface Computer { public class ApplicationServer implements Computer {


float getPrice();
String getSpecification(); public ApplicationServer () {
} System.out.println(Create new ApplicationServer instance.\n);
}
Listagem 7. Cdigo da classe DesktopImpl.
@Override
public class DesktopImpl implements Computer { public String getPrice() {
return 5200.00;
public DesktopImpl() { }
System.out.println(Create new Desktop instance.\n);
} @Override
public String getSpecification() {
@Override return Im ApplicationServer.;
public String getPrice() { }
return 1200.00; }
} public class DataBaseServer implements Computer {

@Override public DataBaseServer () {


public String getSpecification() { System.out.println(Create new DataBaseServer instance.\n);
return Im Desktop.; }
}
} @Override
public String getPrice() {
return 8000.00;
}
Observando a Listagem 6, note que criamos a interface
@Override
Computer partindo do princpio que em nossa loja vendemos public String getSpecification() {
computadores e que toda abstrao que representa um tipo de return Im DataBaseServer.;
computador deve implementar esse contrato. }
}
Na Listagem 7, a classe concreta DesktopImpl implementa a
interface Computer e define a abstrao que representa nosso Listagem 9. Cdigo da classe ComputerFactory.
computador desktop. Dessa classe, de acordo com a estrutura
public class ComputerFactory {
do Flyweight, teremos apenas uma instncia imutvel armaze-
nada em uma varivel esttica que ser gerenciada pela nossa private static Computer desktop;

fbrica de instncias, ComputerFactory (vide Listagem 10). public enum COMPUTER_TYPE {


Essa instncia, posteriormente, ser compartilhada por todos DESKTOP, APP_SERVER, DB_SERVER;
}
os carrinhos de compras dos clientes.
Na Listagem 8 temos duas classes que implementam a public static Computer getComputer(COMPUTER_TYPE type) {
interface Computer, mais especificamente as classes concretas if (type == null) {
throw new IllegalArgumentException();
ApplicationServer e DataBaseServer. Analisando os requisitos }
informados, no existe a necessidade do compartilhamento de
if (COMPUTER_TYPE.DESKTOP.equals(type)) {
instncias, pois a quantidade de computadores desses tipos return getDesktop();
que so vendidos mnima e consequentemente o nmero }
de instncias criadas no relevante a ponto de interferir na
if (COMPUTER_TYPE.APP_SERVER.equals(type)) {
memria. return new ApplicationServerImpl();
J na Listagem 9 apresentamos o cdigo de ComputerFactory, }
responsvel por retornar objetos de abstraes que implemen- if (COMPUTER_TYPE.DB_SERVER.equals(type)) {
tam a interface Computer. Essa fbrica cria as instncias con- return new DataBaseServerImpl();
forme o parmetro COMPUTER_TYPE, podendo ser dos tipos }
Return null;
DESKTOP, APP_SERVER ou BD_SERVER. Nesta classe tam- }
bm implementamos a criao do cache do objeto Flyweight.
private static Computer getDesktop() {
Esse cache nada mais do que uma instncia atribuda a uma if (desktop == null) {
varivel esttica que ser retornada toda vez que um cliente desktop = new DesktopImpl();
solicitar um computador do tipo DESKTOP (DesktopImpl). }

A implementao do Flyweight requer uma fbrica de objetos return desktop;


que tem como uma das suas principais metas evitar que o Client }
}
fique responsvel pela criao dessas instncias.

20 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Apresentada na Listagem 10, a classe Client representa nosso Ao observar algumas classes do JDK podemos perceber o uso
carrinho de compras. Nela, note que por meio da execuo do desse padro como, por exemplo, na implementao da classe
mtodo addShoppingCart() podemos adicionar computadores Integer. Para reforar o conceito desse pattern, na Listagem 11
ao carrinho de compras. Sempre que esse mtodo for executado lanamos um pequeno desafio, onde o leitor deve analisar um
ser solicitada uma instncia de computador fbrica, que re- trecho de cdigo e escolher a opo correta sobre a utilizao do
tornar um objeto de acordo com COMPUTER_TYPE passado padro de projeto Flyweight pelo wrapper Integer do JDK.
como parmetro.
Listagem 11. Desafio sobre a utilizao do Flyweight pela classe Integer.
Listagem 10. Cdigo da classe Client (Client).
public class Desafio {
public class Client {
public static void main(String[] args) {
private Map<Quantity, Computer> shoppingCart = new HashMap<Quantity, Integer a = Integer.valueOf(127);
Computer>(); Integer b = Integer.valueOf(127);

public static void main(String[] args) { System.out.println(a == b);
Client manager = new Client();
manager.addShoppingCart(4, COMPUTER_TYPE.DESKTOP); a = Integer.valueOf(128);
manager.addShoppingCart(1, COMPUTER_TYPE.APP_SERVER); b = Integer.valueOf(128);
manager.addShoppingCart(1, COMPUTER_TYPE.APP_SERVER);
manager.showShoppingCart(); System.out.println(a == b);
} }
}
private void addShoppingCart(int quantity, COMPUTER_TYPE type) {
Quantity quantity = new Quantity(quantity, type);
shoppingCart.put(quantity, ComputerFactory.getComputer(type));
} Ao executar esse cdigo, quais valores booleanos sero impressos:
a) true, true
private void showShoppingCart() {
for (Entry<Quantity, Computer> entry : shoppingCart.entrySet()) { b) false, false
System.out.println(Quantity: + entry.getKey().getQuantity()); c) true, false
System.out.println(Specification: + entry.getValue().getSpecification());
}
d) false, true
}
Ao utilizar quaisquer mtodos valueOf() da classe Integer, se o
class Quantity{
valor passado como parmetro estiver entre -128 e 127, ser utili-
Integer quantity; zado o cache de instncias previamente criadas de acordo com a
COMPUTER_TYPE computerType;

recomendao do padro Flyweight. Dessa forma, toda vez que o
public Quantity(Integer quantity, COMPUTER_TYPE computerType) { mtodo valueOf() for executado recebendo como parmetro um
this.quantity = quantity; valor dentro desse intervalo, ser retornada uma referncia de
this.computerType = computerType;
} um objeto Integer j criado na memria heap e mantido em um
array de Integer esttico dentro da classe IntegerCache. Portanto,
public Integer getQuantity() {
return quantity;
a resposta correta para esse desafio a alternativa c.
} Na Listagem 12 demonstramos trechos reduzidos da classe
IntegerCache, com a implementao do cache dos objetos mais
public COMPUTER_TYPE getComputerType() {
return computerType; utilizados e imutveis.
} O padro Flyweight tem como principal critrio de utilizao que
}
}
os objetos a serem compartilhados sejam imutveis. Como a classe
Integer imutvel, ao observar o cdigo desta classe, veremos que
o atributo value final, sendo atribudo apenas pelo construtor.
Ao executar a classe Client constatamos que quando chamamos No JDK, alm da classe Integer, String, Boolean, Byte, Character,
o mtodo getComputer() de ComputerFactory para obter um Short e Long tambm implementam o Flyweight.
novo objeto de DesktopImpl, nossa factory sempre retornar a Ainda sobre a aplicabilidade desse padro, na plataforma Java
mesma instncia, independentemente do nmero de instncias Enterprise Edition tambm o encontramos. Por exemplo: quando
de DesktopImpl solicitadas por um ou vrios clientes. trabalhamos com EJB 3.1 e possumos um cliente externo que pre-
cisa utilizar um servio EJB interno ao servidor de aplicao, como
Onde o Flyweight utilizado no universo Java e Java EE? alternativa ao mecanismo de Injeo de Dependncias, podemos
Aps demonstrarmos a implementao de um exemplo com o criar um servio de localizao de EJB utilizando Flyweight.
padro de projeto Flyweight, vamos citar alguns casos concretos Esse servio de localizao permite buscar e consequentemente
onde esse pattern aplicado no Java. executar servios remotos internos ao servidor de aplicao.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 21


Vantagens e desvantagens dos padres de projeto Singleton e Flyweight

Listagem 12. Inner class IntegerCache da classe java.lang.Integer. Quanto ao Flyweight, vale ressaltar sua eficincia em situaes
simples, que sugerem evitar repeties na criao de objetos e
/*
* Classe interna privada e esttica da classe java.lang.Integer. Extrados trechos de
assim prover a reutilizao de instncias. Sempre que estiver
* cdigo que representam o uso de Flyweight para reutilizao de valores numricos em dvida sobre quando aplicar esse pattern, lembre-se que se
* entre -128 e 127. sua soluo possui um grande nmero de objetos imutveis que
*/
private static class IntegerCache { podem ser reutilizados, o uso do Flyweight deve ser considerado,
static final int low = -128; melhorando o desempenho e proporcionando uma economia
static final int high = 127;
de memria.
// +1 para considerar o valor zero entre -128 e 127.
static final Integer[] cache = new Integer[(high - low) + 1];

static {
int j = low;
for (int k = 0; k < cache.length; k++) { Autor
vcache[k] = new Integer(j++);
} Ednardo Luiz Martins
} ednardomartins@gmail.com
} Graduado em Cincias da Computao pela Universidade
Federal de Ouro Preto. Em contato com Java desde 2002,
trabalhou como Arquiteto Java em alguns projetos na rea de Segu-
Quando nos deparamos com essa situao, uma soluo im- rana Pblica e Justia. Atualmente Analista de Sistemas Snior em
plementar o padro Service Locator do Core J2EE Patterns. Dessa Florianpolis/SC.
forma, sempre que precisarmos de um EJB fora do servidor de
aplicao, podemos solicitar ao nosso Service Locator. No entanto,
mesmo com a criao desse servio, a operao de localizao ma- Autor
nual pode ter um alto custo, porque envolve o acesso ao servidor Ricardo da S. Longa
de nomes do servidor de aplicao e a obteno do objeto remoto ricardo.longa@gmail.com
Proxy. Dependendo do nmero de objetos e do nmero de vezes Graduado em Sistemas de Informao e ps-graduado em
que esta operao for executada, podemos onerar a performance Engenharia de Software pela Universidade do Sul de Santa
da aplicao. Catarina, um arteso de software h 11 anos, blogueiro nas horas vagas
Com o objetivo de melhorar o servio de localizao de um proxy, e professor de cursos tcnicos, graduao e ps-graduao. Atualmente
trabalha como Analista de Sistemas na Neoway Business Solutions, realiza palestras
implementamos o Flyweight para armazenar e compartilhar esse
relacionadas ao Java/Android desde 2013 e integrante/evangelista do Grupo de
objeto. Dessa forma, a localizao realizada apenas uma vez e
Usurios Java de SC.
reaproveitada todas as vezes que precisarmos utiliz-lo.
O entendimento dos padres de projeto em sua plenitude es-
sencial para a eficincia da implementao quando for necessrio Links:
utiliz-los. Sabendo disso, nosso principal objetivo despertar
no leitor uma viso crtica sobre dois importantes padres: o What is CDI?
Flyweight e o Singleton. http://cdi-spec.org/
Neste contexto, diversas peculiaridades sobre o Singleton ainda Pgina da especificao do EJB 3.1.
so obscuras a muitos desenvolvedores da plataforma Java EE, https://jcp.org/en/jsr/detail?id=318
principalmente quando se trata de projetos que so executados
sobre um servidor em cluster. Peculiaridades estas que podem GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. DESIGN
PATTERNS: Elements of reusable object-oriented software. Addison-Wesley
causar uma srie de transtornos, tais como a criao de novas
Longman, Inc. 1995.
instncias via desserializao, a no replicao automtica do
estado de cada instncia de EJB Singleton rodando em cada n KERIEVSKY, Joshua. REFACTORING TO PATTERNS. Person Education, Inc. 2005.
do cluster (quando espera-se um nico Singleton com estado
ALUR, Deepak; CRUPI, John; MALKS, Dan. CORE J2EE PATTERNS: Best Practices and
para a aplicao) e as diferenas em relao ao EJB Singleton e o Design Strategies. Prentice Hall / Sun Microsystems Press. 2003.
Singleton do CDI.

22 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Hibernate Envers:
Auditoria de dados em Java
Aprenda como implementar a auditoria de
dados com o Hibernate Envers, um requisito to
solicitado pelas corporaes

A Fique por dentro


uditoria pode ser considerada como umexa-
me sistemtico das atividades desenvolvidas
em determinada empresa ou setor, tendo Este artigo til por mostrar como rastrear em um banco de dados
como objetivo averiguar se essas atividades esto de o histrico de mudanas de entidades para saber quem, quando e o
acordo com as diretrizes planejadas e/ou estabelecidas que foi alterado. Aprenderemos de forma bsica e simples os principais
previamente, se foram implementadas com eficcia e conceitos relativos ao Envers, fazendo uma anlise geral sobre seu
se esto adequadas. funcionamento, mostrando como configurar um ambiente de desen-
Com o surgimento dos sistemas computacionais, volvimento com Maven, Eclipse e MySQL para uso desta tecnologia e,
as empresas passaram a confiar seus dados rea em seguida, como implementar um sistema de auditoria.
de Tecnologia da Informao (TI), que se tornou
responsvel pela proteo e garantia da consistncia
desses dados. Um tipo de auditoria muito usado nos sistemas atuais o
Como essas informaes, na maioria das vezes, ficam baseado em Stored Procedures e Triggers, realizado diretamente
armazenadas em bancos de dados, ele o ponto de no SGBD. O inconveniente desse mtodo est na necessidade
partida para iniciar uma auditoria ou conferncia, de se desenvolver uma estrutura especfica para as auditorias
tendo como insumos os dados recebidos e enviados diretamente no banco de dados, gerando assim uma alta de-
pelas aplicaes e sistemas corporativos. Dessa for- pendncia desse componente.
ma, auditar a persistncia requer uma anlise direta Independentemente do porte da aplicao, cada vez mais
de operaes que envolvem o acesso a bancos de comum a necessidade real de monitorar as aes do usurio
dados. frente ao sistema, de modo a prover controle sobre seu uso,
Uma das funes da auditoria monitorar quando assim como resguardar a integridade das informaes admi-
e como o dado foi inserido, com o intuito de prevenir nistradas pela mesma. Em torno desta necessidade, hoje, grande
e detectar problemas no cumprimento das regras de parte das aplicaes desenvolvidas sobre a plataforma Java faz
negcio. Outra funo tem relao com questes de uso de frameworks ORM, sendo o Hibernate o mais adotado. E
segurana, objetivando garantir que usurios no au- foi atravs dessa necessidade que foi criado o Envers.
torizados no estejam acessando o banco de dados. Portanto, ao longo desse artigo abordaremos os principais
Uma boa auditoria de persistncia deve fornecer conceitos e caractersticas do Hibernate Envers, e veremos
informaes sobre as operaes realizadas nas tabelas como desenvolver um sistema de cadastro de projetos conside-
envolvidas, como: quem inseriu, editou, excluiu ou at rando como um dos seus principais requisitos no funcionais
mesmo consultou informaes; que informaes fo- a realizao de auditoria sobre seus dados. Para tanto, sero
ram essas; como estavam antes e como ficaram depois; empregados, alm do Envers, a linguagem de programao
quando foi feito, etc. Com estas informaes e caso Java, o ambiente de desenvolvimento Eclipse integrado ao Ma-
algum dado seja inserido/alterado incorretamente, o ven e o sistema de gerenciamento de banco de dados MySQL.
analista ter meios para que possa recuperar a verso O Hibernate ser usado como soluo integrante da camada
antiga da informao que deseja ou mesmo identificar de persistncia, viabilizando a interface entre a aplicao e o
o autor daquela alterao. MySQL.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 23


Hibernate Envers: Auditoria de dados em Java

Hibernate Envers Nota


O Hibernate um framework de mapeamento objeto relacio- Devido a sua importncia, a partir da verso 3.5 do Hibernate o Envers passou a fazer parte do
nal muito popular entre os desenvolvedores Java. Distribudo projeto core do Hibernate.
com a licena LGPL, foi criado por Gavin King em 2001, sen-
do, sem dvida, o framework de persistncia de dados mais
utilizado, sobretudo por dar suporte a classes codificadas Configurao
com agregaes (aquelas que usam outras classes em suas Aps conhecer as principais informaes relacionadas ao Envers,
operaes), herana, polimorfismo, composies e colees, vamos partir para a parte prtica e desenvolver uma aplicao
por implementar a especificao Java Persistense API, por no que possibilite ao leitor visualizar como o framework funciona
restringir a arquitetura da aplicao e por ser amplamente e sua utilidade.
documentado. Segundo a documentao oficial: o Hibernate No entanto, antes de comearmos a codificar, importante
pretende retirar do desenvolvedor cerca de 95% das tarefas instalar os softwares que nos auxiliaro nesse trabalho e tam-
mais comuns de persistncia de dados. bm preparar o ambiente de desenvolvimento para utilizao
Sua principal caracterstica o mapeamento de classes Java em do Hibernate Envers.
tabelas da base de dados (e dos tipos de dados Java para os da
SQL). Alm disso, o Hibernate gera os comandos SQL e libera o Preparando o ambiente de desenvolvimento
desenvolvedor do trabalho manual de transformao, mantendo Neste artigo optamos por utilizar o Eclipse, pois se trata de um
o programa portvel para quaisquer bancos de dados SQL. ambiente de desenvolvimento extremamente poderoso e flexvel,
O Envers um projeto open source criado pelo engenheiro de sendo o mais utilizado no mercado. Na seo Links voc encontra
software Adam Warski e hoje conduzido pela empresa JBoss. o endereo para download desta IDE. Neste artigo foi adotada a
Essa biblioteca permite a auditoria de classes persistentes (ORM) verso Luna SR2 (4.4.2).
de forma simples e fcil, utilizando-se do conceito de revises, de O processo de instalao do Eclipse bastante simples, sendo
modo similar ao Subversion, onde cada transao uma reviso. necessrio apenas ter o JDK 8 instalado no sistema. Ainda na
A motivao para a utilizao desse recurso poder auditar com- seo Links, est disponvel o endereo onde pode ser obtido
pletamente os dados, ou seja, rastrear o histrico de mudanas e este JDK.
ter as informaes de quem, quando e o que foi modificado. Aps baixar o arquivo adequado ao seu sistema, descompacte-o
A grande inovao dessa ferramenta a gerao e alimentao no local de sua preferncia. Optamos pela pasta C:\Eclipse_Luna.
automtica de tabelas idnticas s tabelas originais, permitin- Logo aps, basta executar o arquivo eclipse.exe (em ambientes
do o controle de verses do contedo das tabelas mapeadas. Windows).
Essas tabelas geradas podem ser chamadas de versionadas ou
de histrico. Integrando o Maven ao Eclipse
Cada vez que uma tabela auditvel sofre uma alterao em seus O Maven uma ferramenta pertencente Fundao Apache que
registros criada uma reviso, que por sua vez representa um tem como finalidade o gerenciamento e automao de construo
registro na tabela versionada contendo um atributo de reviso (build) de projetos Java. De acordo com o prprio site do Maven,
como chave primria, o tipo de operao realizada e todos os a ferramenta tem os seguintes objetivos: prover um padro para
campos auditveis da tabela que sofre auditoria. Em suma, para o desenvolvimento de aplicaes; criar mecanismos para uma
cada UPDATE realizado em uma tabela, um INSERT feito na clara definio da estrutura do projeto; controlar verso e artefa-
tabela versionada. tos; e possibilitar o compartilhamento de bibliotecas entre vrios
Como vantagens da utilizao do Hibernate Envers podemos projetos. Como grande destaque, a principal facilidade ofertada
citar: poucas alteraes no cdigo fonte da aplicao, sendo neces- pelo Maven o gerenciamento de dependncias e este ser o nosso
srio apenas a incluso de algumas anotaes; independncia de motivador para utiliz-lo em parceria com a IDE Eclipse.
fornecedor para o banco de dados; agregao de valor ao produto Na seo Links voc encontrar o endereo para download desta
entregue ao cliente; ganho de produtividade; e pode ser adotado ferramenta. Aqui, adotamos a verso 3.3.1. Aps o download, crie
em qualquer ambiente computacional em que o Hibernate Core uma pasta de nome Maven e copie o arquivo baixado para dentro
funcione. dela. Por fim, descompacte esse arquivo.
Como veremos, o uso do Envers simples e basicamente resume- Para integrar o Maven ao Eclipse, recomenda-se utilizar o plugin
se a incluir a anotao @Audited no cdigo da classe mapeada. M2E. No entanto, a verso do IDE que optamos por utilizar j vem
Feito isso, o framework criar uma tabela com o histrico das com este plugin e com uma instalao embarcada do Maven.
mudanas dos dados da entidade. Caso o desenvolvedor neces- Para visualizarmos esta instalao, com o Eclipse aberto, acesse o
site que algum dos campos no seja includo no histrico, basta menuWindow > Preferencese escolha a opoMaven > Installations,
anot-lo com @NotAudited. Feito isso, se um campo no auditado conforme a Figura 1. Perceba a presena da verso embarcada.
sofrer uma mudana, nenhuma entrada ser inserida na tabela Porm, vamos utilizar a verso do Maven que baixamos, que
de auditoria. uma verso mais atual do software.

24 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Para adicionar o Maven ao Eclipse, ainda na Figura 1, clique vedores Java. Entre suas caractersticas, podemos citar: excelente
em Add e selecione a pasta com a nossa instalao, no caso: C:\ desempenho e estabilidade; suporte a praticamente qualquer
Maven\apache-maven. Feito isso, automaticamente o arquivo de sistema operacional (portabilidade); e interface grfica com boa
configurao do Maven localizado, como pode ser visto na usabilidade. Na seo Links voc encontrar o endereo para
Figura 2, e esta verso adicionada ao IDE. Por fim, clique em Ok. download e mais informaes. A verso adotada neste artigo foi
a Community Server 5.6.22.
O sistema gerenciador de banco de dados Aps o download do instalador do MySQL, os seguintes pas-
O banco de dados que utilizaremos ser o MySQL, uma opo sos devem ser executados para instalar o banco de dados no
gratuita, de cdigo aberto, simples e muito adotado por desenvol- sistema:
1. Clique duas vezes sobre o instalador. Feito isso, uma nova janela
ser exibida solicitando que o usurio aguarde enquanto o sistema
operacional configura o instalador do SGBD;
2. Em seguida, uma tela exibe os termos do contrato de licena do
produto. Neste ponto, aceite os termos e clique em Next;
3. O prximo passo escolher o tipo de instalao. Abaixo de
cada tipo existe uma breve explicao a respeito. O usurio deve
escolher a opo que melhor atenda aos seus propsitos. Aqui,
optamos pela Custom. Logo aps, clique em Next;
4. A prxima tela oferece ao usurio a possibilidade de escolher
os produtos que deseja instalar. Escolha MySQL Server 5.6.21 e
clique em Next;
5. Em seguida possvel verificar os produtos selecionados na tela
anterior. Clique em Execute para dar sequncia instalao;
6. Aps a instalao ser concluda, a mensagem presente na coluna
Status da tela de instalao mudar de Ready to Download para
Complete. Selecione Next;
7. Finalizada a instalao, o prximo passo configurar o MySQL
Server. Sendo assim, clique mais uma vez em Next;
8. Nesse momento o usurio deve escolher o tipo de configurao
para o MySQL Server. Em nosso estudo, apenas mantenha os
valores padro e clique em Next;
Figura 1. Instalao do Maven fornecida pelo Eclipse
9. A tela seguinte (veja a Figura 3) pede para definir a senha do
administrador. Portanto, informe uma senha e clique em Next;

Figura 2. Adicionando o Maven ao Eclipse Figura 3. Definindo a senha do administrador

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 25


Hibernate Envers: Auditoria de dados em Java

10. Feito isso, a prxima tela permite ao usurio configurar o uma aplicao desktop. O objetivo desta ser gerenciar o cadastro
MySQL como um servio do Sistema Operacional Windows. Para de projetos de uma empresa.
tanto, basta manter os valores default e clicar em Next; O sistema que construiremos conter apenas trs entidades (Fun-
11. A janela que aparecer detalha todos os passos de configura- cionario, Projeto e Trabalha) e a partir delas sero construdas as
o que sero aplicados. Confirmadas estas escolhas, clique em principais operaes realizadas sobre um cadastro, como salvar,
Execute, como pode ser visto na Figura 4; atualizar, listar e excluir.
12. Neste momento, uma tela mostrando o fim do processo ser As informaes que cada funcionrio carregar so: cdigo,
exibida. Clique ento em Finish; nome, CPF, cargo, endereo e salrio. J um projeto conter: um
13. Ento, uma nova janela informa que a configurao do MySQL cdigo, nome, data de incio e data de trmino.
Server foi realizada (Figura 5). Clique pela ltima vez em Next; A entidade Trabalha resultado da relao muitos para muitos
14. Para encerrar, uma janela exibida indicando que a instalao existente entre Funcionario e Projeto e armazenar o cdigo de
foi concluda. Neste momento, apenas clique em Finish. ambos. Saiba que no necessrio criar uma classe mapeada para
essa entidade, uma vez que o prprio Hibernate gera uma tabela
Desenvolvendo o cadastro de funcionrios de ligaopara viabilizar esse relacionamento.
Agora que temos o banco de dados instalado, assim como o A Figura 6 mostra o relacionamento existente (diagrama entida-
Eclipse e o Maven integrados, vamos partir para a construo de de relacionamento) entre as tabelas do banco de dados.

Figura 6. Diagrama Entidade Relacionamento

Criando o banco de dados


Com o MySQL instalado, abra-o e crie o banco empresabd execu-
tando o script SQL apresentado na Listagem 1. Os comandos das
linhas 1 e 2 possibilitam, respectivamente, criar o banco de dados
e entrar no contexto do banco criado. As tabelas e seus respectivos
Figura 4. Aplicando as configuraes definidas no Servidor
atributos so criados nas linhas seguintes, sendo que a tabela
trabalha resultado da relao muitos para muitos, existente entre
as tabelas funcionrio e projeto.

Criando o projeto Maven no Eclipse


Com o banco de dados pronto, vamos partir para o projeto Java,
criando um novo a partir do Maven no Eclipse. Como utilizare-
mos anotaes JPA, importante que o projeto adote o Java 1.5
ou superior. Dito isso, com o Eclipse aberto, clique em File > New
Project e, na tela que aparecer, selecione a opoCreate a simple
project (skip archetype selection)e clique emNext.
Na tela seguinte, vamos identificar o projeto preenchendo as
seguintes opes:
Group ID (identificador da empresa/grupo ao qual o projeto
pertence):br.com.devmedia;
Artifact ID(nome do projeto):hibernate-envers;
Packaging (forma como o projeto dever ser empacotado): jar;
Figura 5. Configurao do MySQL finalizada Version (verso do projeto): 0.0.1-SNAPSHOT.

26 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Listagem 1. Script para criao do banco de dados.

01. CREATE database empresabd;


02. USE empresabd;
03.
04. CREATE TABLE funcionario (
05. codigo INT NOT NULL AUTO_INCREMENT,
06. nome VARCHAR(100),
07. cargo VARCHAR(30),
08. salario DOUBLE,
09. cpf VARCHAR(20),
10. endereco VARCHAR (100),
11. PRIMARY KEY (codigo)
12. );
13.
14. CREATE TABLE projeto (
15. codigo INT NOT NULL AUTO_INCREMENT,
16. nome VARCHAR(100),
17. data_inicio DATE,
18. data_fim DATE,
19. PRIMARY KEY (codigo)
20. );
21.
22. CREATE TABLE trabalha ( Figura 7. Estrutura do projeto Maven
23. cod_funcionario INT,
24. cod_projeto INT, O arquivo pom.xml ficar conforme o cdigo apresentado na
25. PRIMARY KEY (cod_funcionario, cod_projeto),
Listagem 2.
26. CONSTRAINT FK_PROJETO FOREIGN KEY (cod_projeto)
REFERENCES PROJETO (codigo), Inseridas as dependncias, clique com o boto direito do mouse
27. CONSTRAINT FK_FUNCIONARIO FOREIGN KEY (cod_funcionario) sobre o projeto e selecione o menu Maven > Update Project. Com
REFERENCES FUNCIONARIO (codigo)
isso, todas as bibliotecas definidas sero adicionadas ao projeto.
28.
29. );
Construindo as entidades
Nosso prximo passo criar, mapear as entidades e definir
Aps preencher todos os dados mencionados, clique emFinish. aquelas que queremos que sejam auditadas. Tudo isso lanando
Note que a estrutura que criamos ainda no est definida com as mo de annotations.
configuraes de um projeto Java. Sendo assim, clique com o boto Para tanto, primeiramente vamos criar a classe Funcionario. Esta
direito sobre o projeto e acesse o menuMaven > Update Project. uma das classes de negcio da aplicao e ser mapeada pelo
Na tela que aparecer, selecione o projeto que acabamos de criar e Hibernate como uma tabela no banco de dados. Classes desse tipo
clique emOkpara atualiz-lo de acordo com as configuraes do so classes Java simples, definidas como POJOs. Elas contm todos
Maven. Feito isso, o projeto passar a ter a estrutura caracterstica os seus atributos encapsulados atravs dos mtodos de acesso get
do Maven, como demonstra Figura 7. e set e tambm disponibilizam o construtor padro.
Essa estrutura pode ser descrita da seguinte maneira: Em nosso exemplo, as classes de negcio ficaro dentro do
src/main/java: pasta onde ficam os pacotes e classes Java; pacote com.jm.entidade. Para cri-lo, clique com o boto direito
src/main/resources: pasta onde ficam os arquivos de propriedades do mouse sobre o projeto hibernate-envers-01, selecione New >
(persistence.xml, configurao de logs, etc.); Package e informe seu nome. Com o pacote criado, clique com o
src/test/java: pasta onde ficam os arquivos de teste unitrios; boto direito do mouse sobre ele, escolha a opo New > Class e,
src/test/resources: pasta onde ficam os arquivos de propriedades na tela que aparecer, d o nome Funcionario classe, cujo cdigo
que so usados para os testes; apresentado na Listagem 3.
pom.xml: esse o arquivo onde ficam todas as configuraes per- Na linha 5 podemos verificar a anotao @Entity, principal
tinentes gerao de build, testes, bibliotecas de terceiros, etc. anotao do JPA. Ela aparece antes do nome da classe e sinaliza
que haver uma tabela relacionada a essa classe no banco de dados
Adicionando as dependncias e que os objetos desta sero persistidos.
Com o projeto pronto, o prximo passo inserir as dependncias, J a anotao @id, vista na linha 12, utilizada para indicar qual
isto , adicionar as bibliotecas que sero utilizadas pela aplicao atributo de uma classe anotada com @Entity ser mapeado como
no arquivo pom.xml. Para isso, clique com o boto direto neste a chave primria da tabela correspondente classe.
arquivo e escolha a opo Maven > Add Dependency. Na tela que Uma anotao que geralmente acompanha @id a @Generated-
aparecer, digite o nome das bibliotecas que deseja adicionar, sendo Value, presente na linha 14. Esta serve para indicar que o valor do
elas: Hibernate Core, Hibernate Envers e o driver do MySQL. Em atributo que compe a chave primria deve ser gerado pelo banco
seguida, clique em Ok. no momento em que um novo registro for inserido

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 27


Hibernate Envers: Auditoria de dados em Java

Listagem 2. Configurao do arquivo pom.xml. Ainda podemos utilizar as anotaes @Column e @Table, que
so usadas para personalizar o nome das tabelas e das colunas,
01. <project xmlns=http://maven.apache.org/POM/4.0.0 bastando informar o nome entre parnteses. No nosso caso, como
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
no adicionamos essa informao, fica considerado que o nome
xsi:schemaLocation=http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd>
das tabelas e colunas ser exatamente igual ao nome das classes
02. <modelVersion>4.0.0</modelVersion> e suas propriedades.
03. <groupId>br.com.devmedia</groupId> A anotao que indica que a entidade criada passar por audi-
04. <artifactId>hibernate-envers-01</artifactId> toria pode ser vista na linha 6. Trata-se da anotao @Audited,
05. <version>0.0.1-SNAPSHOT</version>
pertencente ao pacote org.hibernate.envers. A presena dela faz
06. <dependencies>
07. <dependency>
com que outra tabela seja criada no banco, sendo utilizada para
08. <groupId>mysql</groupId> armazenar todas as alteraes sofridas pela entidade auditada.
09. <artifactId>mysql-connector-java</artifactId> O nome da tabela criada automaticamente pode ser customizado
10. <version>5.1.34</version> atravs da anotao @AuditTable. Quando no se utiliza essa ano-
11. </dependency> tao, o Envers entende que a nomenclatura da tabela de histrico
12. <dependency>
deve ter o nome da classe com o sufixo _AUD.
13. <groupId>org.hibernate</groupId>
14. <artifactId>hibernate-core</artifactId> Agora vamos criar a outra classe, que mapear a entidade Pro-
15. <version>4.3.8.Final</version> jeto. Os passos so idnticos aos executados para criar a classe
16. </dependency> Funcionario. Na Listagem 4 possvel visualizar seu cdigo.
17. <dependency> Assim como na classe Funcionario, perceba na linha 6 a presen-
18. <groupId>org.hibernate</groupId>
a da anotao @Audited, indicando que a entidade em questo
19. <artifactId>hibernate-envers</artifactId>
20. <version>4.3.8.Final</version>
sofrer auditoria. Portanto, uma tabela de histrico de nome pro-
21. </dependency> jeto_AUD ser criada no banco de dados para armazenar todas
22. </dependencies> as alteraes ocorridas nessa entidade.
23. </project>

Listagem 3. Cdigo da classe Funcionario. Listagem 4. Cdigo da classe Projeto.

01. package com.jm.entidade; 01. package com.jm.entidade;


02. 02.
03. //imports omitidos... 03. //imports omitidos...
04. 04.
05. @Entity 05. @Entity
06. @Audited 06. @Audited
07. @Table 07. @Table
08. public class Funcionario implements Serializable { 08. public class Projeto implements Serializable {
09. 09.
10. private static final long serialVersionUID = 1L; 10. private static final long serialVersionUID = 1L;
11. 11.
12. @Id 12. @Id
13. @Column 13. @Column
14. @GeneratedValue(strategy = GenerationType.AUTO) 14. @GeneratedValue(strategy = GenerationType.AUTO)
15. private int codigo; 15. private int codigo;
16. 16.
17. @Column 17. @NotAudited
18. private String nome; 18. @Column
19. @Column 19. private String nome;
20. private String cargo; 20. @Column
21. @Column 21. private Date data_Inicio;
22. private double salario; 22. @Column
23. @Column 23. private Date data_Fim;
24. private String cpf; 24.
25. @Column 25. @ManyToMany(cascade = {CascadeType.ALL}, fetch=FetchType.EAGER)
26. private String endereco; 26. @JoinTable(name=trabalha, joinColumns=
27. 27. {@JoinColumn(name=cod_projeto)}, inverseJoinColumns=
28. @ManyToMany(mappedBy = funcionarios, fetch=FetchType.LAZY) 28. {@JoinColumn(name=cod_funcionario)})
29. private List<Projeto> projetos; 29. private List<Funcionario> funcionarios;
30. 30.
31. //construtores e mtodos gets e sets omitidos 31. //construtores e mtodos gets e sets omitidos
32. } 32. }

28 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Configurando a aplicao connection.url (linha 7): especifica a URL de conexo com o
Nesse momento, alm de informar ao Hibernate onde est nosso banco de dados;
banco de dados e como se conectar a ele, precisamos registrar os connection.username (linha 8): refere-se ao nome de usurio
event listeners que permitiro ao Envers checar se alguma entidade com o qual o Hibernate deve se conectar ao banco de dados;
auditada foi modificada. connection.password (linha 9): especifica a senha do usurio
Os event listeners, assim como as configuraes do Hibernate, com o qual o Hibernate deve se conectar ao banco;
devem ser adicionados em um arquivo XML. Sendo assim, crie o show_sql (linha 10): flag que permite tornar visvel no console o
arquivo hibernate.cfg.xml. Para isso, clique com o boto direito sobre SQL que o Hibernate est gerando. Assim, o desenvolvedor pode
o projeto, selecione Novo > Documento XML e o chame de hibernate copiar e colar os comandos no banco de dados para verificar se
.cfg. Seu contedo deve ser semelhante ao da Listagem 5. est tudo conforme o esperado;
mapping (linha 11): informa as classes que esto mapeadas
Listagem 5. Contedo do arquivo hibernate.cfg.xml. para realizar operaes no banco.

01. <?xml version=1.0 encoding=UTF-8?>


A partir da linha 13 possvel visualizar os event listeners. Estes
02. <!DOCTYPE hibernate-configuration PUBLIC -//Hibernate/Hibernate
Configuration DTD 3.0//EN http://hibernate.sourceforge.net/ permitem ao Envers trabalhar em conjunto com o Hibernate e
hibernate-configuration-3.0.dtd> so responsveis pelas aes de auditoria, ou seja, tm a funo
03. <hibernate-configuration>
de incluir registros nas tabelas de histrico de acordo com a ao
04. <session-factory>
05. <property name=hibernate.dialect>org.hibernate.dialect.MySQL realizada pelo usurio na aplicao.
Dialect</property>
06. <property name=hibernate.connection.driver_class> Criando a conexo com o banco de dados
com.mysql.jdbc.Driver</property>
07. <property name=hibernate.connection.url> Configurada a aplicao, vamos criar agora a classe auxiliar
jdbc:mysql://localhost:3306/empresabd?zeroDateTimeBehavior= HibernateUtil, que ser responsvel por entregar uma instncia
convertToNull</property> de SessionFactory aplicao.
08. <property name=hibernate.connection.username>root</property>
09. <property name=hibernate.connection.password>1234</property>
Com o intuito de manter a organizao do projeto, esta classe
10. <property name=hibernate.show_sql>true</property> ser criada no pacote com.jm.util e seu cdigo pode ser visto na
11. <mapping class=com.jm.entidade.Funcionario /> Listagem 6.
12. <mapping class=com.jm.entidade.Projeto />
13. <listener class=org.hibernate.envers.event.AuditEventListener
type=post-insert /> Listagem 6. Cdigo da classe HibernateUtil.
14. <listener class=org.hibernate.envers.event.AuditEventListener
type=post-update /> 01. package util;
15. <listener class=org.hibernate.envers.event.AuditEventListener 02.
type=post-delete /> 03. //imports omitidos
16. <listener class=org.hibernate.envers.event.AuditEventListener 04.
type=pre-collection-update /> 05. public class HibernateUtil {
17. <listener class=org.hibernate.envers.event.AuditEventListener 06.
type=pre-collection-remove /> 07. private static SessionFactory sessionFactory;
18. <listener class=org.hibernate.envers.event.AuditEventListener 08.
type=post-collection-recreate /> 09. public static SessionFactory getSessionFactory() {
19. </session-factory> 10. if (sessionFactory == null) {
20. </hibernate-configuration> 11. Configuration configuration = new Configuration().configure();
12. ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
13. .applySettings(configuration.getProperties()).build();
14.
Este arquivo comea com a definio do DTD (Document Type 15. sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Definition) e na linha 3 temos o elemento raiz, <hibernate-confi- 16. SchemaUpdate se = new SchemaUpdate(configuration);
17. se.execute(true, true);
guration>. Logo aps, encontramos o elemento <session-factory>, 18. }
na linha 4, local onde se inicia a configurao da conexo com 19.
o banco de dados e tambm so adicionadas as informaes de 22. return sessionFactory;
23. }
mapeamento. 24.}
As propriedades configuradas so apresentadas a seguir:
dialect (linha 5): especifica o dialeto com o qual o Hibernate
se comunicar com a base de dados, isto , informa ao Hibernate A SessionFactory uma classe de infraestrutura do Hibernate
quais comandos SQL gerar diante de um banco de dados rela- que implementa o design pattern Abstract Factory para construir
cional especfico; instncias de objetos do tipo org.hibernate.Session. Essas ins-
connection.driver_class (linha 6): determina o nome da classe tncias so usadas para realizar as tarefas de persistncia do
do driver JDBC utilizado. No nosso caso, trata-se do driver para framework, sendo que cada instncia de SessionFactory possui
o MySQL; um estado interno imutvel, ou seja, uma vez criada, os seus

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 29


Hibernate Envers: Auditoria de dados em Java

atributos no sero modificados. Este estado interno inclui o me- jm.acessobd clicando com o boto direito do mouse sobre o projeto
tadata referente ao mapeamento objeto relacional das entidades do hibernate-envers-01, escolhendo New > Package e informando com.
sistema e tambm a referncia de quem ir fornecer as conexes jm.acessobd como nome.
com o banco de dados. Por esse motivo, a classe HibernateUtil Criado o pacote, clique sobre ele com o boto direito, escolha
utiliza-se do mtodo esttico getSessionFactory(), visto na linha 9, New > Class e d o nome classe de FuncionarioDAO.
para oferecer sempre a mesma instncia de um SessionFactory O cdigo fonte dessa classe deve ficar semelhante ao apresentado
no contexto da aplicao. na Listagem 7.
Por fim, na linha 15, temos o mtodo privado esttico build- Na linha 9, dentro do construtor da classe FuncionarioDAO,
SessionFactory(), que responsvel por construir a instncia de o mtodo getSessionFactory() invocado para a criao de um
SessionFactory. Essa construo feita com base no arquivo de SessionFactory. Esse objeto deve ser criado uma nica vez, pois,
configurao do Hibernate. por armazenar os mapeamentos e configuraes do Hibernate,
muito pesado e lento de se criar.
Persistindo dados no banco J na linha 12, o mtodo adicionarFuncionario() recebe como
Agora, criaremos as classes que sero responsveis por via- parmetro os dados do funcionrio e realiza a insero deste no
bilizar todas as operaes de persistncia disponibilizadas banco de dados. Para isso, na linha 13 obtida uma sesso a partir
pela aplicao. Para isso, criemos primeiramente o pacote com. de um SessionFactory.

Listagem 7. Cdigo da classe FuncionarioDAO. Disponibiliza todas as operaes que sero realizadas no banco de dados.

01. package com.jm.acessobd; 45. session.close();


02. 46. }
03. //imports omitidos... 47. return funcionarios;
04. 48. }
05. public class FuncionarioDAO { 49.
06. private static SessionFactory factory; 50. public void atualizarFuncionario(Integer codigoFuncionario, int salario) {
07. 51. Session session = factory.openSession();
08. public FuncionarioDAO() { 52. Transaction tx = null;
09. factory = HibernateUtil.getSessionFactory(); 53. try {
10. } 54. tx = session.beginTransaction();
11. 55. Funcionario funcionario = (Funcionario) session.get
12. public Integer adicionarFuncionario(Funcionario funcionario) { (Funcionario.class, codigoFuncionario);
13. Session session = factory.openSession(); 56. funcionario.setSalario(salario);
14. Transaction tx = null; 57. session.update(funcionario);
15. Integer cod_func = null; 58. tx.commit();
16. try { 59. } catch (HibernateException e) {
17. tx = session.beginTransaction(); 60. if (tx != null) {
18. cod_func = (Integer) session.save(funcionario); 61. tx.rollback();
19. tx.commit();//executa o commit 62. }
20. } catch (HibernateException e) { 63. e.printStackTrace();
21. if (tx != null) { 64. } finally {
22. tx.rollback(); 65. session.close();
23. } 66. }
24. e.printStackTrace(); 67. }
25. } finally { 68.
26. session.close(); 69. public void apagarFuncionario(Integer codigoFuncionario) {
27. } 70. Session session = factory.openSession();
28. return cod_func; 71. Transaction tx = null;
29. } 72. try {
30. 73. tx = session.beginTransaction();
31. public List<Funcionario> listarFuncionarios() { 74. Funcionario funcionario = (Funcionario) session.get(Funcionario.class,
32. Session session = factory.openSession(); codigoFuncionario);
33. Transaction tx = null; 75. session.delete(funcionario);
34. List<Funcionario> funcionarios = null; 76. tx.commit();
35. try { 77. } catch (HibernateException e) {
36. tx = session.beginTransaction(); 78. if (tx != null) {
37. funcionarios = session.createCriteria(Funcionario.class).list(); 79. tx.rollback();
38. tx.commit(); 80. }
39. } catch (HibernateException e) { 81. e.printStackTrace();
40. if (tx != null) { 82. } finally {
41. tx.rollback(); 83. session.close();
42. } 84. }
43. e.printStackTrace(); 85. }
44. } finally { 86. }

30 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Um objeto Session pode ser considerado como uma sesso de tro, assim como o mtodo delete(), chamado na linha 79, remove
comunicao com o banco de dados atravs de uma conexo o funcionrio passado como parmetro da base de dados.
JDBC. A operao beginTransaction(), presente na linha 17, inicia Depois de criar a classe FuncionarioDAO, deve-se criar a classe
uma transao. Em seguida, o mtodo save(), invocado na linha ProjetoDAO, no mesmo pacote e da mesma forma que a classe
18, realiza a persistncia do objeto. Com isso, um registro adi- anterior. O cdigo fonte ficar conforme a Listagem 8. Como po-
cionado na tabela funcionario de acordo com os valores definidos demos verificar, a lgica empregada foi a mesma utilizada para
no objeto. Por fim, na linha 19, o commit finaliza a transao e a o DAO da classe Funcionario. A nica alterao que o objeto
sesso fechada na linha 26. que passar a ser persistido do tipo Projeto.
Alm deste, outros trs mtodos foram criados, a saber: apagar-
Funcionario(), atualizarFuncionario() e ListarFuncionarios(). Testando a aplicao
Basicamente, esses mtodos possuem a mesma estrutura com Nesse momento, podemos partir para os testes. Para isso, vamos
relao ao mtodo j explicado. As diferenas encontram-se nas criar duas classes de teste: TesteEnvers1 e TesteEnvers2. Visando
linhas 37, 61 e 79. manter a organizao do projeto, crie estas classes no pacote com.
Na linha 37, o mtodo createCriteria(), da classe Session, cria jm.teste.
uma query passando como parmetro a classe que vai ser pesqui- O cdigo da classe TesteEnvers1 apresentado na Listagem 9.
sada; no nosso caso, Funcionario. Por sua vez, o mtodo update(), O seu objetivo cadastrar alguns funcionrios na base de dados.
na linha 61, realiza a atualizao do objeto passado como parme- A partir da linha 8 (at a linha 11) so instanciados quatro objetos

Listagem 8. Cdigo da classe ProjetoDAO. Disponibiliza todas as operaes que sero realizadas no banco de dados.

01. package com.jm.acessobd; 44. } finally {


02. 45. session.close();
03. //imports omitidos... 46. }
04. 47. return projetos;
05. public class ProjetoDAO { 48. }
06. private static SessionFactory factory; 49.
07. 50. public void atualizarProjeto(Integer cod, String nome) {
08. public ProjetoDAO() { 51. Session session = factory.openSession();
09. factory = HibernateUtil.getSessionFactory(); 52. Transaction tx = null;
10. } 53. try {
11. 54. tx = session.beginTransaction();
12. public Integer adicionarProjeto(Projeto projeto) { 55. Projeto projeto = (Projeto) session.get(Projeto.class, cod);
13. Session session = factory.openSession(); 56. projeto.setNome(nome);
14. Transaction tx = null; 57. session.update(projeto);
15. Integer cod_func = null; 58. tx.commit();
16. try { 59. } catch (HibernateException e) {
17. tx = session.beginTransaction(); 60. if (tx != null) {
18. cod_func = (Integer) session.save(projeto); 61. tx.rollback();
19. tx.commit();//executa o commit 62. }
20. } catch (HibernateException e) { 63. e.printStackTrace();
21. if (tx != null) { 64. } finally {
22. tx.rollback(); 65. session.close();
23. } 66. }
24. e.printStackTrace(); 67. }
25. } finally { 68.
26. session.close(); 69. public void apagarProjeto(Integer cod) {
27. } 70. Session session = factory.openSession();
28. return cod_func; 71. Transaction tx = null;
29. } 72. try {
30. 73. tx = session.beginTransaction();
31. public List<Projeto> listarProjetos() { 74. Projeto projeto = (Projeto) session.get(Projeto.class, cod);
32. Session session = factory.openSession(); 75. session.delete(projeto);
33. Transaction tx = null; 76. tx.commit();
34. List<Projeto> projetos = null; 77. } catch (HibernateException e) {
35. try { 78. if (tx != null) {
36. tx = session.beginTransaction(); 79. tx.rollback();
37. projetos = session.createCriteria(Projeto.class).list(); 80. }
38. tx.commit(); 81. e.printStackTrace();
39. } catch (HibernateException e) { 82. } finally {
40. if (tx != null) { 83. session.close();
41. tx.rollback(); 84. }
42. } 85. }
43. e.printStackTrace(); 86. }

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 31


Hibernate Envers: Auditoria de dados em Java

da classe Funcionario. Esses objetos so internalizados no banco J a tabela REVINFO inclui os seguintes campos: REV, que faz a
de dados atravs do mtodo adicionarFuncionario(), implemen- ligao com as tabelas de auditoria individuais (as de final _aud);
tado na classe FuncionarioDAO. e REVTSTMP, que indica o momento exato (timestamp) em que a
A Listagem 10 mostra o resultado da execuo dessa classe, operao ocorreu.
obtido atravs do console da IDE Eclipse. A Figura 8 mostra as alteraes provocadas na estrutura do
banco de dados aps a execuo da classe de teste. Note que alm
Listagem 9. Cdigo da classe de teste 1. das tabelas mencionadas, foram criadas as tabelas versionadas
Projeto_AUD e Trabalha_AUD, que armazenaro o histrico das
01. package com.jm.teste;
02. //imports omitidos...
mudanas sofridas por suas respectivas entidades. Nesse momen-
03. to, essas tabelas encontram-se vazias pois ainda no foi realizada
04. public class TesteEnvers1 { qualquer operao sobre as tabelas originais.
05.
06. public static void main(String[] args) throws ParseException {
07.
08. Funcionario funcionario1 = new Funcionario(Clenio Rocha,
Analista de TI, 5600, 06493084765, Rua Rio Paranaiba);
09. Funcionario funcionario2 = new Funcionario(Carlos Martins,
Analista de TI, 4300, 06433055765, Rua Abadia dos Dourados);
10. Funcionario funcionario3 = new Funcionario(Daniel Cioqueta,
Analista de TI, 4200, 06493084444, Rua Rio das Ostras);
11. Funcionario funcionario4 = new Funcionario(Yujo Rodrigues,
Gerente de Projetos, 4110, 07777775765, Rua dos Patos);
12.
13. FuncionarioDAO funcionarioDao = new FuncionarioDAO();
14. funcionarioDao.adicionarFuncionario(funcionario1);
15. funcionarioDao.adicionarFuncionario(funcionario2);
16. funcionarioDao.adicionarFuncionario(funcionario3);
17. funcionarioDao.adicionarFuncionario(funcionario4);
Figura 8. Viso do banco de dados aps a execuo da classe de teste
18. }
19. }

Listagem 10. Sada gerada pela execuo da classe TesteEnvers1. A Figura 9 mostra os registros adicionados tabela Funcio-
nario_ AUD. Alm de todos os campos auditados, podemos
01. Hibernate: insert into Funcionario (cargo, cpf, endereco, nome, salario) verificar as colunas REV e REVTYPE. A primeira coluna, como
values (?, ?, ?, ?, ?)
02. Hibernate: insert into REVINFO (REVTSTMP) values (?) j mencionado, armazena o identificador da reviso, e chave
03. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco, estrangeira da tabela REVINFO. J a segunda coluna indica
nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
o tipo de ao executada. O valor 0 indica que foi adicionado
04. Hibernate: insert into Funcionario (cargo, cpf, endereco, nome, salario)
values (?, ?, ?, ?, ?) um novo registro. A Figura 9 mostra, ainda, a partir de uma
05. Hibernate: insert into REVINFO (REVTSTMP) values (?) consulta, como ficou a tabela REVINFO. Essa tabela armazena
06. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
os identificadores das revises, assim como o momento em que
nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
07. Hibernate: insert into Funcionario (cargo, cpf, endereco, nome, salario) cada operao ocorreu.
values (?, ?, ?, ?, ?) Dando sequncia aos testes, vamos realizar mais algumas aes
08. Hibernate: insert into REVINFO (REVTSTMP) values (?)
09. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
contra o banco de dados para avaliar outros detalhes sobre como
nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?) o Hibernate Envers se comporta. Para isso, execute o cdigo da
10. Hibernate: insert into Funcionario (cargo, cpf, endereco, nome, salario) classe TesteEnvers2, apresentado na Listagem 11.
values (?, ?, ?, ?, ?)
11. Hibernate: insert into REVINFO (REVTSTMP) values (?)
O objetivo desta classe cadastrar um projeto e associar al-
12. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco, guns funcionrios a ele. Os funcionrios associados ao projeto
nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?) sero aqueles criados na execuo anterior e que nesse momento
encontram-se na base de dados. A Listagem 12 mostra o resultado
Perceba que, alm dos inserts na tabela funcionario (linhas 1, 4, da execuo, obtido atravs do console do Eclipse.
7 e 10), foram feitas inseres em outras duas tabelas: REVINFO Analisando o resultado da execuo da classe TesteEnvers2, note
e Funcionario_AUD. que realizada uma busca por todos os funcionrios cadastrados
A tabela Funcionario_ AUD armazenar o histrico de mu- na linha 1. Como trata-se de uma busca e nenhuma alterao
danas sofridas pela entidade funcionrio, sendo composta por realizada nas tabelas auditadas, nenhum insert ser registrado
todos os campos auditados da tabela original, alm de outros nas tabelas versionadas.
dois atributos: o cdigo da reviso (REV), que representa a Na linha 2, nota-se que foi criada uma entrada na tabela projeto.
chave primria da tabela; e o tipo de reviso (REVTYPE), que Esta ao gerou na linha 12 a adio de um registro na tabela
representa a ao executada, como incluso, alterao ou ex- Projeto_AUD. Esse registro contm os campos da tabela original,
cluso de dados. o tipo de operao e o cdigo da reviso.

32 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Em seguida, a atualizao realizada na tabela Funcionario (vide Listagem 12. Sada da execuo da classe TesteEnvers2.
linhas 3, 4 ,5 e 6) tambm gerou a incluso de registros em Funcio-
nario_AUD. As linhas 13, 14, 15 e 16 mostram os inserts realizados 01. Hibernate: select this_.codigo as codigo1_0_0_, this_.cargo as cargo2_0_0_,
this_.cpf as cpf3_0_0_, this_.endereco as endereco4_0_0_, this_.nome as
nesta tabela. nome5_0_0_, this_.salario as salario6_0_0_ from Funcionario this_
Note ainda que a tabela trabalha tambm sofreu alteraes. Foram 02. Hibernate: insert into Projeto (data_Fim, data_Inicio, nome) values (?, ?, ?)
03. Hibernate: update Funcionario set cargo=?, cpf=?, endereco=?, nome=?,
inseridos quatro registros, nas linhas 7, 8, 9 e 10. Em virtude dessa
salario=? where codigo=?
alterao, a tabela versionada Trabalha_AUD tambm recebeu 04. Hibernate: update Funcionario set cargo=?, cpf=?, endereco=?, nome=?,
inseres, como expe as linhas 17, 18, 19 e 20. salario=? where codigo=?
05. Hibernate: update Funcionario set cargo=?, cpf=?, endereco=?, nome=?,
salario=? where codigo=?
Listagem 11. Cdigo da classe de teste 2. 06. Hibernate: update Funcionario set cargo=?, cpf=?, endereco=?, nome=?,
salario=? where codigo=?
01. package com.jm.teste; 07. Hibernate: insert into trabalha (cod_projeto, cod_funcionario) values (?, ?)
02. 08. Hibernate: insert into trabalha (cod_projeto, cod_funcionario) values (?, ?)
03. //imports omitidos 09. Hibernate: insert into trabalha (cod_projeto, cod_funcionario) values (?, ?)
04. 10. Hibernate: insert into trabalha (cod_projeto, cod_funcionario) values (?, ?)
05. public class TesteEnvers2 { 11. Hibernate: insert into REVINFO (REVTSTMP) values (?)
06. 12. Hibernate: insert into Projeto_AUD (REVTYPE, data_Fim, data_Inicio, nome,
07. public static void main(String[] args) throws ParseException { codigo, REV) values (?, ?, ?, ?, ?, ?)
08. 13. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
09. FuncionarioDAO funcionarioDao = new FuncionarioDAO(); nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
10. List<Funcionario> funcionarios = funcionarioDao.listarFuncionarios(); 14. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
11. nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
12. ProjetoDAO projetoDao = new ProjetoDAO(); 15. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
13. nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
14. Date dataInicio = Date.from(LocalDate.of( 2014 , 12, 25).atStartOfDay(). 16. Hibernate: insert into Funcionario_AUD (REVTYPE, cargo, cpf, endereco,
atZone(ZoneId.systemDefault()).toInstant()); nome, salario, codigo, REV) values (?, ?, ?, ?, ?, ?, ?, ?)
15. Date dataFim = Date.from(LocalDate.of( 2015 , 5, 25).atStartOfDay(). 17. Hibernate: insert into trabalha_AUD (REVTYPE, REV, cod_projeto,
atZone(ZoneId.systemDefault()).toInstant()); cod_funcionario) values (?, ?, ?, ?)
16. 18. Hibernate: insert into trabalha_AUD (REVTYPE, REV, cod_projeto,
17. Projeto projeto = new Projeto(Nono Digito, dataInicio, dataFim, cod_funcionario) values (?, ?, ?, ?)
funcionarios); 19. Hibernate: insert into trabalha_AUD (REVTYPE, REV, cod_projeto,
18. projetoDao.adicionarProjeto(projeto); cod_funcionario) values (?, ?, ?, ?)
19. } 20. Hibernate: insert into trabalha_AUD (REVTYPE, REV, cod_projeto,
20. } cod_funcionario) values (?, ?, ?, ?)

Figura 9. Viso das tabelas alteradas aps a execuo da classe de teste

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 33


Hibernate Envers: Auditoria de dados em Java

Alm de tudo isso, pode tornar o trabalho muito mais produtivo


com o mnimo de alteraes no cdigo, pois como vimos, basta a
incluso de algumas anotaes e poucas configuraes para que
o framework funcione.

Autor
Carlos Alberto Silva
casilvamg@hotmail.com
formado em Cincia da Computao pela Universidade Federal
de Uberlndia (UFU), com especializao em Desenvolvimento
Java pelo Centro Universitrio do Tringulo (UNITRI) e em Anlise e De-
senvolvimento de Sistemas Aplicados a Gesto Empresarial pelo Instituto
Federal do Tringulo Mineiro (IFTM). Trabalha atualmente na empresa Algar Telecom como
Analista de TI. Possui as seguintes certificaes: OCJP, OCWCD e ITIL.

Links:
Figura 10. Viso das tabelas aps a execuo da classe TesteEnvers2
Site oficial do Hibernate Envers.
Por fim, a tabela REVINFO (vide linha 11) recebeu a incluso http://hibernate.org/orm/envers/
de um registro que contm o identificador da nova reviso e o Site oficial do MySQL.
momento exato em que ela ocorreu. http://www.mysql.com/
A Figura 10 mostra a viso das tabelas do banco aps a execuo
Endereo para download do driver do MySQL.
da classe TesteEnvers2.
http://dev.mysql.com/downloads/connector/j/
O Hibernate Envers um poderosssimo recurso para realizao
de auditoria que torna desnecessrio, por exemplo, criar uma Site oficial do Eclipse.
estrutura especfica para as auditorias diretamente no SGBD, https://eclipse.org/downloads/
como Stored Procedures ou triggers, recursos muito utilizados Endereo para download do JDK.
nos sistemas atuais. http://www.oracle.com/technetwork/java/javase/downloads
Diante disso, no desenvolvimento de projetos que requerem
auditoria, certamente o desenvolvedor poder incorporar o Envers Endereo para download do Maven.
http://maven.apache.org/download.html
para o tratamento deste requisito, minimizando os impactos por
ser um framework transparente, baseado em revises e, principal- Christian Bauer, Gavin King. Livro Java Persistence com Hibernate, 2005.
mente, por no ser intrusivo.

34 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia


Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 35