Para entender transações, considere um exemplo cotidiano: uma transferência bancária online. Imagine que você deseja transferir R$1.000 entre contas. Isso envolve duas operações: debitar o valor da conta de origem e creditar na conta de destino. Ambas devem ocorrer com sucesso ou nenhuma delas deve ser aplicada. Se apenas uma ocorrer, haverá perda financeira. Portanto, é essencial tratar essas operações como uma unidade indivisível.
Transações são sequências lógicas de ações que garantem a integridade dos dados. No desenvolvimento de aplicações corporativas, o gerenciamento de transações assegura que o sistema permaneça em um estado consistente mesmo em caso de falhas.
As transações seguem quatro propriedades conhecidas como ACID:
- Atomicidade: Todas as operações dentro de uma transação são executadas ou nenhuma é efetivada.
- Consistência: Após a conclusão, o sistema mantém regras de negócio válidas.
- Isolamento: Transações concorrentes não interferem umas nas outras.
- Durabilidade: Resultados de transações confirmadas persistem mesmo após falhas do sistema.
O Spring Framework oferece mecanismos para gerenciar transações, centralizados no TransactionManager.
Níveis de Isolamento
O isolamento define como transações interagem em ambiente concorrente. A interface TransactionDefinition inclui:
- ISOLATION_DEFAULT: Utiliza o nível padrão do banco de dados, geralmente
READ_COMMITTED. - ISOLATION_READ_UNCOMMITTED: Permite leitura de dados não confirmados, sujeito a inconsistências. Raramente aplicado.
- ISOLATION_READ_COMMITTED: Previne leituras sujas, recomendado para maioria dos cenários.
- ISOLATION_REPEATABLE_READ: Garante consistência em consultas repetidas.
- ISOLATION_SERIALIZABLE: Oferece isolamento completo, mas com alto custo de desempenho.
Comportamentos de Propagação
A propagação define a interação com transações existentes. Constantes em TransactionDefinition:
- PROPAGATION_REQUIRED: Junta-se a transação existente ou cria uma nova.
- PROPAGATION_REQUIRES_NEW: Inicia uma nova transação, suspendendo a atual.
- PROPAGATION_SUPPORTS: Participa de transação existente ou executa sem ela.
- PROPAGATION_NOT_SUPPORTED: Executa sem transação, suspendendo a atual.
- PROPAGATION_NEVER: Rejeita transações existentes.
- PROPAGATION_MANDATORY: Exige uma transação existente.
- PROPAGATION_NESTED: Cria transação aninhada dentro de uma existente.
Timeout e Somente Leitura
Timeout define o tempo limite para uma transação; após expirar, ela é revertida. O padrão usa configurações do sistema subjacente.
Somente leitura otimiza operações que apenas consultam dados.
Abordagens no Spring
Gerenciamento Programático
Utiliza diretamente TransactionTemplate ou PlatformTransactionManager no código.
Gerenciamento Declarativo
Baseado em AOP, aplica transações via configuração ou anotações, separando a lógica de negócio do controle transacional.
Comparação
O declarativo é mais simples e menos intrusivo, mas a granularidade é limitada ao nível de método.
Configuração Declarativa
Via XML
Configurar o gerenciador de transações:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Adicionar namespaces:
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
Definir atriubtos da transação:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="executarPedido" propagation="REQUIRED"/>
<tx:method name="processarPagamento" propagation="REQUIRED"/>
<tx:method name="listar*" read-only="true"/>
<tx:method name="buscar*" read-only="true"/>
</tx:attributes>
</tx:advice>
Configurar pointcut:
<aop:config>
<aop:pointcut expression="execution(* com.example.servico.*.*(..))" id="txPointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
Via Anotações
Habilitar suporte a anotações no XML:
<tx:annotation-driven transaction-manager="transactionManager"/>
Aplicar @Transactional em métodos:
@Transactional
@Override
public void realizarCompra(String codigoItem, String cliente) {
double valor = estoqueDao.obterPreco(codigoItem);
estoqueDao.deduzirEstoque(codigoItem);
estoqueDao.debitarSaldo(cliente, valor);
}