VRaptor3 + Hibernate + @Transactional na Controller

64 respostas
D

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..  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

64 Respostas

L

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;
     }

}
D

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

D

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!

D

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

L

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?

D

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

L

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

D

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!

L

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?

D

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

D

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

D

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

L

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);
    }
}
D

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?

L

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)

D

Beleza, ficou perfeito.
Valeu

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

L

blz =)

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

[]'s

D

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

abraços

L

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

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

D

hmmm
acho que não tem erro nenhum com a @Transactional não… :oops:

como prometi, fiz o projeto de teste
E FUNCIONOU!!

Tudo funcionou como deveria!
Na execução do Interceptor, a transação foi aberta e depois de exibido a jsp, commitada. Tanto que o LazyLoading funcionou normalmente.
Se tirar o @MyTransactional do método test() da TestController, o Interceptor não executa o intercept(), logo a transação não é aberta, e na JSP estoura, corretamente, uma LazyLoadingInitializationException.

a unica modificação deste projeto para o que estava dando o erro é que este não está usando Maven. Baixei as dependencias manualmente.
pode ser que a versão do AspectJ que configurei no pom.xml lá na empresa estava incorreta, ou precisava baixar mais coisas.

:arrow: enfim, estou anexando o projeto para avaliação.

PS: tirei a pasta lib para poupar espaço… mas segue as libs e suas versões que tenho na WEB-INF/lib

A versão que baixei do AspectJ foi a 1.6.10
dentro dela tem 4 jar’s… e incluí todos na pasta WEB-INF/lib

obrigado!

D

uhfuaha po,
agora que removi tudo que era Spring!!!

vou baixar e dar uma olhada, dps posto.
ah vc testou no Tomcat? qual versão?

D

hahahah

tb fiquei confuso!
amanhã no trabalho vou testar incluindo no maven a versão correta e ver se tem estas 4 libs do AspectJ
e se as outras libs também estão com as versões corretas

Sim, testei no Tomcat. Versão 6.0.29

abraços!

G

A linha onde dá o class-cast é onde o vraptor faz cast do getBean usando a própria classe passada como parametro.

As chances disso acontecer são quase nulas, a menos que tenha ocorrido algum erro na criação dos proxies, mas para investigar isso é necessário um stacktrace mais complexo. Claro, não estou dizendo que isso nunca vai acontecer, mas sim que é algo muito raro.

D

Isso é verdade, garcia-jj!

eu cheguei até a baixar os fontes do VRaptor pra ver o que tava acontecendo…

E o stacktrace era só aquele mesmo… está completo

D

Agora percebi um problema que estou tento neste projetinho que anexei… um pouco diferente do assunto principal…

O primeiro request no contexto vai perfeito… e tudo ocorre bem…

mas se eu der um F5 na página, parece que o Spring não consegue mais encontrar o Interceptor…

acontecendo esta exception:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [test.interceptor.MyInterceptor] is defined: no bean for this type registered

estou fazendo alguma coisa de errado?

Pra testar, anotei o Interceptor com @ApplicationScoped do VRaptor. Mas aí o Interceptor nem executa… nenhuma vez…

G

Na base do adivinhometro é complicado saber…

Coloque a definição da classe aqui, ou seja, quais as anotações que estão declaradas na classe.

D

Ta tudo lá no projeto que anexei no 6º post acima deste.
Mas pra simplificar tenho o seguinte cenário:

TestController.java - A Controller devidamente anotada com @Resource

@Path("/")
	@MyTransactional //minha annotation para o Interceptor pegar
	public void test() {
		
		User user = dao.getUser();
		if (user == null) {
			setup();
			user = dao.getUser();
		}
		
		result.include("user", user);
		result.include("test", "Test OK");
	}

MyTransactional.java - Minha annotation para o Interceptor pegar

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTransactional {

}

MyInterceptor.java - O Inteceptor

@Intercepts
public class MyInterceptor implements Interceptor {

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

