Controle transacional declarativo com Spring: AOP ou @Transactional
Aprenda as vantagens e desvantagens de configurar o controle transacional do Spring via XML ou anotação
Um dos principais motivos para adoção do Spring em projetos Java sem dúvida está relacionado ao seu robusto, simples e flexível controle transacional. O gerenciamento das transações feito pelo Spring evita diversos problemas durante o desenvolvimento, como repetição de código, acoplamento entre código de negócio e infraestrutura, vazamento de conexões e o famigerado glue code.
O controle transacional do Spring é suportado de forma declarativa e/ou programática, o que possibilita um controle fino e transparente das transações dentro da aplicação. Para a maioria das aplicações o controle declarativo é o mais indicado, devido a sua simplicidade e baixo ou nenhum acoplamento com a API de transações do framework. No entanto, quando usamos o gerenciamento de transações declarativo, precisamos decidir qual abordagem usar, pois ela se divide em duas: AOP (Programação Orientada a Aspectos) e @Transactional
.
Entender os prós e contras de cada abordagem é essencial para qualquer arquiteto ou desenvolvedor mais experiente, dessa forma evita-se problemas dentro da aplicação e problemas de comunicação entre a equipe.
AOP (Programação Orientada a Aspectos) ou XML
O controle transacional declarativo via Spring AOP se dá através de configuração XML. A idéia básica é declararmos de forma global as regras que definem quais métodos devem ser transacionais ou não:
<tx:advice id="txAdvice">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- métodos que começam com 'get' ou 'busca' são read-only -->
<tx:method name="get*" read-only="true" />
<tx:method name="busca*" read-only="true" />
<!-- demais métodos são transacionais por padrão -->
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
O próximo passo é definir através de pointcuts em quais classes e métodos estas regras devem ser aplicadas:
<!-- Aqui decidimos onde a regras acima serão aplicadas -->
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* br.com.triadworks.myapp.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice"pointcut-ref="serviceMethods" />
</aop:config>
O aspecto acima define que todas as classes do pacote br.com.triadworks.myapp.service
terão seus métodos gerenciados pelas regras que definimos dentro da tag tx:advice
, ou seja, métodos com nomes que começam com get
ou busca
serão transacionais com somente leitura; enquanto os demais métodos serão transacionais com leitura e escrita.
@Transactional ou anotação
O controle transacional declarativo via anotação se dá com o uso da anotação @Transactional
. Basicamente temos que habilitar este controle com a seguinte tag no applicationContext.xml
:
<tx:annotation-driven />
Depois disso, basta anotarmos os métodos ou classes gerenciados pelo Spring que precisam ser transacionais, como no código a seguir:
@Service
public class ProdutoService {
@PersistenceContext
private EntityManager manager;
@Transactional
public void salva(Produto produto) {
manager.persist(produto);
}
@Transactional(readOnly = true)
public Produto busca(Integer id) {
return manager.find(Produto.class, id);
}
}
Apesar de usarmos anotação nesta abordagem, o Spring também se utiliza de AOP para aplicar o controle transacional nos métodos. No entanto, nos preocupamos apenas em anotar os métodos ou classes e não com regras globais em XML.
Prós e contras de cada abordagem
Ambas as abordagens são simples e tornam o código limpo ao trabalharmos com persistência de dados. Apesar disso, cada uma apresenta vantagens e desvantagens que devem ser levadas em consideração antes de adota-las, se possível, no início do projeto. Por esse motivo, podemos destacar algumas delas abaixo:
Usando AOP diretamente
- O código Java fica mais limpo pois o controle transacional está declarado fora do código;
- Todo o controle transacional está centralizado dentro de um único local, normalmente o
applicationContext.xml
; - Não é necessário recompilar o código para modificar o gerenciamento das transações;
- Desenvolvedores precisam entender e dominar um pouco AOP para modificar ou criar novas regras;
- Por o controle estar fora do código Java, pode ficar difícil para os desenvolvedores entenderem quais métodos são transacionais ou não;
- A simples mudança de nome de métodos ou pacotes durante um refactoring pode tirar ou colocar um método sob o controle transacional;
- Mesmo pequenas modificações nas regras podem impactar toda a aplicação, trazendo bugs e problemas difíceis de detectar;
- Por as regras serem aplicadas de forma global, é preciso rodar testes de regressão sempre que uma regra é modificada;
- Ter um controle transacional mais fino além de requerer mais XML, também requer um entendimento de todas as regras;
Usando a anotação @Transactional
- Por o controle ser aplicado diretamente no código, temos uma melhor legibilidade do código e do gerenciamento das transações;
- Desenvolvedores estarão cientes sobre o fluxo transacional das operações enquanto modificam ou refatoram o código;
- Novos métodos criados e/ou anotados com
@Transactional
dificilmente impactarão no restante dos métodos da aplicação; - Muito simples ter um controle fino das transações;
- É uma abordagem invasiva, pois precisamos anotar os métodos com uma anotação do Spring;
- É necessário recompilar o código ao mudar as regras transacionais;
Os prós de uma abordagem são os contras da outra. Por esse motivo, decidir entre qual usar nem sempre será uma decisão fácil, pois depende da aplicação e da experiência da equipe. A abordagem via XML nos permite uma controle global e transparente, o que é ótimo para códigos genéricos e padronizados, como CRUDs. Por outro lado, a abordagem via anotação nos dá simplicidade e controle fino do fluxo das transações, o que é muito útil quando precisamos lidar com fluxos complexos.
No geral, nós da TriadWorks recomendamos o controle transacional via @Transactional
, pois acreditamos que ter regras explícitas desta natureza são melhores entendidas e trabalhadas pela equipe, além de motivar cada desenvolvedor a entender os fluxos de uma unidade de trabalho dentro do sistema.
No curso de JSF 2, PrimeFaces e Spring o aluno aprende como configurar e aplicar o controle transacional declarativo com Spring através da anotação @Transactional
, além de entender detalhes importantes para ter um controle fino das transações.
Trabalhando com as duas abordagens ao mesmo tempo
Tentar usar as duas abordagens ao mesmo tempo pode trazer problemas e bugs difíceis de detectar, pois podem haver conflitos nas regras globais contidas no XML e nas regras da anotação. Infelizmente não há uma maneira padrão no Spring de dizer qual abordagem terá prioridade sobre a outra.
No entanto, com um pouco de conhecimento de aspectos nós podemos tirar proveito do Spring AOP. Bastaria declarar um pointcut que simplesmente não aplique as regras globais em métodos anotados com @Transactional
, como a seguir:
<aop:pointcut id="serviceMethods"
expression="execution(* br.com.triadworks.myapp.service.*.*(..)) and not @annotation(org.springframework.transaction.annotation.Transactional)" />
Dessa forma, aplicamos as regras globais em todos os métodos do pacote de serviços que não possuem a anotação @Transactional
. No fim, temos todo o poder e transparência da abordagem XML sem abrir mão do controle fino da anotação @Transactional
.
E você, que abordagem tem utilizado para fazer o controle transacional na sua aplicação?
You might also be interested in these articles...
Desenvolvedor e instrutor na TriadWorks