sexta-feira, 27 de outubro de 2017

Cuidado com Try/Catch no EJB e Spring!

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;
 

           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