Hibernate . problema ao deletar objeto (VRaptor 3.1)

8 respostas
P

Boas pessoal

Estou trabalhando em um projeto com VRaptor 3.1

Registrei o HibernateProvider

<context-param>  
        <param-name>br.com.caelum.vraptor.provider</param-name>  
        <param-value>br.com.caelum.vraptor.util.hibernate.HibernateCustomProvider</param-value>  
    </context-param>

Tenho uma classe Usuario e uma Auditoria, que tem referencia a um Usuario

@Entity
@SequenceGenerator(name = "AUDITORIA_SEQ", sequenceName = "auditoria_seq")
public class Auditoria implements Serializable{

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO,generator="AUDITORIA_SEQ")
	private Long id;
	
	@ManyToOne
	private Usuario usuario;
	
}
package gpo.model;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;

/**
 * @author Paulo Henrique
 *
 */
@Entity
@SequenceGenerator(name = "USUARIO_SEQ", sequenceName = "usuario_seq") 
public class Usuario {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO,generator="USUARIO_SEQ")
	private Long id;

	@Column(nullable=false)
	private String nome;

	@Column(length=15, unique=true, nullable=false)
	private String login;

	@Column(length=15, nullable=false)
	private String senha;

	
}

Toda vez que um Usuário acessa o sistema, ele gera já uma Auditoria do tipo acesso.

O problema é quando vou excluir o Usuário.

Tem algum jeito que eu poderia anotar a relação (na classe Auditoria) para que ao excluir o Usuario as Auditorias fossem excluídas?

Porque o que ta acontecendo é que, quando vou excluir o Usuario, lança Exception pois o Usuario está sendo referenciado na tabela Auditoria. Tentei manualmente excluir as auditorias. Faço o select de todas auditorias daquele Usuario, imprimo o size da lista (digamos que esteja em 6). Depois faço um for pela lista e excluo uma por uma. Faço um novo select e imprimo o tamanho da lista (ficou zero, o que me faz acreditar que excluiu as auditorias). Em seguida tento excluir o usuario, mas continua dando a excessao de que o Usuario esta sendo referenciado na tabela auditoria.

@Path("/usuarios/excluir/{id}")
	public void excluirUsuario(Long id){	
		Usuario usuario = this.daoFactory.getUsuarioDao().procura(id);
		result.include("mensagem", "Usuário "+usuario.getNome()+" excluído com sucesso.");
		List<Auditoria> auditorias = this.daoFactory.getAuditoriaDao().auditoriaUsuario(usuario);
		System.out.println(auditorias.size()); //imprime correto o número de auditorias
		
		while (auditorias != null && auditorias.size() != 0){
			Auditoria aux = auditorias.remove(0);
			this.daoFactory.getAuditoriaDao().remove(aux);
			this.daoFactory.getAuditoriaDao().limpaCache(aux);
			aux = null;
		}
		
		auditorias = this.daoFactory.getAuditoriaDao().auditoriaUsuario(usuario);
		System.out.println(auditorias.size()); //imprime 0(zero)
		this.daoFactory.getUsuarioDao().remove(usuario); //lança a exception
		result.redirectTo(UsuariosController.class).listaUsuarios(1, null, null, null);
	}

Segue o erro:

12:11:04,962  WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: null
12:11:04,962 ERROR [JDBCExceptionReporter] Entrada em lote 0 delete from Usuario where id=23 foi abortada. Chame getNextException para ver a causa.
12:11:04,962  WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: 23503
12:11:04,962 ERROR [JDBCExceptionReporter] ERROR: update or delete on table "usuario" violates foreign key constraint "fk82fc84763ee195cd" on table "auditoria"
  Detalhe: Key (id)=(23) is still referenced from table "auditoria".
12:11:04,962 ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:366)
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
	at br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:46)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:48)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:55)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.BatchUpdateException: Entrada em lote 0 delete from Usuario where id=23 foi abortada. Chame getNextException para ver a causa.
	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
	... 37 more
05/02/2010 12:11:04 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet default threw exception
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:366)
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
	at br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:46)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:41)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:46)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:48)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:59)
	at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:62)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:55)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.BatchUpdateException: Entrada em lote 0 delete from Usuario where id=23 foi abortada. Chame getNextException para ver a causa.
	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
	... 37 more

Se algum dos gurus em VRaptor ou Hibernate puder me ajudar agradeço muito.

Abraços

8 Respostas

P

Sim cara, eu entendo isso e em vários outros relacionamentos faço dessa forma.

