Buscar

Auditoria Simples com Hibernate Envers

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 43 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 43 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 43 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Para melhorar a experiência das pessoas que acessam o InfoQ Brasil, nós criamos uma
série de funcionalidades que te permitem ficar por dentro das últimas tendências e das
novidades de seu interesse, sem que você seja incomodado por coisas irrelevantes. Receba
e-mails periódicos e notificações sobre seus tópicos favoritos!
Independentemente do tamanho da aplicação, a necessidade de monitorar as ações
realizadas por um usuário frente às diversas funcionalidades existentes são reais. Em um
sistema de uma instituição financeira, por exemplo, existe inclusive uma motivação legal para
que sejam registradas todas as alterações realizadas nos saldos das contas para uma
eventual análise posterior.
Links
patrocinados
Auditoria Simples com Hibernate
Envers
 por em 04 abr 2017. Tempo estimado de leitura: 25 minutos 1 Dê sua
opinião
Carlos Alberto Silva 
Resguardar a integridade das informações administradas por um sistema geralmente é uma
exigência da área de Segurança da Informação da corporação que é responsável por
especificar, por exemplo, quais ações serão rastreadas, quais dados serão armazenados, por
quanto tempo, quem poderá consultá-los etc. Essa demanda, por sua vez, é repassada à
área de Tecnologia da Informação (TI) que geralmente é a detentora das informações e a
responsável por proteger e garantir a consistência dos dados que na maioria das vezes,
ficam internalizados em banco de dados relacionais. Dessa forma, cabe a equipe de TI
decidir qual o mecanismo técnico e procedimento capaz de gerar um registro de histórico das
ações executadas.
Existem algumas técnicas clássicas que são empregadas, como por exemplo, utilizar-se de
recursos de banco, entre eles Stored Procedure e Triggers. O problema desse tipo de
abordagem é o alto acoplamento existente entre a aplicação e o banco de dados.
Outra forma é através de código feito pela equipe de desenvolvimento que, para manter
todas alterações realizadas nas tabelas auditadas, cria uma tabela exclusiva de histórico,
além da tabela monitorada que mantém o estado vigente dos dados. Porém, este expediente
requer ajustes em todo o código que lida com o banco, necessitando que a internalização
ocorra primeiro para salvar os dados na tabela de histórico e depois para realizar a
persistência na tabela auditada.
Hoje, a grande maioria dos projetos Java lançam mão de frameworks ORM (mapeamento
objeto relacional) para a camada de persistência. Certamente um dos mais utilizados pela
comunidade Java é o Hibernate, que permite a diminuição da complexidade existente na
construção de aplicações que trabalham com banco de dados relacional. Em cima desse
cenário, e diante da necessidade de rastrear as ações realizadas sobre as entidades de uma
aplicação, surgiu o subprojeto Envers.
O Hibernate Envers oferece a organização do histórico das versões dos dados gerenciados
pela aplicação, através das entidades mapeadas para a persistência JPA para auditar as
modificações ocorridas em um dado registro. Dessa forma, com sua utilização, uma
aplicação é capaz de gerir todas as modificações realizadas no seu banco de dados de
forma fácil e não intrusiva.
Um processo de auditoria bem modelado deve fornecer algumas informações básicas sobre
as operações realizadas sobre um sistema, como: quem consultou informações, quais
informações foram essas, quem excluiu, o que foi excluído, o que foi editado, como estavam
antes e como ficaram depois; quando foi feito, etc. De posse dessa fotografia e caso algum
dado seja inserido ou alterado de forma equivocada, o analista terá modos para recuperar a
versão antiga dos dados e inferir quem foi o autor daquela ação.
Neste artigo serão apresentadas algumas das principais características do Hibernate Envers,
detalhando-as e mostrando na prática o controle feito em uma aplicação simples, completa e
integrada com JSF, e que tem como um dos seus requisitos a necessidade de rastrear as
operações efetuadas pelos seus usuários. As tecnologias utilizadas, além dos frameworks já
mencionados, serão a linguagem Java, o Eclipse, o Apache Maven, o MySQL, o Tomcat e o
Hibernate que viabilizará o contato entre a aplicação e o banco de dados.
Conhecento o Hibernate Envers
O Hibernate Envers é uma biblioteca que permite auditar classes persistentes através do
controle de versões em mapeamentos objetos relacionais feitos através do Hibernate.
Para cada entidade mapeada auditada, uma tabela versionada é criada no banco de dados,
contendo todo o histórico das alterações efetuadas sobre aquela entidade. Basicamente,
cada transação realizada no banco é classificada como uma revisão (ao menos que essa
transação não realize nenhuma modificação), sendo que cada nova revisão gera alimentação
automática das tabelas que permitem o versionamento das classes persistentes. Em suma,
cada vez que uma tabela sinalizada para auditoria sofre alterações em seus dados
(registros), uma nova "versão" dela é gerada pelo Envers e armazenada em uma outra
tabela, que contém como chave primária o atributo de revisão, além do tipo de operação e de
todos os campos auditáveis da tabela alvo. Pode-se comparar o Envers a sistemas de
controle de versão, como Subversion, CVS, Git que mantêm revisões globais para todas as
mudanças ocorridas.
Dessa forma, o analista pode recuperar e consultar dados históricos sem muito esforço,
sendo possível, por exemplo, verificar quais informações foram alteradas naquela revisão, o
dia em que aquela alteração ocorreu, em qual momento, quem realizou tal mudança e até
mesmo registrar outras informações que julgar imprescindíveis. Em cima disso, é possível
identificar comportamentos indevidos da aplicação por parte dos seus usuários e até mesmo
recuperar dados que não deveriam ter sofridos alterações.
Como vantagens da utilização desse framework pode-se citarr: permite auditar todas as
entidades mapeadas pelo Hibernate; baseia-se em revisões; independe do fabricante para o
banco de dados; agrega valor ao produto; reduz o custo de manutenção e aumenta a
produtividade.
Outra vantagem do Envers (Easy Entity Versioning) é a sua fácil implementação, uma vez
que sua utilização se resume a incluir no código da classe mapeada a anotação @Audited e
inserir algumas poucas classes de listener na configuração do projeto. Feito isso, a aplicação
está pronta para registrar as alterações ocorridas através da tabela de históricos referente a
entidade que será criada automaticamente no banco de dados. Caso algum atributo da
entidade não necessite ser auditado, pode-se lançar mão de outra anotação que é a
@NotAudited.
Para que o Envers trabalhe de forma adequada, todas as entidades que passarão por
processo de auditoria devem ter chaves primárias (identificadores exclusivos) imutáveis.
Configuração do Ambiente de Desenvolvimento
Após abordado o contexto do artigo e mencionado os principais objetivos deste trabalho,
será dada ênfase a parte prática, com o desenvolvimento de uma aplicação web que
armazenará além das informações default do framework, outras, como o usuário que fez a
mudança assim como o IP de sua máquina.
O primeiro passo nesse processo é ter o ambiente de desenvolvimento corretamente
configurado. O utilizado neste material foi o Eclipse Luna Java EE, por ser open-source,
propiciar maior produtividade durante a construção e por ser amplamente utilizado pela
comunidade Java. Trabalhando com o Eclipse, de forma integrada, será empregado o
Apache Maven 3.1.1, que oferece maior simplicidade no gerenciamento do projeto e das
bibliotecas.
Para a persistência dos dados será utilizado o SGBD (Sistema Gerenciador de Banco de
dados) MySQL 6.3. Além disso, é necessário ter acesso, de forma local ou remota, a um
servidor web que implemente as bibliotecas do Java EE para execução da aplicação.
Desenvolvendo o cadastro de alunos
O objetivo deste sistema será gerenciar o cadastro de alunos de umainstituição educacional
e para isso será construído um formulário simples para internalizar as informações na base
de dados.
Como o intuito maior desse artigo é abordar o Hibernate Envers, por questão de
simplificação, o sistema conterá apenas duas entidades de domínio, chamadas Aluno e
Usuário que não possuem relacionamento, e a partir daí serão construídas as ações do
CRUD (Create-Read-Update-Delete).
As propriedades de um aluno serão: código, nome, matricula, CPF, email e cidade. Já o
usuário possuirá nome e senha.
A Figura 1 mostra a representação gráfica da entidade de domínio do sistema.
Figura 1. Representação gráfica da tabela Aluno.
Criando a aplicação
Para iniciar a construção, crie um novo projeto Java, a partir do Maven no IDE e coloque as
informações de identificação do projeto. Os campos Group ID, Artifact ID (nome do projeto),
Packaging e Version devem ser preenchidos, respectivamente, com os seguintes valores:
br.com.infoq, hibernate-envers-web, war e 0.0.1-SNAPSHOT.
Feito isso, tem-se o projeto criado. Porém, ainda é necessário atualizar o projeto para que
sua estrutura seja construída e as configurações do Maven definidas. Portanto, siga até o
menu Maven > Update Project, selecione o projeto recém-criado e clique em OK.
Adicionando as dependências necessárias
Com a estrutura do projeto Maven pronta, o próximo passo é inserir as bibliotecas que serão
empregadas no projeto. Isso é feito diretamente configurando o arquivo pom.xml, local onde
está contida a configuração principal do Maven, através da adição de dependências. As
seguintes serão adicionadas:
Hibernate Core (5.2.6);
Hibernate Envers (5.2.6);
Driver do MySQL (5.1.34);
JSF API (2.2.13);
JSF IMPL (2.2.13);
Java EE API (7.0);
Javax Servlet API (3.1.0).
Após adicionadas as dependências, é necessário atualizar o projeto para que as bibliotecas
sejam baixadas e incorporadas ao Build Path do projeto. Dessa forma, clique em Maven >
Update Project.
A Listagem 1 mostra trecho do arquivo pom.xml contendo todas as dependências
adicionadas.
Listagem 1. Configuração do arquivo pom.xml.
<dependencies> 
 <dependency> 
 <groupId>mysql</groupId> 
 <artifactId>mysql-connector-java</artifactId> 
 <version>5.1.34</version> 
 </dependency> 
 <dependency> 
 <groupId>org.hibernate</groupId> 
 <artifactId>hibernate-core</artifactId> 
 <version>5.2.6.Final</version> 
 </dependency> 
 <dependency> 
 <groupId>com.sun.faces</groupId> 
 <artifactId>jsf-api</artifactId> 
 <version>2.2.13</version> 
 </dependency> 
 <dependency> 
 <groupId>org.hibernate</groupId> 
 <artifactId>hibernate-envers</artifactId> 
 <version>5.2.6.Final</version> 
 </dependency> 
 <dependency> 
 <groupId>com.sun.faces</groupId> 
 <artifactId>jsf-impl</artifactId> 
 <version>2.2.13</version> 
 </dependency> 
 <dependency> 
 <groupId>javax.servlet</groupId> 
 <artifactId>javax.servlet-api</artifactId> 
 <version>3.1.0</version> 
 <scope>provided</scope> 
 </dependency> 
 <dependency> 
 <groupId>javax</groupId> 
 <artifactId>javaee-api</artifactId> 
 <version>7.0</version> 
 </dependency> 
 </dependencies> 