	@Override
	@Transactional // do Spring 
	public void intercept(InterceptorStack stack, ResourceMethod method, Object obj) throws InterceptionException {
		System.out.println("===============>>>> MyInterceptor init  <<<=========================");
		stack.next(method, obj);
		System.out.println("===============>>>> MyInterceptor end  <<<=========================");
	}

}

Desta forma só 1 request funciona. Depois da NoSuchBeanDefinitionException.
E colocando @ApplicationScope no Interceptor, ele sequer é executado

Meu applicationContext.xml está em WEB-INF/classes
mas este tanto aí quanto no /src é lido e interpretado corretamente

valeu!

G

Creio que o problema seja porque o spring registra as classes que o vraptor já registrou.

Estou com uma dúvida… você precisa realmente usar esse hibrido de spring com vraptor? somente os componentes do vraptor deixando o spring apenas como provider transparente não dá? Não entendi porque ficar anotando manualmente os métodos como transaction se o vrapor já controla automagicamente as transações.

L

como o garcia falou, se vc vai colocar transaction no interceptor é melhor usar o que vem do VRaptor

D

a necessidade de usar o Spring é porque tenho jars de outros projetos que são usados em vários projetos.
Uma espécie de “Módulo Core” que é um simples projeto onde tem as entidades padrões das maiorias dos projetos daqui da empresa, assim como seus DAOs e alguns componentes comuns a varios projetos.
E tudo isso é usando Spring, pq este “módulo core” é usado tanto em projetos web, console e desktop.

Não sabia que o VRaptor ja controla automaticamente as transações.
Conheci agora o HibernateTransactionInterceptor é pra isto que este cara serve né?

valeu aí :slight_smile:

L

isso, o HibernateTransactionInterceptor é o cara pra isso =)
só que ele intercepta todas as requisições…

vc precisaria estendê-lo (sobrescrevendo o accepts) para só aceitar os métodos anotados

D

fiz uns testes estendendo o HibernateTransactionInterceptor
e vi que ele precisa de uma Session criada ja

Aí registrei o SessionCreator. Não usei o HibernateCustomProvider pq nao preciso do SessionFactoryCreator.

Ok… tudo certo… agora tenho a Session.
Mas acabou não funcionando como eu queria…
pq esta Session não é aquela gerenciada pelo Spring. Eu precisaria fechar ela manualmente, correto?

pois vi que o SessionCreator faz um factory.openSession() e nos meus DAOs eu faço factory.currentSession() que já é a com suporte a transação controlados pelo Spring

E como a maioria dos meus DAOs ficam em um Jar separado (o famoso “módulo core”) se eu mudasse ele, iria impactar diversos projetos, web ou não.
Este é um dos grandes motivos que tenho um misto de Spring e VRaptor.

Por isso a solução perfeita pra mim é colocar o @Transactional no intercepts() do Interceptor.
Só que com isso parece que o Interceptor acaba ficando registrado tanto no Spring quanto no VRaptor… e a partir do segundo request, da erro que o Spring não encontrou nenhum bean do meu Interceptor.
Isto pode ser classificado como um bug?

Obrigado!
e desculpa se eu estou sendo chato demais… hehehe… :oops:

L

talvez seja um bug sim…

o ideal é vc colocar o @Transactional nos lugares onde vc precisa de transação mesmo… não deveria ser no interceptor

ou anotar no próprio controller, ou dentro de uma classe que ele usa (no DAO por exemplo)…

vou investigar o bug e ver se tem salvação

G

:lol: :lol:

D

realmente o ideal seria anotar no próprio Controller
apesar que acho que faz sentido colocar a @Transactional no intercepts() sim… principalmente nos casos onde quer usar Lazy Loading iterando a collection na JSP com <c:forEach>

L

se vc usa o OpenSessionInViewFilter tanto faz se tah no Controller ou no Interceptor (qto ao Lazy Loading)

L

desculpa a demora, mas aparentemente eu corrigi o bug =)