Mas nesse caso específico, eu não queria que o Usuário tivesse a lista de auditorias, pois toda vez que busco um Usuário, não preciso necessariamente (quase nunca) da lista de auditorias realizadas por ele. Eu imagino que o default seja LAZY LOAD e eu não teria problema, pois não buscaria as auditorias se eu não as chamasse, estou certo? Se for isso mesmo, ai ok, caso contrário, não posso ter essa lista ai pois ficaria pesado, pois podem haver muitas auditorias.

Valew

Paulo_Silveira

Paulo

Uma ideia: Por que nao criar um atributo boolean em usuario que indica se ele esta ativo? dessa forma, em vez de deleta-lo, voce só marca ele como inativo, e se um inativo tentar logar, voce trata como se o usuari nao existisse. Dessa forma voce nao tera o problema de chave extrangeira e mantem seus dados de auditoria no banco de dados, caso precise num futuro saber sobre usuarios inativos.

P

Paulo Silveira

Realmente não tinha visto por esse lado.

No ponto que estou do sistema, as auditorias apenas são de login, de forma que ao excluir o usuário eu poderia excluir elas também. Porém, mais adianta, outras ações serão auditadas, e ai sim seria interessante não excluir o usuário. Vou seguir por esse caminho. Obrigado pela dica.

Aproveitando, digamos que tenho uma classe produto. Quando houver uma edição de produto, terei que auditar quem fez a modificação e quais campos foram modificados. Existe alguma forma de fazer isso ou terei de pegar o produto do banco, e comparar atributo por atributo para ver quais foram alterados e depois atualizá-lo no banco?

Muito obrigado.

P

Obrigado, vou dar uma olhada no Envers.

Pelo pouco que olhei lá, para cada classe auditada (por exemplo, produto e usuario), seria criada uma tabela de auditoria, e não estaria tudo em uma mesma tabela de auditoria como eu tenho hj. É isso mesmo?

Vlw

G

paulohrl:
Obrigado, vou dar uma olhada no Envers.

Pelo pouco que olhei lá, para cada classe auditada (por exemplo, produto e usuario), seria criada uma tabela de auditoria, e não estaria tudo em uma mesma tabela de auditoria como eu tenho hj. É isso mesmo?

Vlw

Sim, isso mesmo. Não lembro ao certo o padrão do envers, se não me engano é nome_da_tabela + _audit, porém você pode alterar isso nas configurações.

O envers faz parte já do core do Hibernate, e se não me engano a partir da versão 3.5 o envers já vem junto, não necessitando jar extra algum. O projeto envers logo não existirá mais e será tudo tratado apenas como Hibernate.

seufagner

O problema não é do VRaptor e sim do mapeamento das entidades.

@Entity
@SequenceGenerator(name = "USUARIO_SEQ", sequenceName = "usuario_seq") 
public class Usuario {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO,generator="USUARIO_SEQ")
	private Long id;

	@Column(nullable=false)
	private String nome;

	@Column(length=15, unique=true, nullable=false)
	private String login;

	@Column(length=15, nullable=false)
	private String senha;

       @OneToMany(mappedBy="usuario",cascade=CascadeType.ALL)
        private List<Auditoria> auditorias;

..
}
Desta forma as alterações em Usuario vão se refletir, também, em auditoria. :-)
seufagner

É. Normalmente não se excluir auditorias e sim faz o famoso “delete lógico”, como o Paulo falou.

Ah, mas só para te esclarecer: O default é EAGER, não LAZY.

seufagner

paulohrl:
Paulo Silveira

Realmente não tinha visto por esse lado.

No ponto que estou do sistema, as auditorias apenas são de login, de forma que ao excluir o usuário eu poderia excluir elas também. Porém, mais adianta, outras ações serão auditadas, e ai sim seria interessante não excluir o usuário. Vou seguir por esse caminho. Obrigado pela dica.

Aproveitando, digamos que tenho uma classe produto. Quando houver uma edição de produto, terei que auditar quem fez a modificação e quais campos foram modificados. Existe alguma forma de fazer isso ou terei de pegar o produto do banco, e comparar atributo por atributo para ver quais foram alterados e depois atualizá-lo no banco?

Muito obrigado.

Leia sobre o Envers. São poucas configurações para que você possa obter total controle sobre auditoria e versionamento dos registros do banco.

http://www.jboss.org/envers

Criado 5 de fevereiro de 2010
Ultima resposta 5 de fev. de 2010
Respostas 8
Participantes 4