Java EE EJB transaction propagation (@TransactionAttribute) tutorial

08 July 2013
By Gonçalo Marques
In this tutorial we will see the available transaction propagation behaviours provided by the EJB container using @TransactionAttribute annotation.

Introduction

While dealing with Container Managed Transactions (CMT) in EJB, the application developer is able to declaratively define how an arbitrary call to an EJB method should behave in terms of transaction propagation. In other words, the developer is able to define if a previously existing transaction can be used, or if a new transaction should be opened leaving the existing one suspended, etc.

In this tutorial we will see the distinct transaction propagation behaviour provided by an EJB container.

@TransactionAttribute annotation

The @TransactionAttribute annotation may be used both at the type level and method level. When defined at the type level it states that all methods in a given EJB should have the declared transaction propagation behaviour. When applied to a single method, only that method will have that propagation behaviour, overriding the type level definition (if any).

Type level @TransactionAttribute

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class InnerBean {

  // ...

}



Method level @TransactionAttribute

@Stateless
public class InnerBean {

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void testRequiresNew() {
    
    // ...

  }

}

REQUIRED behaviour

The REQUIRED behaviour is the default behaviour for EJB methods. This means that if we don't explicitly define the @TransactionAttribute annotation, the defined EJB method will have REQUIRED as transactional attribute value.

Outer bean

@Stateless
public class OuterBean {

  @PersistenceContext
  private EntityManager em;
  
  @EJB
  private InnerBean innerBean;
  
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void insertUser(User user){
    em.persist(user);
    try {
      innerBean.testRequired();
    } catch (TestException e) {
      // handle exception
    }
  }
  
}



Inner bean

@Stateless
public class InnerBean {

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void testRequired() throws TestException {
    throw new TestException();
  }
  
}

In this specific example, the OuterBean persists an entity - user - and then calls a method from an inner EJB: InnerBean. The inner EJB method is annotated with REQUIRED behaviour so this means that it will use the same transaction that was opened in the outer EJB. Since the inner EJB is throwing an exception that is configured to rollback existing transactions (we will also see this shortly) it will cause the transaction to completely rollback as a whole, so the entity persisted by the outer bean will not be persisted in the database.

During EJB method execution, there are a couple of exception types that cause a transaction to rollback. One is unchecked exceptions (like RuntimeException). The other are all the exceptions that are configured as application exceptions that cause transactions to rollback. We used TestException in the previous sample. This exception is defined as the following:

Application exception configured to rollback transactions

import javax.ejb.ApplicationException;

@ApplicationException(rollback=true)
public class TestException extends Exception {

  private static final long serialVersionUID = 1L;
	
  public TestException(){
    super();
  }

}

Note the @ApplicationException annotation with rollback=true. This configures the exception in cause to be considered an application exception that causes a current transaction to rollback when thrown.

REQUIRES_NEW behaviour

The REQUIRES_NEW behaviour will pause any existing transaction and will create a new one for the current method execution. The newly created transaction outcome will not affect the outer transaction outcome. The existing transaction outcome will also not impact the newly created transaction outcome: They behave independently. When the inner method completes and its transaction is also completed, the outer transaction will then be resumed.

Outer bean

@Stateless
public class OuterBean {

  @PersistenceContext
  private EntityManager em;
  
  @EJB
  private InnerBean innerBean;
  
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void insertUser(User user){
    em.persist(user);
    try {
      innerBean.testRequiresNew();
    } catch (TestException e) {
      // handle exception
    }
  }
  
}



Inner bean

@Stateless
public class InnerBean {

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void testRequiresNew() throws TestException {
    throw new TestException();
  }
  
}

The inner EJB method is annotated with REQUIRES_NEW behaviour so it will run in an newly created transaction. Meanwhile the outer bean transaction will be paused. This means that the exception being thrown inside testRequiresNew() method will cause the inner bean transaction to rollback but the outer bean transaction will remain unaffected, so the entity being persisted in the outer bean will be persisted to the database after transaction conclusion.

If there is not any opened transaction when a method annotated with REQUIRES_NEW is executed, the container will open a new one and use it.

MANDATORY behaviour

The MANDATORY behaviour states that an existing opened transaction must already exist. If it doesn't an exception will be thrown by the container.

NEVER behaviour

The NEVER behaviour states that an existing opened transaction must not already exist. If an opened transaction already exists the container will throw an exception.

NOT_SUPPORTED behaviour

The NOT_SUPPORTED behaviour will execute outside of the scope of any transaction. If an opened transaction already exists it will be paused and later resumed after method execution.

SUPPORTS behaviour

The SUPPORTS behaviour will execute in the scope of a transaction if an opened transaction already exists. If there isn't an already opened transaction the method will execute anyway but in a non-transactional way.

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: http://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: