Before Java EE 7, EJBs were the only component that supported declarative transactions. This made your CDI beans a popular option when used as a glue between the JSF side and the EJB side, which was in charge to complete the transactions declaratively. So, in other words you could choose to either bloat your CDI Beans with Transactional bolierplate code, or add a component (the EJB) which should be in charge to complete the transactions.
One of the most interesting news of Java EE 7 is the addition of a transactional (@Transactional) interceptor which can be used to control transaction boundaries in a declarative way by CDI beans, but now only! Even JAX-RS or a Simple Servlet can now run declarative transactions like the EJB counterpart.
So, let’s start from Java EE 6. This is an example SessionBean that I used in my training labs to show how to persist data in a Java EE applications:
@Stateless public class ServiceBean { @Inject private Event<SimpleProperty> propEventSrc; @Inject private EntityManager em; public void put(SimpleProperty p){ try { em.persist(p); propEventSrc.fire(p); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void delete(SimpleProperty p){ Query query = em.createQuery("delete FROM SimpleProperty p where p.key='"+p.getKey()+"'"); query.executeUpdate(); propEventSrc.fire(p); } }
As you can see, this is a simple EJB that is responsible to finalize the transaction of a CDI application. You might still turn it to a CDI bean, however, you would need to bloat the complexity of your code, as for every EntityManager interaction you would need to perform the following boilerplate checklist:
- Start your transaction with: tx.begin
- EntityManager interaction
- tx.commit
- Handle errors and eventually issue tx.rollback
so in our case:
public void put(SimpleProperty p){ try { em.getTransaction().begin(); em.persist(p); propEventSrc.fire(p); em.getTransaction().commit(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw e; }
One of the benefits of Java EE 7 is that now you can apply the ease-of-use facilities of EJB also for other components like CDI. For example you can rewrite the above EJB to run exactly the same as CDI with the javax.transaction.Transactional annotation:
@Model @Transactional public class ServiceBean { ..... }
The annotation can be applied also at method level, so that you define transaction boundaries just for one particular method:
@Transactional public void put(SimpleProperty p){ try { em.getTransaction().begin(); em.persist(p); propEventSrc.fire(p); em.getTransaction().commit(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw e; }
Additionally, you can specify the policy to be used for transactional context to be used (default is SUPPORTS):
@Transactional(Transactional.TxType.REQUIRES_NEW) public void put(SimpleProperty p){ }
In the above example, if the method is called outside a transaction context, the interceptor must begin a new JTA transaction, the managed bean method execution must then continue inside this transaction context, and the transaction must be completed by the interceptor. But Transactional context is not an exclusive feature of CDI, you can apply it also to other components like REST Web services for example or a simple Servlet.
@WebServlet(name="txservlet", urlPatterns={"/txservlet"} ) public class TxServlet extends HttpServlet { @Inject private EntityManager em; @Transactional protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SimpleProperty p = new SimpleProperty(); p.setKey("mykey"); p.setValue("myvalue"); try { em.persist(p); } catch (Exception e) { e.printStackTrace(); } }
You can further refine your Transaction policy by setting the TxType
in your annotation, as in the following example:
@Transactional(TxType.REQUIRES_NEW) private void innerFunctionality() { // business logic }
Finally, it’s important to note that the TxType
declared in the influences the transaction management only when called with the managed CDI bean. If you call the method directly – without the CDI container wraps the invocation then the TxType
has no effect. See the following example:
@RequestScope public class DemoCDIBean { @Inject DemoCDIBean myBean; @Transactional(TxType.REQUIRED) public void coreMethod() { // CDI container does not wrap the invocation // no new transaction is started innerFunctionality(); // CDI container starts a new transaction // the method uses TxType.REQUIRES_NEW and is called from the CDI bean myBean.innerFunctionality(); } @Transactional(TxType.REQUIRES_NEW) private void innerFunctionality() { // business logic } }