VRaptor3 + Hibernate + @Transactional na Controller

opa…

é possível anotar um método de uma Controller com a @Transactional do Spring?
assim:

 @Path("/detalhado/{idBaseProcessamento}")
 @Transactional
	public void detalhado(Long idBaseProcessamento) {		
		BaseProcessamento base = dao.findById(idBaseProcessamento);
		base.getItems().size(); //fiz isso pra rodar o lazy loading.. só pra teste

		result.include("base", base);
	}

acontece que desde que coloquei comecou a dar pau de CGLib… mas eu inclusive já tenha a lib dela…
antes deu classnotfound do AspectJ… coloquei o jar… aí depois começou a dar IllegalStateException de CGLib alegando que não poderia passar null num tal construtor la…

Qual a maneira correta de transacionar um método do Controller?

Obrigado

o melhor jeito de usar um Transactional no controller é usando Interceptors do VRaptor mesmo…

crie esse interceptor:

@Intercepts
public class TransactionalInterceptor {

    //sem construtor

    public boolean accepts(ResourceMethod method) {
          return method.containsAnnotation(MeuTransactional.class); //crie essa anotação
    }

    @Transactional // do spring
    public void intercept(...) {
         stack.next(...);
    }
}

outra solução é fazer injeção por init method nos controllers que precisarem do @Transactional:

@Resource
public class MeuController {

     @Autowired
     public void init(Objeto1 um, Objeto2 dois) {
          //public MeuController(Objeto1 um, Objeto2 dois) { //troque o construtor pelo init
         this.um = um;
         this.dois = dois;
     }

}

valeu Lucas! sempre nos ajudando! :smiley:

gostei mais da primeira opção. me pareceu mais natural.
cada dia me impressiono mais com o VRaptor! :slight_smile:

mas fiquei com uma dúvida aqui:
existe algum motivo tecnico para um método do Controller nao poder ser anotado pela @Transactional do Spring?

digo isto pois uma Controller é, internamente, um componente do Spring certo? Logo a anotação não seria tratada automaticamente pelo Spring?

outro motivo seria nos TestCase das Controllers… pois se a transação fica no Interceptor, vai ser ignorado no testcase

valeu!
abraço

pensando bem, acho que ja tenho as respostas das minhas dúvidas acima…

o VRaptor pode ter outros provides de IoC além do Spring, logo não poderia amarrar a @Transactional do Spring numa Controller

sobre os testes… dei uma viajada… os controllers, quando testados, são instanciados via operador ‘new’… ou seja, qualquer anotação no método será ignorada

estou correto nas minhas colocações?

agora uma sugestão: pra evitar amarrar a controller no Spring com a @Transactional, o VRaptor poderia ter uma anotação própria repassasse o gerenciamento da transação para o provider usado.

valeu!

não rolou nem com a anotação própria…
deu ClassCastException :frowning:

bom, vamos lá:

o spring consegue tratar a anotação dentro do controller. O q acontece é q qdo vc coloca a anotação no controller o Spring usa o AspectJ pra colocar as transações. E o AspectJ só consegue fazer isso se a classe tiver construtor sem argumentos ou vc usar interfaces. No caso do controller não tem como chamar via interface (pq é o vraptor que chama), então vc precisa do construtor sem argumentos mesmo.

não é por isso, é pelo motivo que eu citei acima. o Transactional não tem nada a ver com o VRaptor

nos seus testes vc vai precisar controlar a transação manualmente, ou mockar a session ou o dao pra não precisar disso

os outros containers não tem nada parecido com o @Transactional.
vc pode usar o do spring sim, desde que o seu controller tenha um construtor sem argumentos.

qual classCast?

Lucas,

criei o Interceptor e a anotação própria exatamente do jeito que você falou.
mas tive o mesmo ClassCastException que o dbdbdb teve.

Ela aparece sempre que subo o VRaptor com alguma anotação @Transactional (do Spring)
Engraçado que é só tirar a anotação que a aplicação sobe perfeitamente.

