Using Transactions in CDI Beans

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
  }
}
Found the article helpful? if so please follow us on Socials