VRaptor3 + Hibernate + @Transactional na Controller

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.

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

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

[quote=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[/quote]

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

unchecked warning :wink:

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

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

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?

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

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

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.

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

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

vc tah com o openSessionInViewFilter configurado?

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.

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

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

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.

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

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) {
		.....
	}
}