Spring Framework: Controle de Transações usando Anotações
Veja neste artigo como realizar o controle de transações usando o Spring Framework.
Neste artigo iremos apresentar o uso de Transações no Spring Framework, para tornar a aplicação mais segura e sem inconsistências. Mas antes de dar inicio a transações no Spring, vamos primeiramente entender o que são transações.
O conceito de transação pode ser descrito com a sigla ACID abaixo:
Atomicidade: A transação deve ser tratada como uma operação singular, isso significa que todos os comandos presentes neste transação podem ser concluídos com sucesso ou sem sucesso. Nada será salvo se pelo menos 1 comando de 100 der errado, todos devem executar com sucesso, sem erros.
Consistência: Este representa a consistência de integridade da base de dados, são elas: chaves primarias únicas e etc.
Isolação: Muitas transação podem ser executadas ao mesmo tempo, a isolação garante que cada transação é isolada da outra, prevenindo corrupção de dados.
Durabilidade: Uma transação depois de persistida deve continuar persistida e não pode ser apagada por falhas no sistema.
Depois de entendido o conceito, devemos também ter em mente que há 2 formas de se trabalhar com transações em uma aplicação: Programaticamente ou Declarativamente.
Trabalhar de forma programática traz imensa flexibilidade para gerenciar suas transações, porém a manutenibilidade do código torna-se difícil e onerosa. Por outro lado, trabalhar Declarativamente é mais elegante e segue as boas práticas de programação, pois você estará separando totalmente o controle de transações das regras de negócio. Em se tratando de Spring Framework você pode trabalhar declarativamente através de XML ou Anotações, mas neste artigo daremos foco a anotações.
Transações com Spring Framework
Antes de iniciar o uso das transações você precisa dizer ao Spring que deseja realizar o controle de transações via anotações, isso é feito através de cofinguração mostrada na listagem 1 no arquivo applicationContext.xml.
Listagem 1: Habilitando transações via anotações no applicationContext.xml
Após isso você estará apto a usar a anotação @Transactional afim de definir que determinado método deverá esta dentro de uma transação.
O primeiro parâmetro encontrado é o Isolation, este pode ser definido da seguinte forma:
Listagem 2: Definindo nível de Isolação / Isolation Level
Os tipos possíveis para o isolation são:
ISOLATION_DEFAULT: Nível de isolação padrão.
ISOLATION_READ_COMMITTED: Previne apenas "dirty reads".
ISOLATION_READ_UNCOMMITED: Indica que "dirty reads", "non-repeatable reads" e "phatom reads" podem ocorrer, ou seja, não serão impedidos.
ISOLATION_REPEATABLE_READ: Previne apenas "dirty reads" e "non-repeatable reads".
ISOLATION_SERIALIZABLE: Previne "dirty reads", "non-repeatable reads" e "phatom reads".
Nosso foco não é ensinar a fundo o funcionamento de transações, apenas sua aplicação ao Spring, porém vamos explicar resumidamente esses tipos de leituras prevenidas no Isolation.
Dirty Read: ocorre quando uma transação A escreve um valor X e uma transação B ler este valor X sem a transação A ter realizado o commit ou rollback da transação. Caso a transação A de um rollback, o valor que a transação B leu torna-se inválido.
Non-repeatable read: Ocorre quando em uma mesma transação uma linha é lida duas vezes e os valores dessas duas leituras são diferentes.
Phatom Read: Ocorre quando duas querys idênticas são executadas e o resultado da segunda é diferente da primeira.
Definindo Propagation / Propagação
Um outro parâmetro muito importante a ser definido em uma transação no Spring é como a propagação dessa transação será realizada. Veja abaixo as possíveis formas de propagação:
PROPAGATION_MANDATORY: Obriga o uso de uma transação, caso não haja nenhuma transação corrente, é lançada uma exceção.
PROPAGATION_NESTED: Executa dentro de uma transação aninhada se uma transação corrente existir.
PROPAGATION_NEVER: Impede o uso de uma transação. Caso exista uma transação corrente, é lançada uma exceção.
PROPAGATION_NOT_SUPPORTED: Não usa a transação corrente. Este é executado sempre sem nenhuma transação.
PROPAGATION_REQUIRED: Usa a transação corrente se existir, caso não exista cria uma nova transação.
PROPAGATION_REQUIRES_NEW: Cria uma nova transação, se já existir uma corrente, suspende esta.
PROPAGATION_SUPPORTS: Usa a transação corrente se existir, caso contrário executa sem transação.
Ainda existe o parâmetro "readOnly" na anotação @Transactional. Este especifica que nenhuma operação de DML poderá ser executada (Insert, Delete ou Update), ou seja, apenas consultas.
Na listagem 3 definidos uma classe que utiliza o controle de transações do Spring, esta utilizará um readOnly=false e um PROPAGATION_REQUIRED, assim garantimos que sempre o método de salvar será executado dentro de uma transação.
Listagem 3: Usando @Transactional na prática
@Override  //para execução deste método é requerido uma
//transação, é isso que estamos dizendo aqui
As outras anotações como @Autowired e @Repository são definições do Spring também, que não serão focadas neste artigo. Como você pode perceber no método "save" definidos a transação como PROPAGATION_REQUIRED, assim sendo se tivéssemos 10 saves dentro do save, todos deveriam obrigatoriamente ter sucesso na sua execução, caso contrário um rollback seria realizado.
Para aprofundar-se mais no controle de de transações do Spring, recomendamos a leitura no próprio site do Spring (colocado da seção links deste artigo). Lá você encontrará informações com um nível de detalhamento bem maior.
Este artigo teve como objetivo o uso prático e rápido de Transações, objetivando que ao termino deste leitura você esteja apto a utilizar esse recurso do Spring.
Chapter 9. Transaction Management - http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html