testa aí com esse snapshot (substitua o seu vraptor-3.1.3.jar)

https://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.2.1-SNAPSHOT/vraptor-3.2.1-20101214.180111-8.jar

Abraços

D

que da hora, cara!
vou testar daqui a pouco!

testo colocando a @Transactional na Controller, Interceptor, ou outra forma?

demora? que nada! foi bem rápido! menos de uma semana :smiley:
parabéns pelo EXCELENTE trabalho com o framework! :smiley:

L

tenta primeiro no interceptor (que foi o que funcionou pra mim)

mas no controller tb deve funcionar (só não vai ser open session in view, a menos que vc coloque o OpenSessionInViewFilter no web.xml)

[]'s

D

ok!

e faz total sentido que colocando no controller nao vai ser open session in view… o ciclo de vida da transação acaba quando o método termina.

D

Lucas,

só por curiosidade, onde foi a mudança no código do VRaptor… to navegando no github, mas nao encontrei nenhum commit que poderia ser da correção.

valeu
abraços

L

não mandei as mudanças pro github ainda :stuck_out_tongue:

eu tirei um type.cast do VRaptorApplicationContext.getBean
https://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/ioc/spring/VRaptorApplicationContext.java

linha 259

G

Lucas Cavalcanti:
não mandei as mudanças pro github ainda :stuck_out_tongue:

eu tirei um type.cast do VRaptorApplicationContext.getBean
https://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/ioc/spring/VRaptorApplicationContext.java

linha 259

Outro dia, lendo esse tópico pensei: porque precisamos daquele type-cast se o retorno é um generic T :slight_smile:

L

unchecked warning :wink:

D

Testei com aquele projetinho que anexei aqui no tópico e funcionou perfeitamente!

mais tarde eu vou alterar no sistema aqui da empresa pra testar!

valeu!
abraços

R

opa, ressuscitando o topico…

anotei um interceptor com @Transactional como em:

@Intercepts
@Transactional
public class TransactionInterceptor implements Interceptor {

	@Override
	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
			throws InterceptionException {

		stack.next(method, resourceInstance);

	}

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

}

dai é lançada uma exception:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'transactionInterceptor' must be of type [br.com.projeto.mvc.interceptor.chain.TransactionInterceptor], but was actually of type [$Proxy113]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:360)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:270)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1113)
	at br.com.caelum.vraptor.ioc.spring.SpringBasedContainer.instanceFor(SpringBasedContainer.java:86)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:47)
	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.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.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:93)
	at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59)
	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.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54)
	at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:44)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:91)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at br.com.projeto.mvc.filter.ParametersFilter.doFilter(ParametersFilter.java:32)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
	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:225)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
	at java.lang.Thread.run(Thread.java:722)

Acho estranho anotar um intercepts com @Transactional.

Creio que usar o componente do Vraptor que abre e fecha a transacao a cada request mais bonito, porém, para fins de teste tentei fazer isso e deu erro.

algo mais deveria ser feito?

lembrando que minha session vem do spring e meu txmanager tb

UPDATE: a unica vantagem da abordagem com spring, alem do controle mais ‘fino’, seria não ter de mapear cada classe de persistencia num arquivo de configuração, correto? ou existe alguma outra?

abrass

L

pra esse @Transactional funcionar vc talvez precise fazer umas maracutaias… algo como gerar uma interface chamada TransactionInterceptor e anotar ela com o @Intercepts

mas não tenho certeza se vai funcionar mesmo…

sem usar o spring vc precisa mapear todas as classes de persistencia? em que contexto?

R

Teria de informar pro meu sessionFactory quais classes são entidades, através do hibernate.cfg.xml. Usando através do spring consigo scannear isso automaticamente, mas isso é apenas um detalhe.

A grande questão é que tenho o transações controladas pelo Spring, pensei que seria mais interessante colocar uma transação por request, uma vez que esse projeto é apenas web, assim não preciso ficar anotando os controllers com @Transactional e construtor @Autowired, correto? Também só posso receber interfaces, não que isso seja ruim, mas minha classe que representa um usuário na sessão não tem sentido de ser interfaceada.