Criando, mapeando e auditando
Através de anotações no código, a partir de agora, será criado e definido o mapeamento
objeto/relacional da entidade, e também sinalizado ao Envers qual entidade sofrerá auditoria.
Na organização do projeto, cada tipo de classe será colocado em um pacote diferente.
Sendo assim, a entidade Aluno ficará no pacote br.com.infoq.entidade, devendo o mesmo ser
criado. O código da classe Aluno é apresentado na Listagem 2.
Listagem 2. Código da classe Aluno.
package br.com.infoq.entidade; 
//imports omitidos 
@Audited 
@Table 
@AuditTable(value="aluno_auditoria") 
@Entity 
public class Aluno implements Serializable { 
 private static final long serialVersionUID = 1L; 
 
 public Aluno(){} 
 
 @Id 
 @Column 
 @GeneratedValue(strategy = GenerationType.AUTO) 
 private int codigo; 
 @Column 
 public String nome; 
 
 @Column 
 public int matricula; 
 
 @Column 
 public String cpf; 
 
 @Column 
 public String cidade; 
 
 @Column 
 public String email; 
 
 // construtor e métodos gets e sets omitidos 
} 
A anotação principal referente ao Hibernate Envers e que indica que a entidade e suas
propriedades passarão por auditoria é a @Audited e pode ser vista na linha 5. Outra
anotação da API do Envers aparece na linha 7, @AuditTable, que permite customizar o nome
da tabela que será criada para armazenar todas as alterações sofridas pela entidade
auditada. Quando não se utiliza essa marcação, o framework utiliza a nomenclatura padrão
que consiste do nome da entidade auditada com o sufixo _AUD. As demais anotações são
referentes ao Hibernate Core.
Outra classe do domínio da aplicação é a Usuario que será criada dentro do mesmo pacote.
Por questão de simplicidade, essa classe não será persistida, nem mesmo enfrentará o
processo de auditoria. O motivo da sua criação é para permitir identificar o usuário que estará
logado na aplicação e assim poder associar o identificador do usuário as alterações feitas na
aplicação. O código da classe Usuário pode ser visualizado na Listagem 3.
Listagem 3. Código da classe Usuário.
package br.com.jm.entidade; 
public class Usuario { 
 
