Transactions com Bean Managed

Olá,

 Estou trabalhando em um projeto J2EE e estou usando EJB com BMP, o servidor é JBoss.
 A duvida é em relação as transações com Entity Beans, vejam o cenário :
 Tenho um EJB session ( stateful ) que faz o papel de Facade, e alguns Entity Beans BMP. Em uma chamada de inclusão por exemplo, o facade vai lidar com alguns beans de entidade, os quais devem participar da mesma transação. Como quem marca o inicio da transação é o container, eu não tenho como fazer um rollback quando algo dá errado.
 Como funciona esse esquema de marcação de inicio de transação e quando ela termina ?
 Se eu estiver atualizando 5 entity beans e o 5º lançar uma Exception, o container vai executar um rollback na conexão ?
 Como configurar o JBoss para que ele se comporte como no classico exemplo de transferencia bancaria ? ( o famoso ACID  )
 Qualquer ajuda é benvinda.

se voce estiver usando Container managed Transactions, o container faz o rollback para voce. Se ele vai fazer ou nao, sao 2 casos:

  • Se a exception eh um exception “esperada”, uma application exception, do tipo FinderException, CreateException ou alguma outra checked Exception, ele NAO vai setar a flag de rollback, porque ele acha que ainda tem salvacao. Se voce precisar que ele realmente volte atras, voce precisa chamat o contextDoSeuEJB.setRollbackOnly()

  • Se a exception for Runtime, ou for RemoteException, ou EJBException (que eh runtime tb), ele SEMPRE marca para rollback, pois o problema foi provavelmente no middleware, e voce nao vai conseguir resolver.

Gostaria de partilhar um pouco minha experiencia com statefull & transcoes. Nao tenho muito conhecimento de BMP, mas ja user CMP’s com stateful session bem.

Uma coisa que achei altamente interessante eh trocar do Container Managed Transaction para o Bean Managed Transaction. Quando isto eh feito num Statefull Session Bean, a transacao vai durar por toda a vida do Statefull. Ou seja, um cliente web pode criar um session statefull, e esse statefull pega uma transacao e em uma chamada a um metodo comecar uma transacao.
Todas as outras chamadas ao mesmo statefull session bean ficam na mesma transacao. Apos uma ultima, chamada, pode se dar um commit ou rollback

Eh claro, esse recurso tem que ser usado com cuidado, a fim de nao gerar dead locks.

O esquema de transação gerenciada por bean parece interessante para alguns casos. O que eu gostaria de saber, e que ainda não vi em livros sobre EJB, é onde se inicializa a transação. Seria na chamada de cada metodo de negocio do Facade ?
Tirei isso da documentação de EntityContext.setRollBackOnly();

"Mark the current transaction for rollback. The transaction will become permanently marked for rollback. A transaction marked for rollback can never commit. Only enterprise beans with container-managed transactions are allowed to use this method."

 Entre outras coisas, diz que uma transação marcada como rollback only nunca pode ser "commitada", mas , pelo menos no JDBC sempre precisamos executar Connection.commit() para fechar uma transação.
 Quando o container faz isso ? ( se é que ele faz ), pois como estamos lidando com BMP, quem cria o objeto de conexão somos nós, como seria isso ?
 Desculpem pelos muitos "como isso funciona", é que quando nos inicializamos neste ambiente, muitas coisas que outrora eram verdades consolidadas, agora nem sempre.
 Obrigado pela ajuda.

Ok, vamos por partes.
O fato de voce estar usando BMP, acredito que nao faca diferenca. Acredito que o funcionamento seja semelhante aos CMP, quanto a transacao.

O melhor lugar para voce iniciar a transacao eh realmente no facade, no seu session bean. Isso dara mais performance. Caso voce nao inicie a transacao no no session, cada chamada para o entity bean criara uma nova transacao.

Sobre transacao nos containers voce tem duas opcoes: a Containter Managed Transaction (CMT) e Bean Managed Transaction (BMT). Cada uma tem suas peculiaridades. O setRollBackOnly que voce citou, por exemplo, eh uma pertencente ao CMT. Ja no BMT voce usa todo o potencial da JTA.

O CMT eh um sistema de transacao declarativa, voce seta as caracteristicas da transacao no xml de deploy (ejb-jar.xml). Ja no JTA voce tem todo o poder de dar um begin/commit/rollback onde quiser. Ja no CMT voce sempre esta preso a granularidade de metodo. Voce inicia a transacao no inicio do metodo e commit ou cancela ao fim do metodo.

No CMT, se voce quiser fazer duas transacoes independentes no mesmo metodo nao da. Nao tem como dar um commit numa transacao , startar outra. Alem disso se voce quiser que uma transacao perdure entre metodos tambem eh impossivel com o CMT.

TOdas essas limitacoes descritas do CMT nao ocorrem no BMT. No BMT voce requisita uma transacao ao container, pode armazenar essa transacao em variaveis da sua classe (ja que estas usando Statefull) e reutiliza-la entre metodos. Vou dar um exemplo, sem me preocupar com as excessoes que ele pode gerar:

public class Statefull extends SessionBean {
  transient private UserTransaction utx;
  public void ejbCreate() {
    utx = context.getUserTransaction();
  }
  public void acao1() {
    // Acoes nos entity
  }
  public void acao2() {
    // Acoes nos entity
  }
  public void commit() {
    utx.commit();
  }
  public void rollback() {
    utx.rollback();
  }
}

La da parte web da sua aplicacao voce poderia criar esse Statefull e chamar quantas vezes quiser a acao1, acao2, nas ordens que voce quiser e por fim dar um commit ou rollback. Tudo estaria na mesma transacao.
Isso pode ate nao ser uma boa arquitetura de software, pode gerar dead locks, se o commit/rollback for “esquecido” pela parte web, mas eh uma possibilidade que o J2EE permite.

No seu post voce comentou sobre o commit. o commit nao eh nada obrigatorio no JDBC. se voce realiza somente leitura, consulta, nao precisa dar commit nem rollback.

Resumindo: BMT te da todo o poder que voce possa querer. Ja no CMT, o container tipo, bota no inicio do seu metodo um begin, e no fim do metodo ele ve. Foi setado o setRollBackOnly? Se sim, da rollback, se nao da commit. Para isso o metodo precisa ser declarado como um metodo transacional do tipo required, mandatory, ai eh escolher o que tu queres.

se ainda tiver duvidas eh soh dar um toque.

Tome cuidado, pois no caso do Stateful, o objeto de transacão não pode ser transient.

[]'s

Estive lendo a especificação EJB 2.0 na parte que lida com transações e vi que entity beans não podem ser BMT.
No meu caso eu acho mais interessante o uso de BMT, sendo assim, eu deveria configurar estes entity beans para pegar a transação corrente, ou seja , a que foi inicializada pelo client, que no meu caso vai ser um facade ( Stateful session ).
A duvida é qual configuração usar, pois pelo que vi poderia usar tanto “required” quanto “mandatory”, mas qual devo usar ?
Vejam o exemplo :

 facadeBusinessMethod(){
      ut = beanContext.getUserTransaction();
      ut.begin();
      entityHome.create(... );
      obj = entityHome.findByblabla();
      obj.setValueObject( ... );
      ut.commit();
 }

 usando uma das configurações que citei acima,  os entity beans que estão entre o begin e o commit estariam sujeitos a mesma transação ?
 Se for isso, devo chamar "setAutoCommit( false )" no objeto de conexão que faço uso dentro dos entity beans ?
 Como definir no deployment descriptor se um bean vai ser BMT ou CMT ?

 Agradeço a ajuda.

Realmente, entity beans não podem ser BMT, o EJB Container fica a cargo de executar e controlar as transações dentro do ciclo de vida da entidade.

Tanto SessionBeans como EntityBeans podem ser CMT (Recomendável)

Atributos:

Never - este método nunca pode ser chamado dentro de uma transação, se acontecer, gerará excessão.

NotSupport - este método será executado fora do contexto da transação, podendo fazer alterações ou leituras indesejáveis.

Support - depende do chamador. Se o chamador estiver dentro de um contexto transacional, fará parte da transação, caso contrário será executado fora do contexto de uma transação, podendo fazer alterações ou leituras indesejáveis.

Required - se não houver um contexto transacional, será criado um a partir deste método. Se já houver, este método será incluido dentro do contexto transacional do chamador.

RequiresNew - se não houver um contexto transacional, será criado um a partir deste método. Se já houver, este método criará outro contexto transacional independente do primeiro. Cuidado pois o uso desse método pode ferir o modelo ACID.

Mandatory - se não houver um contexto transacional, será gerada uma excessão. Se houver um contexto transacional, este método será incluido dentro deste contexto.

Conclusão.

Se SessionBeans (BMT ou CMT) acessam EntityBeans (CMT) é interessante que se mantenha nos EntityBeans atributos de transação Required, NotSupport ou Mandatory, e nos SessionBeans sempre Required.

Se SessionBeans (BMT ou CMT) acessam EntityBeans (CMT) e MessageDrivenBeans (BMT ou CMT) devemos manter o mesmo modelo.

Entretando se existe alguma transação que deve ser executada mesmo se sua chamadora for marcada para RollBack, usamos neste Bean o atributo RequiresNew.

Não recomendo serializar, nem aumentar o clico de vida das transações usando SessionBeans Stateful como foi mostrado neste POST, pois degradará muito a performance. É melhor ter-se métodos atômicos e pontuais que usem essas transações, e reservar seu uso somente para atualizações de dados.

Vale reforçar que antes de iniciar uma transação o EJBContainer vai chamar o ejbLoad() de cada Entity envolvido e ao final dela, se acontecer o commit, será executado o ejbStore() de cada um deles.

Bom estudo.

Esse esquema que vc disse é interessante e acho que vou usar, dessa forma quem vai inicializar e finalizar as transações será o facade, os entity beans vão sempre participar da transação do facade, dessa forma nem preciso usar JTA diretamente como estava pretendendo.
Vou colocar o facade como Required e os entity como Mandatory.
Valeu muito pelas dicas e obrigado a todos que participaram do POST.