Vraptor 3.3.1 + Hibernate + ConstraintViolationException

Pessoal,
Quando tento deletar um objeto persistido que possui uma constraint, não consigo capturar a exxeption.

public void remove(Long id){
Profissao Profissao = dao.carrega(id);
try {
dao.remove(Profissao);
} catch (ConstraintViolationException e) {
result.redirectTo(this).constraintViolationException();
}
result.redirectTo(this).lista();
}

Stack trace:

19/06/2011 09:43:28 org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() for servlet [default] in context with path [/Hermes] threw exception
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor.intercept(HibernateTransactionInterceptor.java:50)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69)
at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:23)
at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:562)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:395)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:250)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (hermes.perfildocliente, CONSTRAINT FK9C4FA2637DC9DD45 FOREIGN KEY (profissao_codigoCBO) REFERENCES profissao (codigoCBO))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1257)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:943)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
… 44 more

Olá. Ao postar código, por favor, siga essas dicas:
http://www.guj.com.br/java/50115-voce-e-novo-no-guj-vai-criar-um-topico-e-colar-seu-codigo-fonte-leia-aqui-antes-por-favor

Você pode usar o exception handler que está disponível desde o 3.2: http://vraptor.caelum.com.br/documentacao/exception-handling/

não adianta dar o try…catch no dao.remove, pois a exceção só acontece no commit da transação…

você pode usar o exceptionhandler ou criar um interceptor que trata essa exceção (e roda before=HibernateTransactionInterceptor.class ou qqer que seja o interceptor de transações que vc está usando)

Vou tentar aqui Lucas. Vlw pela ajuda.

Lucas, criei um Interceptor, conforme código abaixo

@Intercepts(before=HibernateTransactionInterceptor.class)
public class ConstraintViolationInterceptor implements Interceptor {
	
	private final Result result;

	public ConstraintViolationInterceptor(Result result) {
		this.result = result;
	}

	@Override
	public boolean accepts(ResourceMethod method) {
		return true;
	}

	@Override
	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) throws InterceptionException {
		try {
			stack.next(method, resourceInstance);
		} catch (ConstraintViolationException e) {
			result.include("error", "Este registro não pode ser removido pois está sendo usado por outro registro");
		}
	}
}

E na minha página eu tenho:

<c:if test="${not empty error}">
	<div class="ui-state-error ui-corner-all itlx-error">
		<span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
		<span class="itlx-error-msg">${error}</span>
	</div>
</c:if>

Porém a mensagem nunca é exibida, acredito que é pq o interceptador é chamado após o interceptor do vraptor e aí a mensagem já não está mais lá. Tentei fazer um forward ou um redirect mas sempre dá erro pq diz que a resposta já foi comitada. Como eu devo proceder? Está certo usar o before no interceptor né? Valeu.

aí que está, o interceptor do hibernate é Open Session In view, ou seja, ele só vai commitar a transação após a geração da página. Ou seja, nesse interceptor a resposta já está commitada e não adianta fazer mais nada relacionada à response.

o que você precisa é controlar as transações de um jeito mais esperto, que commite a transação antes de gerar a resposta…

você registrou o pacote do hibernate no web.xml, ou você implementou na mão as factories de Session e SessionFactory?

Eu registrei o pacote no web.xml.

tenta fazer isso:

-mude o seu interceptor pra @Intercepts(after=ExecuteMethodInterceptor.class, before=ForwardToDefaultViewInterceptor)

-mude o intercepts pra

try {
    session.getTransaction().commit();
    stack.next(...);
} catch (ConstraintViolationException e) {
    //coisas aqui
}

talvez dê pau por causa da transação sendo commitada duas vezes, mas testa aí

dá pau antes mesmo.

org.hibernate.TransactionException: Transaction not successfully started

vc usou o getTransaction ou o beginTransaction? tem que ser o getTransaction

usei o getTransaction.

acho que a solução mais fácil é não usar o controle de transações do VRaptor e controlar manualmente as transações…

o que você pode tentar fazer é usar o controle de transações do Spring, como no apêndice de spring dessa apostila:
http://www.caelum.com.br/curso/fj-28-vraptor-hibernate-ajax/

daí vc anota os métodos que precisam realmente de transação (os que modificam o banco) com @Transactional

Oops, my bad.

Pessoal,
Estou conseguindo capturar a exception com o interceptor abaixo:

package br.estacio.hermes.interceptor;

import org.apache.log4j.chainsaw.Main;
import org.hibernate.exception.ConstraintViolationException;

import br.com.caelum.vraptor.InterceptionException;
import br.com.caelum.vraptor.Intercepts;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.core.InterceptorStack;
import br.com.caelum.vraptor.interceptor.Interceptor;
import br.com.caelum.vraptor.interceptor.OutjectResult;
import br.com.caelum.vraptor.resource.ResourceMethod;
import br.com.caelum.vraptor.util.hibernate.HibernateTransactionInterceptor;
import br.estacio.hermes.controller.MainController;
import br.estacio.hermes.controller.ProfissoesController;


@Intercepts(before=HibernateTransactionInterceptor.class)  
public class ConstraintViolationInterceptor implements Interceptor {  
    
    private final Result result;  
  
    public ConstraintViolationInterceptor(Result result) {  
        this.result = result;  
    }  
  
    public boolean accepts(ResourceMethod method) {  
        return true;  
    }  
  
    public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) throws InterceptionException {  
        try {  
            stack.next(method,resourceInstance);  
        } catch (ConstraintViolationException e) {  
        	
    	}  
    }  
}  

O problema todo é que quando a exception é captura, eu sou redirecionado para a lógica anterior, mas não consigo enviar uma mensagem ou um erro para a view. Alguém faz idéia ???

você pode tentar usar o exceptionHandler do VRaptor…

implemente o interceptor assim:

public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance) throws InterceptionException {    
        result.on(ConstraintViolationException.class).redirectTo(....)...
        stack.next(method,resourceInstance);    
        
    }   

assim vc consegue redirecionar para alguma lógica… mas não tenho certeza se vai funcionar

Olá,

Desculpem reativar este post, no entanto estou tendo este problema e não consegui resolver utilizando

@Intercepts(before=HibernateTransactionInterceptor.class) 
public class ConstraintViolationInterceptor implements Interceptor {  ...

//e no intercepts

@result.on(ConstraintViolationException.class).include(Arrays.asList(mensagem)).redirectTo("/");

Estou usando o plugin para hibernate 4 (vraptor-plugin-hibernate4-1.0.1.jar) para criar a session, no vraptor

No entanto não consigo capturar a ConstraintViolationException nos metodos remove do Session do hibernate.

A unica solução seria realmente ter que usar o Spring? Não queria mecher em todas as minhas Daos e ter que colocar mais jars e mais configuração XML do que já tem…

Esquece, já resolvi, fazendo o commit antes do plugin… assim a exception é capturada pelo controller sem outros problemas.


		if (shouse != null) {
			this.session.delete(shouse);
			if(!session.getTransaction().wasCommitted()){
				session.getTransaction().commit();
			}
		}