UPDATE: então pra ter o comportamento desejado, poderia colocar o @Transactional no interceptor, assim resolveria o problema mais rapidamente, mas me parece que não é legal isso.

abrassss

L

vc pode fazer isso sim… tenta usar direto a api do spring transaction nesse interceptor… ou tenta a estratégia de criar a interface que eu falei

R

achei o lance da interface meio feio.

vou olhar a api de transação do spring, pelo pouco que conheço não será tao simples como implementar um interceptor ou usar o HibernateTransactionInterceptor que ja vem no vraptor.

Neste caso esse interceptor que ja vem seria uma boa opção concorda? posso estender ele para colocar listeners e etc correto?

obrigado mais uma vez lucas.

L

teoricamente é só pegar o TransactionManager e chamar os métodos de começar e terminar a transação…

ou vc só registra o OpenSessionInViewFilter do spring e abre e fecha a transação na mão, como o VRaptor faz no interceptor dele… vc pode criar um interceptor que estende o interceptor do VRaptor e anotá-lo, daí ele já registra

R

Excelente, tentei fazer uma solução parecida com a do Interceptor do vraptor:

@Intercepts(before = TaskValidateChainInterceptor.class)
public class TransactionInterceptor implements Interceptor {

	private final SessionFactory factory;
	private final Validator validator;

	public TransactionInterceptor(SessionFactory factory, Validator validator) {
		this.factory = factory;
		this.validator = validator;
	}

	@Override
	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
			throws InterceptionException {

		Transaction transaction = null;

		try {
			transaction = factory.getCurrentSession().beginTransaction();

			stack.next(method, resourceInstance);

			if (!validator.hasErrors()) {
				transaction.commit();
			}
		} finally {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
		}
	}

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

cai no interceptor direitinho até funciona quando tento inserir algo no banco, porém quando dou merge não atualiza os dados. Muito estranho, acredito que o vraptor já tenha feito seu papel e eu esteja errando em algo da transação.

Meu DAO generico

public abstract class GenericDAOHibernate<T> {

	@Autowired
	protected SessionFactory sessionFactory;

	private final Class<T> persistentClass;

	public GenericDAOHibernate(SessionFactory sessionFactory) {
		this();
		this.sessionFactory = sessionFactory;
	}

	@SuppressWarnings("unchecked")
	public GenericDAOHibernate() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
				.getActualTypeArguments()[0];
	}

	@SuppressWarnings("unchecked")
	public T load(Long id) {
		return (T) this.getSession().get(this.persistentClass, id);
	}

	public T save(T t) {
		this.getSession().persist(t);
		return t;
	}

	public T update(T t) {
		return (T) this.getSession().merge(t);
	}

	public void delete(T t) {
		this.getSession().delete(t);
	}

	@SuppressWarnings("unchecked")
	public List<T> list() {
		return this.getSession().createCriteria(this.persistentClass).list();
	}

	public void refresh(T t) {
		this.getSession().refresh(t);
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

}

tem alguma ideia de pq apenas o merge nao funciona?

UPDATE: é como se a session do DAO nao fosse a mesma do interceptor…

abrasss

L

vc tah com o openSessionInViewFilter configurado?

R

sim… o spring gerencia meu sessionFactory e tenho o filtro do openSessionInView no meu web.xml

<filter>
		<filter-name>openSessionInViewFilter</filter-name>
		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
	</filter>

Estou fazendo uns testes com o TransactionManager do spring, no caso a classe PlatformTransactionManager. Da certo. Vou até dar um feedback sobre isso, mas achei estranho o caso acima não dar certo.

L

bem estranho
tenta imprimir as sessions, pra tentar ver se elas são o mesmo objeto

L

e: o OpenSession filter tem que estar declarado antes do do vraptor.

R

lucas n declaro o vraptor pq uso servlet 3. Meu web.xml está assim:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>fourData</display-name>

	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<filter>
		<filter-name>openSessionInViewFilter</filter-name>
		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>openSessionInViewFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<context-param>
		<param-name>br.com.caelum.vraptor.packages</param-name>
		<param-value>br.com.caelum.vraptor.http.iogi</param-value>
	</context-param>

