Rollback em Transação Gerenciada

Salvadores,

No meu sistema usando EJB 3.0 com JPA e estamos usando transação gerenciada pelo container (JTA).

Para o meu desagrado, acabei descobrindo que, se meu EJB lança uma checked exception, ou seja, uma classe que estende java.lang.Exception diretamente, ele não faz o rollback da transação.

Segundo a documentação, ela diz que para fazer o rollback automático, o EJB deve lançar uma exceção de sistema (RuntimeException).

http://java.sun.com/javaee/5/docs/tutorial/doc/bncij.html

[quote]Rolling Back a Container-Managed Transaction
There are two ways to roll back a container-managed transaction. First, if a system exception is thrown, the container will automatically roll back the transaction. Second, by invoking the setRollbackOnly method of the EJBContext interface, the bean method instructs the container to roll back the transaction. If the bean throws an application exception, the rollback is not automatic but can be initiated by a call to setRollbackOnly.[/quote]

No entanto vi que, se eu mudar mina exceção de Exception para RuntimeException, o rollback passa a funcionar.

Eu não queria fazer isto, pois minhas exceções são exceções de negócio, que deveriam ser checked exception, além do que, se eu lanço uma RuntimeException, o container fica aninhando a exceções dentro de outras (ex: EJBException), e meu mecanismo de controle de exceções fica mais complicado.

Alguém sugere algo para que eu continue a usar checked exception e que, ao ser lançada, o rollback ainda seja feito de forma automática, sem ter de chamar setRollbackOnly?

@Stateless public class MeuEJBBean implements MeuEJB { public class fazUmMonteDeCoisas() throws MinhaException { // faz várias coisas, inclusive salva no BD // dependendo de uma condição, pode lançar a MinhaException } }

Gracias

E ae Destro???

Use a annotation @ApplicationException(rollback=true) com escopo de classe na sua exception de negócio…

Muito bom, eu mesmo não conhecia isto.

Minha preocupação agora é se no caso de algum EJB intermediário (B) na transação lançar uma exceção anotada com @ApplicationException, ele faz o rollback ou ele só faz o rollback caso esta exceção seja lançada na chamada original da transação (A).

Exemplo: A --> B --> C --> [BD]

Vou fazer os testes.

nunca testei, mas acredito que ele faça o rollback de toda a transação, mesmo a mesma exception sendo lançada em [B]… Há de concordar que em se tratando de CMT as demarcações das transações não são tão explícitas… Para esse caso (não sei se ajuda, mas ae vai…) dá uma olhada na interface javax.ejb.SessionSyncronization, que na verdade é um listener do lifecycle das transações…

Voltando ao meu último post deste tópico. Se B lança uma exceção, ela faz parte da transação, mas não demarca ela toda. Ainda não terminei os testes. Falo em seguida!

Anotando a Exceção com @ApplicationException acontecem duas coisas:

Se anoto com @ApplicationException, a exceção é lançada diretamente, mas o rollback não acontece.

Se anoto com @ApplicationException(rollback=true), o rollback ocorre, mas a exceção é encapsulada algumas vezes, dentro de objetos do mesmo tipo de própria exceção. Ou seja, a cada EJB que ela passa, ela ganha um nível de encapsulamento.

Isso não ocorre se você lança diretamente uma classe que estende Exception, ou seja, ela não é encapsulada.

Estranho!

putz! vc testou com vários níveis? Ele somente não encapsula em uma EJBException?

Tive um mesmo problema!
Gostaria de deixar apenas a solução para possíveis consultas:

@ApplicationException(rollback=true)
public class MinhaClasseException extends Exception {
  ...
}
try{
     ...
} catch (EJBException e) {	
			TransactionRolledbackException ex = (TransactionRolledbackException) e.getCausedByException();
			MinhaClasseException mc = (MinhaClasseException) ex.getCause();
      ...
}