Implementando Auditoria com o Hibernate Envers

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.

Alguém ?

Auditoria com hibernate deixa furo, usa trigger.

Teria que criar uma trigger por tabela ?

Tem exemplo ?

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

Obrigado vou analisar

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.

Usamos postgres.

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

Valeu.

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

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

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.

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.

Concordo @kicolobo

Mas o que pode ser meu erro ?

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.

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

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.

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

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?

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.