Implementando Auditoria com o Hibernate Envers

25 respostas
hibernate
guilhermebhte

Fui tentar implementar a auditoria e vi este projeto hibernate-envers.

Mas ao tentar criar o projeto ele dá este erro:

12:16:45,518 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 50) MSC000001: Failed to start service jboss.persistenceunit."nfse-web.war#netsoft": org.jboss.msc.service.StartException in service jboss.persistenceunit."nfse-web.war#netsoft": java.lang.AbstractMethodError
	at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:172) [wildfly-jpa-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:117) [wildfly-jpa-8.2.1.Final.jar:8.2.1.Final]
	at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.8.0_131]
	at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:474)
	at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:182) [wildfly-jpa-8.2.1.Final.jar:8.2.1.Final]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_131]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_131]
	at java.lang.Thread.run(Thread.java:748) [rt.jar:1.8.0_131]
	at org.jboss.threads.JBossThread.run(JBossThread.java:122)
Caused by: java.lang.AbstractMethodError
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:312) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:845) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
	at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:398) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:844) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
	at org.jboss.as.jpa.hibernate4.TwoPhaseBootstrapImpl.build(TwoPhaseBootstrapImpl.java:44) [jipijapa-hibernate4-3-1.0.1.Final.jar:]
	at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:154) [wildfly-jpa-8.2.1.Final.jar:8.2.1.Final]
	... 8 more

