Neste artigo vou mostar como interceptar um método de um Resource especifico, identificando-o a partir de uma anotação e executar ações antes do método executar, e após ele executar.
Vamos supor que nós temos o seguinte Resource para adicionar produtos no nosso sistema
- @Resource
- public class ProdutoController {
- private final DaoFactory factory;
- private final ProdutoDao dao;
- public ProdutoController(DaoFactory factory) {
- this.factory = factory;
- this.dao = factory.getProdutoDao();
- }
- public List<Produto> listar() {
- return dao.list();
- }
- public Produto atualizar(Produto produto) {
- try {
- factory.beginTransaction();
- produto = dao.update(produto);
- factory.commit();
- return produto;
- } catch (DaoException ex) {
- factory.rollback();
- throw ex;
- }
- }
- public Produto adicionar(Produto produto) {
- try {
- factory.beginTransaction();
- produto = dao.store(produto);
- factory.commit();
- return produto;
- } catch (DaoException ex) {
- factory.rollback();
- throw ex;
- }
- }
- }
Agora nos queremos que um interceptador intercepte meu recurso, e execute a lógica dentro de um escopo transacional, como fazer isso ? é só criar um interceptador assim.
- import org.hibernate.Session;
- import org.hibernate.Transaction;
- import br.com.caelum.vraptor.Intercepts;
- import br.com.caelum.vraptor.core.InterceptorStack;
- import br.com.caelum.vraptor.interceptor.Interceptor;
- import br.com.caelum.vraptor.resource.ResourceMethod;
- @Intercepts
- public class TransactionInterceptor implements Interceptor {
- private final Session session;
- public TransactionInterceptor(Session session) {
- this.session = session;
- }
- Transaction transaction = null;
- try {
- transaction = session.beginTransaction();
- stack.next(method, instance);
- transaction.commit();
- } finally {
- if (transaction != null && transaction.isActive()) {
- transaction.rollback();
- }
- }
- }
- public boolean accepts(ResourceMethod method) {
- return true; //aceita todas as requisições
- }
- }
Ok, o interceptador vai rodar e abrir transação antes e depois de executar a logica, e os métodos transacionais da minha lógica irão se reduzir a isto
- public Produto atualizar(Produto produto) {
- return dao.update(produto);
- }
- public Produto adicionar(Produto produto) {
- return dao.store(produto);
- }
Ok mas, neste caso temos o problema de que métodos que não exigem transação estão abrindo e fechando transação a cada requisição sem necessidade.
Como então selecionar apenas algumas lógicas para serem transacionais ? podem criar uma anotação para isto, desta forma:
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * Usado para garantir que um determinado recurso interceptado seja executada em um
- * escopo de tranzação.
- * @author Tomaz Lavieri
- * @since 1.0
- */
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD,ElementType.TYPE})
- public @interface Transactional {}
Agora precisamos marcar os pontos onde queremos que o escopo seja transacional com esta anotação.
- @Resource
- public class ProdutoController {
- private final ProdutoDao dao;
- public ProdutoController(ProdutoDao dao) {
- this.dao = dao;
- }
- public List<Produto> listar() {
- return dao.list();
- }
- @Transactional
- public Produto atualizar(Produto produto) {
- return dao.update(produto);
- }
- @Transactional
- public Produto adicionar(Produto produto) {
- return dao.store(produto);
- }
- }
Ok o código ficou bem mais enxuto, mas como interceptar apenas os métodos marcados com esta anotação ?? para tal basta no nosso accepts do TransactionInterceptor verificarmos se a anotação esta presente no método, ou no proprio rescurso (quando marcado no recurso todos os métodos do recurso seriam transacionais).
A modificação do método ficaria assim
- public boolean accepts(ResourceMethod method) {
- return method
- .getMethod() //metodo anotado
- .isAnnotationPresent(Transactional.class)
- || method
- .getResource() //ou recurso anotado
- .getType()
- .isAnnotationPresent(Transactional.class);
- }
Pronto agora somente os métodos com a anotação @Transacional são executados em escopo de transação e economisamos linhas e linhas de códigos de try{commit}catch{rollback throw ex}
Parabens Tomaz! Vamos linkar no site assim que tiver um novo deploy!
ResponderExcluir