 private String user; 
 private String pwd; 
 
 // construtor e métodos gets e sets omitidos 
} 
Criando o Controller da aplicação
A camada de controle é composta por elementos (Managed Beans) cuja função é controlar o
fluxo de processamento e estabelecer a ligação entre a camada de visão e o modelo.
Os Managed Beans serão criados dentro do pacote br.com.infoq.mb. Uma das classes desse
pacote é a UsuarioBean que tem como função controlar as ações do login e logoff da
aplicação. Seu código pode aparece na Listagem 4.
Listagem 4. Código da classe UsuarioBean.
package br.com.infoq.mb; 
//imports omitidos 
 
@ManagedBean 
@SessionScoped 
public class UsuarioMB implements Serializable { 
 
 private static final long serialVersionUID = 1L; 
 
 private Usuario usuario = new Usuario(); 
 
 public String validateUsernamePassword() { 
 String login = "matheus"; 
 String senha = "1234"; 
 
 if (login.equals(usuario.getUser()) && 
senha.equals(usuario.getPwd())) { 
 HttpSession session = SessionBean.getSession(); 
 session.setAttribute("username", usuario.getUser()); 
 return "menu"; 
 } else { 
 return "login"; 
 } 
 } 
 
 public String logout() { 
 HttpSession session = SessionBean.getSession(); 
 session.invalidate(); 
 return "login"; 
 } 
 //métodos gets e sets omitidos 
} 
Na linha 13, o método validateUsernamePassword, verifica se os dados de acesso a
aplicação foram inseridos corretamente e, caso isso se confirme, cria uma sessão, através do
método estático getSession, da classe SessionBean, definindo uma variável como sendo o
nome do usuário logado. Por questão de simplificação, foram definidos dentro da classe, nas
linhas 14 e 15, o usuário e a senha da aplicação. Caso os dados de acesso sejam inseridos
adequadamente o usuário será direcionado para o objeto view correto.
A Listagem 5 mostra a implementação da classe utilitária SessionBean, utilizada pelo bean.
Listagem 5. Código da classe SessionBean.
package br.com.infoq.util; 
//imports omitidos 
 
