2007-03-26

Transactions in EJB3

A little pause in the Quest for the Java Portal. I'm creating a JavaEE application, and I got a simple problem, but not well explained on the Internet. The scenario:
  • EJB module has:
    • Some CMP beans (with @ManyToOne and @OneToMany relationships);
    • A Session Facade (stateless local bean).
  • WEB module has:
    • A jMaki-powered menu tree;
    • A JSF-managed bean (that populates the jMaki tree with a call to Session Facade).
It is very simple, but I got a ServletException (caused by a LazyInitializationException) when I try to navigate the relationships on web container.

Googleing this problem, I found that the transaction of a stateless bean (using a "requires" or "requires new" attribute) is closed after the method returns, if the container opened it. Specifically, it happens when the EntityManager is destroyed. This is obvious, but not very intuitive. Solutions:
  • Use a stateful bean. The Entity Manager will "survive" along with the session, but I think this will make the transaction survive that long, too. Not the best solution;
  • Open the transaction in web container. This is strange, but logical: if I want to access the database (with lazy fetching), I must have an active transaction. EJB container is prepared to reuse the transaction by using the @TransactionAttribute annotation (defaults to "REQUIRED"). Using the brand-new resource injection, it is a matter of creating a "@Resource UserTransaction tx" attribute on my JSF managed bean. It is not nice to inject a transaction on every bean of web container, so, you only need to create a web filter that opens before chaining the operation.
As a conclusion, my contribution to JavaEE community. If you want to use Lazy Loading on web containers, you just need to create a Filter like this one:
public class TransactionFilter implements Filter {
@Resource
private UserTransaction tx;

public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
try {
if (tx.getStatus() == STATUS_NO_TRANSACTION) {
tx.begin();
}
} catch (NotSupportedException ex) {
throw new ServletException(ex);
} catch (SystemException ex) {
throw new ServletException(ex);
}

chain.doFilter(request, response);
}

public void destroy() {
}

public void init(FilterConfig filterConfig) {
}

}

No comments: