Esse artigo faz parte da revista Java Magazine edição 60. Clique aqui para ler todos os artigos desta edição

AN style="FONT-SIZE: 10pt; BACKGROUND: white; COLOR: red; FONT-FAMILY: Verdana; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">

>

Gerenciando transações de forma 100% transparente

Aprenda como criar um módulo de controle transacional robusto, reutilizável e completamente transparente à aplicação

De que se trata o artigo:

Gerenciamento de transações no Hibernate utilizando AspectJ. Apresentamos como criar aspectos capazes de tratarem tudo o que diz respeito ao gerenciamento de transações sem a necessidade de alteração no código responsável pelas regras de negócio.

 

Para que serve:

Prover uma forma 100% transparente de gerenciar as transações em sistemas que utilizam o Hibernate como framework de persistência. Para isso, criamos um conjunto de classes de controle transacional reutilizáveis que podem ser facilmente adicionadas a qualquer sistema Java.

 

Em que situação o tema é útil:

As transações então presentes em grande parte dos sistemas, portanto, dispor de um componente para gerenciamento de transações que seja facilmente reutilizável agiliza bastante o desenvolvimento do software.

 

Hibernate e AspectJ:

No desenvolvimento de sistemas, é bastante comum a necessidade de implementar algum tipo de gerenciamento de transações. Esse gerenciamento pode ser feito de diversas formas, porém, grande parte delas deixa o código que o utiliza acoplado, de alguma maneira, a esse serviço. Utilizando a programação orientada a aspectos (AOP), por meio da linguagem AspectJ, isso pode ser feito de maneira transparente. Com o uso de aspectos, todo o código relativo ao controle transacional fica isolado. Só é preciso, então, informar que partes do código principal (o qual contém as regras de negócios) devem ser interceptadas pelo código dos aspectos. Isso tudo é feito sem a necessidade de alterar uma linha do código principal. Utilizando esse recurso juntamente com a API de anotações de Java, é possível criar um componente reutilizável capaz de gerenciar transações em um sistema qualquer.

 

Gerenciar transações de forma eficiente e não intrusiva é uma tarefa que ainda causa muita dor de cabeça para o desenvolvedor. Neste artigo veremos como esse problema pode ser resolvido de forma simples quando utilizamos os recursos providos pela programação orientada a aspectos.

É muito comum, no desenvolvimento de sistemas, nos depararmos com requisitos que exigem uma seqüência de operações a serem realizadas de forma atômica, ou seja, onde apenas o resultado do todo faz sentido. Nesse tipo de cenário, quando qualquer operação do conjunto falha, o conjunto inteiro deve falhar. Um exemplo clássico é o da transferência entre contas de um banco, onde as operações de débito na conta origem e crédito na conta destino devem ter sucesso por completo ou falhar por completo.

Um dos benefícios[1] do conceito de transação em banco de dados é exatamente o de prover essa capacidade de restaurar o estado anterior dos dados, caso ocorra alguma falha dentro do contexto transacional. Na realidade, isso é possível porque os dados não são persistidos, de fato, até que a transação seja confirmada através do comando commit, ou seja, enquanto a transação não for commitada esses dados ficam numa área de armazenamento temporária – o “log de transações” – que é visível apenas à sessão corrente, mas não aos demais usuários do BD.

Para gerenciar transações no Hibernate, nos é fornecida a interface org.hibernate.Transaction com métodos como beginTransaction(), commit() e rollback(). Com a ajuda desses métodos e do mecanismo de tratamento de exceções de Java é possível demarcar o escopo transacional facilmente. Vejamos na Listagem 1 como ficaria um pseudo-código onde vários métodos são chamados num contexto transacional.

 

Listagem 1. Pseudo-código: métodos sendo chamados num contexto transacional

try {

  transaction.beginTransaction();

  metodo1();

  metodo2();

  ...

  metodoN();

  transaction.commit();

} catch (Exception e) {

  transaction.rollback();

}

 

Contudo, veremos que implementar um controle transacional robusto e que não prejudique a coesão dos módulos da aplicação nem sempre é uma tarefa fácil. Neste artigo aprenderemos a implementar o gerenciamento de transações de uma forma simples, robusta, transparente e reutilizável. Para isso utilizaremos recursos da programação orientada a aspectos, através da linguagem AspectJ.

Aplicação Exemplo

Para que possamos praticar e validar nossa solução à medida que formos discutindo, este artigo será baseado numa aplicação exemplo que tratará do caso clássico do sistema bancário com suporte a transferência entre contas, uma vez que este deixa bem clara a importância de um controle transacional bem definido. No nosso sistema será possível efetuar operações de crédito, débito e transferência em contas bancárias. Vamos dar uma olhada no que seriam os principais requisitos da nossa aplicação:

1.      O sistema deve possuir operações para crédito e débito em contas bancárias;

2.      Para a operação de débito, o saldo da conta deve ser maior ou igual ao valor que se deseja debitar. Caso essa condição não seja atendida, a operação deve ser abortada e um erro deve ser gerado;

3.      O sistema deve prover a funcionalidade de transferência entre contas e esta funcionalidade deve simplesmente encapsular as operações de crédito e débito;

4.      Uma transferência só pode ser efetivada caso as operações de crédito na conta destino e débito na conta origem tenham sido realizadas com sucesso. Qualquer falha numa dessas operações deve cancelar a transferência.

 

Como podemos perceber, os itens 3 e 4 deixam clara a necessidade de um controle transacional. Mas até mesmo o item 2 exige transações. Se analisarmos a fundo seria possível, por exemplo, o sistema verificar que a conta tem um saldo, aprovando então o débito, mas na fração de segundo entre esta decisão e a efetivação do débito, outro thread ou processo poderia realizar um débito concorrente.

Entendidos os requisitos, vamos então definir nossas classes. A Figura 1 mostra o modelo de classes que utilizaremos. Como podemos observar nosso modelo será bem simples, composto por apenas três classes: a classe persistente Conta, a classe GerenciadorConta que encapsulará as regras do negócio e, por fim, a classe ContaDao, responsável por persistir os dados usando o Hibernate. Note também que, para simplificar, só definimos as operações necessárias para nosso estudo de caso; por exemplo, a classe ContaDao não possui métodos para inserir ou listar já que não precisaremos de tais métodos.

 

Figura 1. Modelo de classes da aplicação exemplo

A Listagem 2 mostra a definição da classe Conta. Note que estamos utilizando as annotations @Entity e @Id da JPA para fazer o mapeamento objeto-relacional.

 

Listagem 2. Classe persistente Conta

// Declaração de pacote e imports omitidos ...

@Entity

public class Conta implements Serializable {

...

Quer ler esse conteúdo completo? Tenha acesso completo