public class SessionBean { 
 
 public static HttpSession getSession() { 
 return (HttpSession) FacesContext.getCurrentInstance() 
 .getExternalContext().getSession(false); 
 } 
 
 public static String getUserName() { 
 HttpSession session= (HttpSession) 
FacesContext.getCurrentInstance() 
 .getExternalContext().getSession(false); 
 return session.getAttribute("username").toString(); 
 } 
} 
Outro controlador chama-se AlunoBean que tem como função controlar o módulo do sistema
que cadastra os alunos, direcionando o fluxo para as várias operações disponíveis. O código
desta classe é o visto na Listagem 6.
Listagem 6. Código da classe AlunoBean.
package br.com.jm.mb; 
//imports omitidos 
@ManagedBean 
@SessionScoped 
public class AlunoMB implements Serializable { 
 private static final long serialVersionUID = 1L; 
 
 private Aluno aluno = new Aluno(); 
 private List<Aluno> lista = new ArrayList<Aluno>(); 
 
 public void buscar(){ 
 AlunoDAO alunoDAO = new AlunoDAO(); 
 aluno = 
alunoDAO.getAlunoByMatricula(aluno.getMatricula()); 
 } 
 
 public String salvar() { 
 AlunoDAO alunoDAO = new AlunoDAO(); 
 alunoDAO.adicionarAluno(aluno); 
 return "sucesso"; 
 } 
 
 public String editar(){ 
 AlunoDAO alunoDAO = new AlunoDAO(); 
 alunoDAO.atualizarAluno(aluno); 
 return "sucesso_edi"; 
 } 
 
 public String remover() { 
 AlunoDAO alunoDAO = new AlunoDAO(); 
 alunoDAO.apagarAluno(aluno.getMatricula()); 
 return "sucesso_del"; 
 } 
 //métodos gets e sets omitidos. 
} 
Criando a camada view
O próximo passo será criar as páginas web por onde as informações do cadastro serão
manipuladas, assim como uma tela de autenticação do usuário, que será a primeira criada.
Trata-se de um formulário simples que se comunica com o bean gerenciado UsuarioMB e
que recebe os dados de acesso do usuário. A página deve ficar com o código semelhante ao
apresentado na Listagem 7.
Listagem 7. Formulário de login.
<h:body> 
 <h:form> 
 <h3>Informe os dados de acesso:</h3> 
 <h:outputText value="Username:" /> 
 <h:inputText id="username" value="#{usuarioMB.usuario.user}">
</h:inputText> 
 <h:message for="username"></h:message> 
 <br /><br /> 
 <h:outputText value="Password:" /> 
 <h:inputSecret id="password" value="#{usuarioMB.usuario.pwd}">