12:16:45,518 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("deploy") failed - address: ([("deployment" => "nfse-web.war")]) - failure description: {"JBAS014671: Failed services" => {"jboss.persistenceunit.\"nfse-web.war#netsoft\"" => "org.jboss.msc.service.StartException in service jboss.persistenceunit.\"nfse-web.war#netsoft\": java.lang.AbstractMethodError
    Caused by: java.lang.AbstractMethodError"}}
12:16:45,549 WARN  [org.jboss.as.jpa] (Controller Boot Thread) JBAS011411: Unexpected problem gathering statistics: java.lang.IllegalStateException: JBAS011477: Persistence unit 'nfse-web.war#netsoft' is not available
	at org.jboss.as.jpa.management.EntityManagerFactoryLookup.entityManagerFactory(EntityManagerFactoryLookup.java:44)
	at org.jboss.as.jpa.hibernate4.management.HibernateEntityCacheStatistics.getDynamicChildrenNames(HibernateEntityCacheStatistics.java:72)
	at org.jboss.as.jpa.management.DynamicManagementStatisticsResource.getChildren(DynamicManagementStatisticsResource.java:204)
	at org.jboss.as.controller.registry.AbstractModelResource$DelegateResource.getChildren(AbstractModelResource.java:254) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:252) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:239) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:225) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:254) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:239) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:225) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:254) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:239) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:225) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:254) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:239) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:225) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.registry.Resource$Tools.readModel(Resource.java:213) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.ModelControllerImpl.writeModel(ModelControllerImpl.java:580) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.OperationContextImpl.createPersistenceResource(OperationContextImpl.java:229) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.doCompleteStep(AbstractOperationContext.java:543) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.completeStepInternal(AbstractOperationContext.java:298) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.finishStep(AbstractOperationContext.java:752) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.executeStep(AbstractOperationContext.java:727) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.doCompleteStep(AbstractOperationContext.java:501) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.completeStepInternal(AbstractOperationContext.java:298) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractOperationContext.executeOperation(AbstractOperationContext.java:293) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.ModelControllerImpl.boot(ModelControllerImpl.java:346) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractControllerService.boot(AbstractControllerService.java:297) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.server.ServerService.boot(ServerService.java:356) [wildfly-server-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.server.ServerService.boot(ServerService.java:331) [wildfly-server-8.2.1.Final.jar:8.2.1.Final]
	at org.jboss.as.controller.AbstractControllerService$1.run(AbstractControllerService.java:259) [wildfly-controller-8.2.1.Final.jar:8.2.1.Final]
	at java.lang.Thread.run(Thread.java:748) [rt.jar:1.8.0_131]

Classe modelo:

@Entity
@Table(name = "TB_REGIME_TRIBUTACAO", schema = "CONFIGURACOES", uniqueConstraints = {@UniqueConstraint(columnNames = {"NR_CODIGO"}, name = "U_TB_REGIME_TRIBUTACAO_1")}, indexes = {
		@Index(columnList = "ST_REGISTRO", name = "I1_TB_REGIME_TRIBUTACAO"),
		@Index(columnList = "NR_CODIGO", name = "I2_TB_REGIME_TRIBUTACAO"),
		@Index(columnList = "DS_DESCRICAO", name = "I2_TB_REGIME_TRIBUTACAO")})
@Audited
@AuditTable(schema = "CONFIGURACOES", value = "TB_REGIME_TRIBUTACAO_AUD")
@ExclusaoLogica
public class RegimeTributacaoEntity extends NFSEEntity {

	private static final long serialVersionUID = -8437445797289766466L;

	private Integer codigo;
	private String descricao;

	@Override
	@Id
	@Column(name = "PK_REGIME_TRIBUTACAO")
	@SequenceGenerator(name = "CONFIGURACOES.SQ_REGIME_TRIBUTACAO", sequenceName = "CONFIGURACOES.SQ_REGIME_TRIBUTACAO", allocationSize = 1)
	@GeneratedValue(generator = "CONFIGURACOES.SQ_REGIME_TRIBUTACAO", strategy = GenerationType.SEQUENCE)
	public Long getId() {
		return super.getId();
	}

	@Column(name = "NR_CODIGO", length = 11, nullable = false)
	public Integer getCodigo() {
		return codigo;
	}

	public void setCodigo(Integer codigo) {
		this.codigo = codigo;
	}

	@Column(name = "DS_DESCRICAO", length = 50, nullable = false)
	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	@Transient
	public String codigoDescricao() {
		return this.codigo.toString().concat(Constantes.TRACO)
				.concat(this.descricao);
	}
}

Meu pom tem:

<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-envers</artifactId>
			<version>5.2.14.Final</version>
		</dependency>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
	xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="netsoft" transaction-type="RESOURCE_LOCAL">

		<provider>org.hibernate.ejb.HibernatePersistence</provider>

		<non-jta-data-source>java:/prefeitura-ds</non-jta-data-source>
<class>br.eti.netsoft.nfse.notaFiscal.pagamento.RegimeTributacaoEntity</class>
<properties>
			<property name="hibernate.hbm2ddl.auto" value="create" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="false" />
			<property name="show_sql" value="true" />

			<!-- Configuração do Hibernate Envers -->
			<property name="post-insert"
				value="org.hibernate.ejb.event.EJB3PostInsertEventListener, org.hibernate.envers.event.EnversListener" />
			<property name="post-update"
				value="org.hibernate.ejb.event.EJB3PostUpdateEventListener, org.hibernate.envers.event.EnversListener" />
			<!-- <property name="post-delete" value="org.hibernate.ejb.event.EJB3PostUpdateEventListener, 
				org.hibernate.envers.event.EnversListener" /> -->
		</properties>
	</persistence-unit>
</persistence>

Sem as informações sobre o Hibernate Envers, o projeto sobe normalmente e cria as tabelas no banco de dados.

25 Respostas

guilhermebhte

Alguém ?

javaflex

Auditoria com hibernate deixa furo, usa trigger.

guilhermebhte

Teria que criar uma trigger por tabela ?

Tem exemplo ?

E

Aqui na empresa que trabalho, usamos trigger.
Caso queira algum exemplo:

guilhermebhte

Obrigado vou analisar

javaflex

Sim, trigger é ligada a cada tabela. Você pode fazer um gerador de código que gere os scripts do que for necessário.

Você pode ter uma tabela de auditoria correspondente a cada tabela ou ter somente duas tabelas, uma de cabeçalho pra identificar o nome da tabela, ação, usuário, ip, data/hora, etc, e outra pra identificar o nome do campo, valor anterior e novo. Essa parte fica a seu critério ou de arcordo com os requisitos. Aqui os ADs trabalham com essas duas tabelas, fica mais dinâmico.

Pra passar um exemplo precisa informar qual banco, ou pesquise sobre auditoria com trigger.

guilhermebhte

Usamos postgres.

javaflex

http://www.postgresqltutorial.com/creating-first-trigger-postgresql/

guilhermebhte

Valeu.

Mas uma dúvida, porque o hibernate deixa furo ?

javaflex

Nada impede de voce usar. Mas não é profissional qualquer solução de auditoria que se restrinja a própria aplicação.

guilhermebhte

Isto que não entendi, o furo que você disse.

Pelo que vi este projeto do hibernate já faz automático para as classes que desejar.

Eu só não consegui implementar pelo erro que informei.

kicolobo

há uma pequena limitação no caso de triggers: pegar o usuário que realizou a ação pode ser mais difícil, pois o usuário do banco de dados não é o mesmo que o da aplicação.

Neste caso, o Envers cai como uma luva, por que você pode configurar como será a tabela de revisão e nela incluir esta e outras informações, que são relativas à negócio.

guilhermebhte

Concordo @kicolobo

Mas o que pode ser meu erro ?

javaflex

Isso se faz simplesmente com variável de contexto do banco. Aqui trabalhamos com Oracle e gravamos o usuário da aplicação quando está setado, caso contrário pode ter sido alguem externo. No postgresql também tem como setar variável pra fazer essa ponte dentro do contexto da sessão.

Essa solução do hibernate seria bem amadora, principalmente para grandes empresas. Não pegaria principalmente alterações indevidas fora da aplicação.

Concordo que é uma solução pra “ganhar tempo”, mas isso é outra história, como falei antes, nada impede de ser usado.

guilhermebhte

Usando a trigger, tenho que criar classes e mapear elas, conforme as classes comuns ?

javaflex

Não sei se entendi sua dúvida. Trigger fica diretamente ligado a tabela no banco, na fonte da informação. Não precisa de estrutura de dados ou classes na aplicação.

javaflex

Para mais segurança é importante também que o usuário de banco da aplicação não tenha acesso a tabela de auditoria.

D

Como seria feito isso em uma aplicação Desktop, utilizando conexão local?
Você tem um exemplo da utilização de Triggers gravando o usuário da aplicação Java?

javaflex

Depende de qual banco e se o mesmo suporta. Em Oracle por exemplo, na aplicação Java você executa este script:

begin
  dbms_application_info.set_client_info('passe aqui o identificador do usuário logado');
end;

E na trigger usa SYS_CONTEXT para retornar a informação que identifica o usuário ou qualquer outra informação:

select sys_context('USERENV', 'CLIENT_INFO') from dual;

Pesquise o equivalente para o seu banco.

Exemplo de trigger encontra fácil se pesquisar, mas já postei um link de exemplo neste mesmo tópico.

guilhermebhte

Para eu fazer uma tela de consulta em banco de dados utilizo as classes com @Entity e esta está ligada a tabela de banco de dados conforme descrito em @Table.

Para auditoria por trigger implementando em outra tabela, teria que mapear a tabela aud do mesmo jeito né ?

Exemplo:

Tabela comum
@Table(name = “TB_REGIME_TRIBUTACAO”, schema = “CONFIGURACOES”)

Tabela Auditoria
@Table(name = “TB_REGIME_TRIBUTACAO_AUD”, schema = “AUDITORIAS”)

javaflex

Depende do caso. Só mapeia se sua aplicação tiver tela pro usuário consultar a tabela de auditoria. Tem que ter uma necessidade. Aqui é só em caso de investigação e nem temos acesso.

guilhermebhte

Sim.

Obrigado

javaflex

Consulta é independente da solução ser por trigger ou outra coisa.

guilhermebhte

Eu entendi.

Posso mapear as AUDITORIAS, com métodos somente GET, assim Somente com acesso a usuários administradores.

Por isto que acho que o hibernate-envers é melhor, porque ele não precisa mapear e já tem classes próprias para tais consultas.

O problema é que não consegui configurar.

javaflex

Melhor é relativo, verdade que é uma solução economica, mas por outro lado deixa brechas. Fica a critério de quem é responsável pelos dados da empresa, só te passei a solução mais profissional.

No sistema que trabalho são somente duas tabelas pra registrar a trilha de auditoria de todas as tabelas que necessitam de auditoria, como tinha explicado em alguma msg acima. Fazem a consulta com um único select. Se alguem fosse mapear isso seria uma classe só pra esse resultset.

Sobre o problema que está passando com hibernate, tente outros exemplos na internet, nao fique preso em um exemplo que nao funciona.

Criado 2 de setembro de 2018
Ultima resposta 7 de set. de 2018
Respostas 25
Participantes 5