	<context-param>
		<param-name>br.com.caelum.vraptor.encoding</param-name>
		<param-value>UTF-8</param-value>
	</context-param>
	
</web-app>

fiz um debug e ele apresenta o mesmo id de objeto na request… creio que é o mesmo sim.

L

bem estranho… tenta quais operações que dão errado? são todas? ou soh o merge?

R

quando faço um insert, ele insere o objeto, quando dou um merge ele não muda o atributo alterado.

Porém fiz assim e deu certo

@Intercepts(before = TaskValidateChainInterceptor.class)
public class TransactionInterceptor implements Interceptor {

	private final Validator validator;

	private PlatformTransactionManager transactionManager;

	public TransactionInterceptor(Validator validator, PlatformTransactionManager transactionManager) {
		this.validator = validator;
		this.transactionManager = transactionManager;
	}

	@Override
	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
			throws InterceptionException {

		TransactionDefinition def = new DefaultTransactionDefinition();
		TransactionStatus status = transactionManager.getTransaction(def);

		try {
			stack.next(method, resourceInstance);

			if (!validator.hasErrors()) {
				transactionManager.commit(status);
			}
		} catch (Exception e) {
			transactionManager.rollback(status);
		}

	}

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

dao generico

@SuppressWarnings("unchecked")
public abstract class GenericDAOHibernate<T> {

	protected SessionFactory sessionFactory;

	private final Class<T> persistentClass;

	@Autowired
	public GenericDAOHibernate(SessionFactory sessionFactory) {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
				.getActualTypeArguments()[0];

		this.sessionFactory = sessionFactory;
	}

	public T load(Long id) {
		return (T) this.getSession().get(this.persistentClass, id);
	}

	public T save(T t) {
		this.getSession().persist(t);
		return t;
	}

	public T update(T t) {
		System.out.println("Session do dao: " + this.getSession());
		return (T) this.getSession().merge(t);
	}

	public void delete(T t) {
		this.getSession().delete(t);
	}

	public List<T> list() {
		return this.getSession().createCriteria(this.persistentClass).list();
	}

	public void refresh(T t) {
		this.getSession().refresh(t);
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

}

implementacao do dao

@Repository
public class LawsuitDAOHibernate extends GenericDAOHibernate<Lawsuit> implements LawsuitRepository {

	@Autowired
	public LawsuitDAOHibernate(SessionFactory sessionFactory) {
		super(sessionFactory);
	}

	@Override
	public Lawsuit getLawsuitBy(Hearing hearing) {
		.....
	}
}
L

cool =)

talvez seja legal extrair um pluginzinho com isso… que acha?

R

Excelente, ia até perguntar oq vc achou para blogar.

Faço o mesmo esquema do plugin do Gson no github e te aviso.

Muito obrigado pela disposição e paciência.

UPDATE: a instanciação do objeto TransactionDefinition pode vir de uma factory method pra deixar esse codigo testável, procede?

abrasss

R

Lucas, vou subir para o git, qual o pacote vc sugere para colocarmos a classe?

Que tal br.com.caelum.vraptor.util.spring ?

UPDATE: fiz um cenário para exemplificar o cenário como um todo: http://renanreismartins.blogspot.com.br/2012/12/controle-transacional-spring-vraptor.html

abrassss

L

creio que seria legal:

br.com.caelum.vraptor.plugin.spring

talvez terminar com .tx a parte das transações

R

ok…

https://github.com/renanreismartins/vraptor-spring-tx-manager

abrassss

Criado 10 de novembro de 2010
Ultima resposta 19 de dez. de 2012
Respostas 64
Participantes 5