</h:inputSecret> 
 <h:message for="password"></h:message> 
 <br /><br /> 
<h:commandButton action="#{usuarioMB.validateUsernamePassword}" 
value="Login" /> 
 <h:commandButton value="Limpar" action="#{usuarioMB.limpar}" /> 
 </h:form> 
</h:body> 
A tela seguinte é o menu principal da aplicação e contém uma lista com todas as
funcionalidades disponibilizadas pela aplicação. Esse arquivo é chamado de menu.xhtml. Um
trecho do código pode ser visto através da Listagem 8.
Listagem 8. Menu Principal.
 <h3>Bem vindo ao AcadêmicoNET</h3> 
 <ul id="nav"> 
 <li><h4>Aluno</h4> 
 <ul> 
 <li><a href="cadastro_aluno.xhtml">Cadastrar</a></li> 
 <li><a href="remover_aluno.xhtml">Remover</a></li> 
 <li><a href="listar_aluno.xhtml">Listar</a></li> 
 <li><a href="editar_aluno.xhtml">Editar</a></li> 
 </ul> 
 </li> 
 </ul> 
O formulário de cadastro aparece no arquivo cadastro_aluno.xhtml e está representado
através da Listagem 9.
Listagem 9. Formulário de cadastro de aluno.
 <h:body> 
 <h2>Preencha o formulário abaixo</h2> 
 <h:form id="frmAluno" method="post"> 
 <h:panelGrid columns="2" style="horizontal-
align:center"> 
 <h:outputText value="Nome:" /> 
 <h:inputText value="#
{alunoMB.aluno.nome}" /> 
 <h:outputText value="Matrícula:" 
/> 
 <h:inputText value="#
{alunoMB.aluno.matricula}" /> 
 <h:outputText value="CPF:" /> 
 <h:inputText value="#{alunoMB.aluno.cpf}" 
/> 
 <h:outputText value="Email:" /> 
 <h:inputText value="#
{alunoMB.aluno.email}" /> 
 <h:outputText value="Cidade:" /> 
 <h:inputText value="#
{alunoMB.aluno.cidade}"/> 
 </h:panelGrid> 
 <h:commandButton action="#{alunoMB.salvar}" 
value="Enviar" /> 
 <h:commandButton action="#{alunoMB.limpar}" 
value="Limpar" /> 
 <input type='button' 
onclick='javascript:history.back()' value='Voltar' name='Voltar'/> 
 </h:form> 
</h:body> 
Configurando a aplicação
Após a implementação das camadas view, model e controller, o passo agora é informar ao
Hibernate todas as informações do banco de dados ao qual ele se conectará. Essa
configuração ficará acessível no arquivo hibernate.cfg.xml que deverá ser criado dentro da
pasta src/main/resources. Seu conteúdo pode ser visto através da Listagem 10.
Listagem 10. Conteúdo do arquivo hibernate.cfg.xml.
<hibernate-configuration> 
 <session-factory> 
 <property 
name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 
 <property 
name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> 
 <property 
name="hibernate.connection.url">jdbc:mysql://localhost:3306/escolabd?
zeroDateTimeBehavior=convertToNull</property> 
 <property name="hibernate.connection.username">root</property> 
 <property name="hibernate.connection.password">1234</property> 
 <property name="hibernate.show_sql">true</property> 
 <property name="hibernate.hbm2ddl.auto">create</property> 
 
 <mapping class="br.com.jm.entidade.Aluno"/> 
 </session-factory> 
 </hibernate-configuration> 
