Ao criarmos um método geralmente colocamos
blocos de código dentro de Try/Catch para podermos tratar os possíveis erros e o sistema continuar seu trabalho.
Este é talvez o principal problema de integridade e consistência de dados
quando as transações são usadas. Veja o exemplo a seguir:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public
TradeData placeTrade(TradeData trade) throws Exception {
try
{
insertTrade(trade);
updateAcct(trade);
return trade;
}
catch (Exception up) {
//log
the error
throw up;
}
}
Suponha que a conta não tenha fundos
suficientes para comprar o estoque em questão ou não está configurado para
comprar ou vender estoque ainda e lança uma exceção verificada (por exemplo,
FundsNotAvailableException
). A ordem comercial – insertTrade(trade) - persistiu no banco de dados ou a unidade lógica inteira do
trabalho foi revertida? A resposta, surpreendentemente, é que, após uma exceção
verificada (no Spring Framework ou EJB), a transação compromete qualquer
trabalho que ainda não tenha sido comitado. Isso significa que
se uma exceção verificada ocorrer durante o método updateAcct()
, a ordem comercial foi persistida, mas a conta
não foi atualizada para refletir a transação financeira.
As exceções de tempo de execução (ou seja, exceções não
verificadas) forçam automaticamente a transação EJB/Spring a
reverter completamente, mas as exceções verificadas não. Isso não é de todo ruim ou errado, pois muitas vezes precisamos tratar os erros e deixar o sistema prosseguir. Por exemplo, se em um processo, precisamos enviar um e-mail com as operações salvas no banco, mas o servidor de e-mail estiver fora, qual o mais importante? Salvar os dados ou enviar o e-mail? Então tratamos o envio do e-mail e deixamos os dados serem salvos.
Mas e quando for o caso em que precisarmos abortar tudo?
A anotação
@TransactionAttribute
encontrada na especificação EJB não inclui diretivas para
especificar o comportamento de reversão. Em vez disso, você deve usar o método SessionContext.setRollbackOnly()
para marcar a transação para rollback em um local específico, ou, o mais aconselhavel é criar uma exceção que force esse rollback com a anotação @ApplicationException
setando a propriedade
rollback = true
. Vamos ver primeiro ver como fazer a exceção funcionar apenas para um método:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public
TradeData placeTrade(TradeData trade) throws Exception {
try
{
insertTrade(trade);
updateAcct(trade);
return
trade;
}
catch (Exception up) {
//log the error
sessionCtx.setRollbackOnly();
throw up;
}
}
Uma vez que o método
setRollbackOnly()
é invocado, você não pode mudar de idéia; O único resultado
possível é reverter a transação após a conclusão do método que iniciou a
transação.Para pegar a sessão em um EJB faz-se daseguinte maneira:
@Resource
SessionContext context;
SessionContext context;
A segunda opção que é criar uma Exceção e anotá-la segue abaixo:
@ApplicationException(rollback=true)
public class RegraNegocioException extends Exception
{
...
}
Já no Spring Framework você especifica esse comportamento do rollback através do parâmetro rollbackFor na anotação @Transactional, conforme mostrado a seguir:
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public TradeData
placeTrade(TradeData trade) throws Exception {
try
{
insertTrade(trade);
updateAcct(trade);
return trade;
}
catch (Exception up) {
//log the error
throw up;
}
}
Esses são problemas difíceis de serem detectados no momento do
desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da
aplicação e o acesso ao banco de dados estão muito altos.
Mas atenção! Essa solução funciona se você tomar cuidado com as armadilhas do REQUIRED_NEW, como mostro nesse outro post:
http://olamundo-java.blogspot.com.br/2017/10/armadilhas-do-requirednew-transaction.html
Espero que esse post ajude. A fonte pela qual me baseei para escrever esse post segue abaixo:
Nenhum comentário:
Postar um comentário