java.lang.ClassCastException
	java.lang.Class.cast(Class.java:2990)
	br.com.caelum.vraptor.ioc.spring.VRaptorApplicationContext.getBean(VRaptorApplicationContext.java:259)
	br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:59)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:47)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:44)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:81)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:53)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

Alguma idéia do que está causando isso?

Valeu

vc não pode colocar o @Transactional no interceptor, nem usar nenhum tipo de AOP nos interceptors do VRaptor… (um bug talvez?)

se a anotação estiver em outro tipo de classe deveria funcionar

foi exatamente esta classclastexception que tive…
mas acabei criando uma outra classe e colocando a lógica do método do controller nessa classe… essa classe sim tinha o método anotado com @Transactional

como isso resolveu, acabei esquecendo de postar a exception de ClassCast aqui… sorry…

enfim, então realmente não da pra anotar o intercept() com o @Transactional como vc postou acima?

valeu Lucas! um legítimo suporte técnico para o excelente VRaptor! :smiley:

abraços!

a anotação estava no Controller? ou no Interceptor?

com o código atual do VRaptor não dá pra anotar com @Transactional em alguns tipos de classes, talvez seja uma boa investigar se a gente está fazendo algo de errado ou se tem alguma solução boa pra isso…

abre uma issue no vraptor por favor?

na minha primeira tentativa (primeiro post do tópico) estava num método da controller

aí vc me explicou que não daria pois como a Controller nao tem interface, as dependencias nao poderiam ser setadas no construtor, e sim num método init().
ou então criando uma annotation própria e fazendo um Interceptor e colocando a @Transactional no intercepts()

entao a ClassCastException deu quando a @Transactional estava no intercepts() do Interceptor

amanha na empresa vou fazer um projetinho que simula o erro e vou incluí-la na issue que pediu pra criar

valeu mais uma vez!
as vezes acho que estou abusando da sua boa vontade! hehehe…
no que precisar de ajuda sobre este assunto, pode contar comigo!

abraços

criei a issue: https://github.com/caelum/vraptor/issues/issue/305

mas nao achei opção de anexar arquivo.
entao amanha quando eu criar o projeto que da o ClassCastException eu vou anexar neste tópico, ok?

valeu

bem, enquanto isso como podemos resolver?

teremos que retirar as @Transactional e as injeções de EntityManager do Spring e utilizar as injeções de EntityManager (ComponentFactory) do próprio VRaptor?

Valeu,
abraços

se vc quer a transação na requisição inteira, em algumas lógicas, é melhor usar o interceptor do vraptor sim…

web.xml:

	<context-param>
		<param-name>br.com.caelum.vraptor.packages</param-name>
		<param-value>br.com.caelum.vraptor.util.jpa</param-value>
	</context-param>

crie uma anotação para habilitar ou desabilitar transações, por exemplo @WithTransaction

crie a classe:

@Component
public class CustomJPAInterceptor extends JPATransactionInterceptor {

    @Override
    public boolean accepts(ResourceMethod method) {
         method.containsAnnotation(WithTransaction.class);
    }
}

mas qual é a minha referência de EntityManager dentro desse CustomJPAInterceptor para que eu possa executar begin() e commit() ?

Se eu injentar um EntityManager, ele criará uma nova instância? diferente da EntityManager que foi injetada em meus DAOs?

vc não precisa fazer o begin e commit no CustomJPAInterceptor, pq ele já faz isso no intercepts()
(se vc estiver estendendo JPATransactionInterceptor)

se vc recebe EntityManager no construtor é a mesma instância que nos daos (na mesma request)

Beleza, ficou perfeito.
Valeu

Mas se vocês conseguirem algo sobre o @Transactional do Spring, por favor dá um toque aqui.
Abraços

blz =)

(vc tb vai receber emails qdo a gente atualizar a issue que vc criou =))

[]'s

hehe não fui eu, foi o dbdbdb
mas blz, ficarei atento

abraços

ops, confundi, sabia q começava com d :stuck_out_tongue:

vou dar um toque aqui sim, então =)