Em versões anteriores do Hibernate Envers, neste ponto, era preciso definir alguns listeners
que permitiam ao Envers controlar o processo de auditoria, inserindo registros nas tabelas
versionadas de acordo com a conduta do usuário. No entanto, a partir da versão 5, essa
configuração não é mais necessária, sendo realizada de forma automática.
Criando a conexão com o banco
Após criado o arquivo de configuração, nesse ponto será implementada a classe utilitária
HibernateUtil que fará a ligação entre o hibernate.cfg.xml e o banco de dados,
disponibilizando uma instância de SessionFactory para a aplicação. Essa classe ficará dentro
do pacote br.com.infoq.util e seu código pode ser visto na Listagem 11.
Listagem 11. Código da classe HibernateUtil.
package br.com.infoq.util; 
//imports omitidos 
public class HibernateUtil { 
 private static SessionFactory sessionFactory; 
 public static SessionFactory getSessionFactory() { 
 if (sessionFactory == null) { 
 Configuration configuration = new Configuration().configure(); 
 ServiceRegistry serviceRegistry = new 
StandardServiceRegistryBuilder() 
 
.applySettings(configuration.getProperties()).build(); 
 
 sessionFactory = 
configuration.buildSessionFactory(serviceRegistry); 
 SchemaUpdate se = new SchemaUpdate(configuration); 
 se.execute(true, true); 
 } 
 return sessionFactory; 
 } 
} 
Persistindo a entidade Aluno
Agora que as classes mapeadas com as anotações de auditoria, o arquivo de configuração e
a conexão com o banco foram implementadas, o passo agora é viabilizar as operações de
persistência. O pacote em questão é o br.com.infoq.acessobd e a classe é a AlunoDAO,
tendo seu código mostrado através da Listagem 12.
Listagem 12. Código da classe AlunoDAO.
package br.com.infoq.acessobd; 
 //imports omitidos 
 public class AlunoDAO { 
 private static SessionFactory factory; 
 public AlunoDAO() { 
 factory = HibernateUtil.getSessionFactory(); 
 } 
 public Integer adicionarAluno(Aluno aluno) { 
 Session session = factory.openSession(); 
 Transaction tx = null; 
 Integer cod_aluno = null; 
 try { 
 tx = session.beginTransaction(); 
 cod_aluno = (Integer) session.save(aluno); 
 tx.commit(); 
 } 
 catch (HibernateException e) { 
 if (tx != null) { 
 tx.rollback(); 
 } 
 e.printStackTrace(); 
 } finally { 
 session.close();} 
 return cod_aluno; 
 } 
 public List<Aluno> listarAlunos() { 
 Session session = factory.openSession(); 
 Transaction tx = null; 
 List<Aluno> alunos = null; 
 try { 
 tx = session.beginTransaction(); 
 alunos = 
session.createCriteria(Aluno.class).list(); 
 tx.commit(); 
 } catch (HibernateException e) { 
 if (tx != null) { 
 tx.rollback(); 
 } 
 e.printStackTrace(); 
 } finally { 
 session.close(); 
 } return alunos; 
 } 
 public void atualizarAluno(Aluno aluno) { 
 Session session = factory.openSession(); 
 Transaction tx = null; 
 Aluno aluno_upd = new Aluno(); 
 try { 
 tx = session.beginTransaction(); 
 aluno_upd.setCidade(aluno.getCidade()); 
 aluno_upd.setCodigo(aluno.getCodigo()); 
 aluno_upd.setCpf(aluno.getCpf()); 
 aluno_upd.setEmail(aluno.getEmail()); 
 aluno_upd.setMatricula(aluno.getMatricula()); 
 aluno_upd.setNome(aluno.getNome()); 
 session.update(aluno_upd); 
 tx.commit(); 
 } catch (HibernateException e) { 
 if (tx != null) { 
 tx.rollback(); 
 } 
 e.printStackTrace(); 
 } finally { 
 session.close(); 
 } 
 } 
 public void apagarAluno(Integer matricula) { 
 Session session = factory.openSession(); 
 Transaction tx = null; 
 try { 
 tx = session.beginTransaction(); 
 Criteria criteria = 
session.createCriteria(Aluno.class); 
 criteria.add(Restrictions.eq("matricula", 
matricula)); 
 List<Aluno> results = criteria.list(); 
 session.delete((Aluno)results.get(0)); 
 tx.commit(); 
 } catch (HibernateException e) { 
 if (tx != null) { 
 tx.rollback(); 
 } 
 e.printStackTrace(); 
 } finally { 
 session.close(); 
 } 
 } 
 
 public Aluno getAlunoByMatricula(int matricula) { 
 Session session = factory.openSession(); 
 Transaction tx = null; 
 Aluno aluno = null; 
 try { 
 tx = session.beginTransaction(); 
 Criteria criteria = 
session.createCriteria(Aluno.class); 
 criteria.add(Restrictions.eq("matricula", 
matricula)); 
 List<Aluno> results = criteria.list(); 
 aluno = ((Aluno)results.get(0)); 
 tx.commit(); 
 } catch (HibernateException e) { 
 if (tx != null) { 
 tx.rollback(); 
 } 
 e.printStackTrace(); 
 } finally { 
 session.close(); 
 } 
 return aluno; 
 } 
} 
Auditando informações adicionais
Nesse ponto, toda aplicação está configurada e funcional, portanto, pronta para ser
executada. Porém, ainda não atende a um dos requisitos propostos no começo desse artigo
que é a possibilidade de customizar informações, adicionando atributos extras junto à
estrutura de auditoria do Hibernate Envers. Conforme mencionado anteriormente, além das
informações default, duas outras informações devem ser registradas: usuário e o endereço
IP.
Para tal, será utilizado o recurso de escuta de revisão do framework e criada uma classe que
representará a entidade de revisão do projeto, sendo responsável por mapear todos os
dados armazenados no momento da criação de uma nova revisão.
Sendo assim, dentro do pacote br.com.infoq.entidade, crie a classe AuditEntity. A Listagem
13 mostra como foi realizada sua implementação.
Na linha 5 a classe foi mapeada como uma entidade JPA, através da anotation @Entity. Além
disso, é feita a alteração do nome da tabela central de auditoria do Envers, que, por padrão,
leva o nome de "revinfo". Através do atributo "name" foi definido o novo nome como sendo
"revinfo_cust".
A anotação @RevisionEntity, presente na linha 6, indica que essa é uma entidade de revisão
e que será usada para armazenar o histórico das revisões. Nessa mesma linha é feita
menção a classe AuditListener, que vai ser um "interceptor" da classe RevisionListener e
implementará o comportamento customizado que será utilizado no momento da criação da
revisão.
Os novos campos que serão inseridos na tabela de revisão aparecem definidos nas linhas 11
e 12. Os demais campos, que são default do framework, são incorporados através da classe
DefaultRevisionEntity, estendida na linha 7.
Outra classe que deve ser criada é a AuditListener que estende AuditEventListener, como
mostra a Listagem 14.
Listagem 13. Código da classe AuditEntity.
package br.com.infoq.entidade; 
//imports omitidos 
@Entity(name="revinfo_cust") 
@RevisionEntity(AuditListener.class) 
public class AuditEntity extends DefaultRevisionEntity { 
 
