Controle transacional: Spring + Vraptor
Autor: Renan Reis | Publicado: 2012-12-18T18:09:00-02:00

Controle de transação é sempre um ponto crítico em qualquer aplicação. Iniciar, tratar falhas e finalizar transações manualmente pode ser muito complicado, portanto muitas vezes usamos algum framework ou prática já consolidada para abordar o problema.



Em aplicações web uma abordagem muito utilizada é "uma transação por requisição", que consiste em abrir uma transação a cada requisição feita e no fim da lógica da requisição podemos efetuar o commit caso tudo tenha acontecido como esperado ou efetuar um rollback.

Note que você pode fazer isso em todos os seus servlets ou métodos dos controllers do seu framework mvc, mas dessa forma a tarefa será repetitiva e de difícil manutenção. Para melhorarmos o cenário, caso esteja trabalhando com uma aplicação usando uma api mais baixa como Servlets é possível usar um filter para implementar essa funcionalidade.



Com o Vraptor&nbsp;temos essa funcionalidade pronta, veja a documentação&nbsp;e o código. Se seguirmos a apostila oficial&nbsp;veremos que neste caso o Vraptor gerencia a SessionFactory, a Session e também nossos DAOs.

A motivação deste post foi um cenário encontrado em um projeto onde já possuíamos tudo gerenciado pelo Spring, inclusive transações e DAOs, não gostaríamos de refatorar tudo e ainda assim tirar proveito da abordagem de uma transação por requisição. Para quem quiser acompanhar como chegamos a solução segue o link do post no guj:&nbsp;http://www.guj.com.br/java/224207-vraptor3--hibernate--transactional-na-controller/4

Vamos ao código:

<beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/data/mongo
      http://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd
      http://www.springframework.org/schema/data/jpa
      http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


  
  
  
  
 

 
  
  
  
   
    true
    true
    org.hibernate.dialect.MySQLDialect
    update
    true
   
  


 
  
 




Este é o applicationContex.xml usado na aplicação, apenas declaramos o dataSource, nossa sessionFactory e também um transactionManager. Nada diferente de qualquer aplicação Spring normal.



Criamos um DAO genérico:
public abstract class GenericDAOHibernate&lt;T&gt; {

 protected SessionFactory sessionFactory;

 private final Class&lt;T&gt; persistentClass;

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

  this.sessionFactory = sessionFactory;
 }

 public T load(Long id) {
  return (T) this.getSession().get(this.persistentClass, id);
 }
       
        // outros métodos...

}

Uma implementação do nosso DAO 

@Repository
public class UserDAOHibernate extends GenericDAOHibernate {

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

 @Override
 public User getUserByLogin(String login) {
  //...
 }
        // outros métodos...

}


E por fim o nosso interceptador de transações:


@Intercepts
public class SpringTransactionInterceptor implements Interceptor {

 private PlatformTransactionManager transactionManager;

 private final Validator validator;

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

 @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);
   }
  } finally {
   if (!status.isCompleted()) {
    transactionManager.rollback(status);
   }
  }

 }

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

Note que você pode tanto neste interceptor, quanto no padrão do Vraptor, sobreescrever os comportamentos. Um exemplo seria interceptar apenas métodos anotados com uma anotação própria.

Você pode baixar esta classe na forma de plugin no Github de contribuições do Vraptor ou no meu particular.

&nbsp;Caso tenha alguma dúvida é só entrar em contato.