 private static final long serialVersionUID = 1L; 
 
 public String usuario; 
 public String ip; 
 //gets e sets omitidos 
} 
Nesta classe, o método newRevision - linha 7 - foi sobrescrito. O mesmo recebe como
parâmetro a entidade que está sendo auditada, que no caso é a Aluno. Esse método é
chamado toda vez que o Envers criar uma nova revisão, assim, pode-se instanciar a classe
AuditEntity e definir os atributos desejados. Nas linhas 9 e 10 informe o usuário que está
realizando a alteração, e o IP de sua máquina. Os atributos que vêm da
DefaultRevisionEntity (id e timestamp), são preenchidos automaticamente pelo Envers.
Listagem 14. Código da classe AuditListener.
package br.com.infoq.util; 
//imports omitidos 
public class AuditListener implements RevisionListener { 
 @Override 
 public void newRevision(Object revisionEntity) { 
 AuditEntity revEntity = (AuditEntity) revisionEntity; 
 revEntity.setUsuario(SessionBean.getUserName()); 
 revEntity.setIp(SessionBean.getIP()); 
 } 
} 
Além da criação dessas duas classes, uma outra configuração deve ser incorporada ao
arquivo hibernate.cfg.xml que representa a identificação da nova entidade criada. Portanto,
adicione o seguinte trecho ao arquivo mencionado.
 <mapping class="br.com.jm.entidade.AuditEntity"/> 
Testando o funcionamento
Enfim, nesse ponto é possível testar a aplicação e ver o Envers trabalhando. Para execução
da aplicação, clique com o botão direito do mouse sobre o projeto, acesse Run As > Run on
Server, escolha o Tomcat como servidor e clique em Finish. Feito isso, a primeira tela que se
aparecerá é a tela de login, conforme mostra a Figura 2. Portanto, informe os dados de
acesso. Caso estejam corretos, uma sessão será criada para o usuário e o mesmo será
direcionado para o menu da aplicação, mostrado pela Figura 3.
Figura 2. Tela de Login
Figura 3. Menu da Aplicação
A primeira ação executada frente ao sistema é o cadastro de um aluno. Após isso, será
possível ver os efeitos do Hibernate Envers no banco de dados. Clique em Cadastrar na tela
em questão e digite as informações do aluno na próxima tela que aparecer. Após clique em
enviar, conforme Figura 4.
Figura 4. Tela de cadastro de aluno.
A Figura 5 mostra o resultado da primeira inserção efetuada no banco.
Figura 5. Visão do Banco de Dados.
Ao realizar o cadastro, além de ser inserido o registro na tabela aluno, duas novas tabelas
foram criadas, sendo elas: aluno_auditoria que contém o histórico (modificação, criação e
remoção) referente a entidade Aluno e a tabela revinfo_cust que indica em que momento foi
criada a revisão e também armazena as informações personalizadas. Após o primeiro
cadastro, veja a visão das duas tabelas, através da Figura 6.
Figura 6. Consulta Banco de Dados.
Na tabela aluno_auditoria, além de todos os atributos auditáveis da tabela aluno, duas novas
colunas aparecem: REV e REVTYPE. A primeira refere-se à identificação da revisão e a
segunda diz respeito ao tipo de operação realizada que pode ser 0, 1 ou 2, indicando,
respectivamente, inserção, edição ou remoção. Veja que nesse caso, o número 0 indica que
foi realizada a inclusão de um registro na tabela auditada.
Já a tabela revinfo_cust possui, por padrão, dois campos: id e timestamp. O primeiro
simboliza a identificação da revisão, que também aparece na tabela aluno_auditoria,
enquantoque o segundo indica o momento em que foi realizada a alteração. As demais
colunas que aparecem nessa tabela foram customizadas.
Nesse ponto mais algumas ações sobre o banco de dados serão realizadas e os efeitos
sobre as tabelas serão vistos. Será cadastrado mais três alunos. Veja o resultado na Figura
7.
Figura 7. Consulta Banco de Dados.
Perceba que os valores da coluna REV na tabela aluno_auditoria mudaram, pois são
revisões diferentes. Lembrando que uma revisão no Envers representa uma transação. Já o
valor da coluna REVTYPE se manteve o mesmo, pois em todos os 4 registros, a operação
realizada sobre eles foi a de inserção.
Aqui será editado um dos alunos cadastrados e o resultado poderá ser visto.
Figura 8. Conteúdo da tabela aluno_auditoria.
A Figura 8 mostra o conteúdo da tabela aluno_auditoria. Foi editado o aluno de código 39301
sendo alterado a sua cidade de Uberlândia para Uberaba. Em virtude dessa ação, foi
adicionado um novo registro nessa tabela. Note que agora na coluna REVTYPE aparece o
valor 1 pois trata-se de uma alteração de registro.
Neste ponto, um registro será deletado. Analisando a consulta da Figura 9 veja que o aluno
de código 2 foi deletado. O valor 2 para o campo REVTYPE indica que houve uma exclusão.
Figura 9. Consulta Banco de Dados.
Consulta e recuperação dos dados de auditoria
O Envers oferece um mecanismo que permite recuperar o histórico de mudanças de uma
entidade através de queries que são semelhantes ao Hibernate Criteria. Isso pode ser feito
através da interface AuditReader, que contém operações de busca, baseada nas entidades
mapeadas.
Por exemplo, a Listagem 15 demostra como recuperar o número máximo de revisões
produzidas pelas alterações feitas no banco. Para se ter acesso as revisões produzidas,
primeiro deve-se criar uma instância de AuditReader, o que pode ser feito através do método
getAuditReader, visível na linha 3.
Listagem 15. Recuperação das revisões.
public int getRevisions(){ 
Number revision = (Number) getAuditReader().createQuery() 
 .forRevisionsOfEntity(Aluno.class, false, true) 
 .setProjection(AuditEntity.revisionNumber().min()) 
 .add(AuditEntity.id().eq(entityId)) 
 .add(AuditEntity.revisionNumber().gt(42)) 
 .getSingleResult(); 
 return revision; 
} 
Conclusão
Uma das principais características oferecidas pelo Hibernate Envers, além de sua
simplicidade, é o ganho em produtividade e a redução de tempo empregado na manutenção.
Enquanto, com outros recursos, perde-se muito tempo e utiliza-se muitas linhas de código,
com esse framework apenas poucas anotações no código precisam ser inseridas.
Além disso, não é necessário ficar preso aos recursos default que a biblioteca oferece
podendo adicionar metadados a uma revisão e customizar as informações, identificando por
exemplo, o usuário que fez determinada alteração, ou indo além, determinando o IP da
máquina do usuário, seu Sistema Operacional ou mesmo outras informações.
Referências
Site oficial do Hibernate Envers.
Artigo originalmente publicado em Oracle Technology Network.
Carlos Alberto Silva (casilvamg@hotmail.com) é Formado em Ciência
da Computação pelo Universidade Federal de Uberlândia (UFU), com
especialização em Desenvolvimento Java pelo Centro Universitário do
Triângulo (UNITRI). e em Análise e Desenvolvimento de Sistemas
Aplicados a Gestão Empresarial no Instituto Federal do Triângulo
Mineiro (IFTM). Trabalha na empresa Algar Telecom como Analista de
Sistemas. Possui as seguintes certificações: OCJP, OCWCD e ITIL.

Outros materiais