Java EE CDI TransactionScoped example

05 February 2014
By Gonçalo Marques
In this article we will cover the Java EE CDI TransactionScoped scope.

Introduction

Java EE 7 introduced a new set of CDI bean scopes. One of them - and the one we will cover in this article - is the transactional scope. As with other CDI beans, we configure a transactional scoped bean by using the @TransactionScoped annotation.

As the name states, a transactional scoped bean life cycle will spawn across the current transaction. As soon as a transactional scoped bean is accessed in the context of an active transaction, it will be initialized and available across the transaction life time. This means that when we inject a transactional scoped bean into distinct components that participate in a single transaction, the container will always inject the same bean instance into those components.

Transactional scoped beans may be seen as an useful way (and provided by the container) to store transactional context without having to pass it around between method invocations.

In this article we will configure a transactional scoped CDI bean and use it across multiple EJB invocations that spawn across the same transaction.

This tutorial considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Glassfish 4.0

You may find more information about other CDI bean scopes in the following article: Java EE CDI bean scopes.

TransactionScoped bean

We start by defining the CDI bean interface and implementation:

Bean interface

package com.byteslounge.bean;

import java.io.Serializable;

public interface TestTransactionalScope extends Serializable {

  long getValue();

}

Bean implementation

package com.byteslounge.bean;

import java.util.Random;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.transaction.TransactionScoped;

@TransactionScoped
public class TestTransactionalScopeBean 
              implements TestTransactionalScope {

  private static final long serialVersionUID = 1L;

  private long value;

  @PostConstruct
  private void init() {
    value = System.currentTimeMillis();
    System.out.println("TestTransactionalScopeBean initialized. Value is "
        + value);
  }

  @PreDestroy
  private void destroy() {
    System.out.println("TestTransactionalScopeBean destroyed. Value is "
        + value);
  }

  @Override
  public long getValue() {
    return value;
  }

}

We defined the bean scope as transactional by the means of the @TransactionScoped annotation, as we do with any other CDI bean scope.

The bean has a single property that will be initialized in the init() method (the bean initialization method is configured through the standard @PostConstruct annotation). The property is of type long and will be initialized with the current timestamp in milliseconds.

This timestamp will be used later in order to check that the same bean instance will be injected during the life time of a single transaction.

The EJBs

We will use a couple of EJB's which method calls will spawn across the same transaction. Both EJB's will use container managed transactions (CMT) where the first EJB to be called will start the transaction and propagate it to the second EJB.

The EJB interfaces and respective implementation follows next:

Service.java

package com.byteslounge.ejb;

import javax.ejb.Local;

@Local
public interface Service {

  void doWork();

}

ServiceBean.java

package com.byteslounge.ejb;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;

import com.byteslounge.bean.TestTransactionalScope;

@Stateless
public class ServiceBean implements Service {

  @EJB
  private NestedService nestedService;

  @Inject
  private TestTransactionalScope testTransactionalScope;

  @Override
  public void doWork() {
    System.out.println("ServiceBean::doWork() called");
    System.out.println("ServiceBean:: testTransactionalScope value is "
        + testTransactionalScope.getValue());
    nestedService.doWork();
  }

}

NestedService.java

package com.byteslounge.ejb;

import javax.ejb.Local;

@Local
public interface NestedService {

  void doWork();

}

NestedServiceBean.java

package com.byteslounge.ejb;

import javax.ejb.Stateless;
import javax.inject.Inject;

import com.byteslounge.bean.TestTransactionalScope;

@Stateless
public class NestedServiceBean implements NestedService {

  @Inject
  private TestTransactionalScope testTransactionalScope;

  @Override
  public void doWork() {
    System.out.println("NestedServiceBean::doWork() called");
    System.out
        .println("NestedServiceBean:: testTransactionalScope value is "
            + testTransactionalScope.getValue());
  }

}

As we stated before we have a couple of nested EJB calls that will spawn across the same transaction.

Both EJB's are using the transactional scoped bean we have defined earlier in this article. The transaction scoped bean will be initialized and injected into the first EJB (ServiceBean). We also inject the same transaction scoped bean into the second (nested) EJB (NestedServiceBean).

The execution will naturally flow between both EJB's and we will confirm that the transactional scoped bean timestamp value will remain the same across the transaction's lifetime : it's the same bean instance.

Testing

Now we define a simple testing servlet that will trigger the nested EJB call:

TestingServlet.java

package com.byteslounge.servlet;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.byteslounge.ejb.Service;

@WebServlet(name = "testingServlet", urlPatterns = { "/testing" })
public class TestingServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  @EJB
  private Service service;

  @Override
  protected void doGet(HttpServletRequest request,
    HttpServletResponse response) 
    throws ServletException, IOException {

    service.doWork();

  }

}

When we access the servlet the following output will be generated:

ServiceBean::doWork() called
TestTransactionalScopeBean initialized. Value is 1391629780304
ServiceBean:: testTransactionalScope value is 1391629780304
NestedServiceBean::doWork() called
NestedServiceBean:: testTransactionalScope value is 1391629780304
TestTransactionalScopeBean destroyed. Value is 1391629780304

If we access the servlet one more time the following output will be generated:

ServiceBean::doWork() called
TestTransactionalScopeBean initialized. Value is 1391629799771
ServiceBean:: testTransactionalScope value is 1391629799771
NestedServiceBean::doWork() called
NestedServiceBean:: testTransactionalScope value is 1391629799771
TestTransactionalScopeBean destroyed. Value is 1391629799771

As we can see the same transactional scoped bean instance was spawn across each of the individual nested EJB transactions triggered by the servlet. Each bean was initialized with a distinct timestamp and preserved it between EJB invocations across the same transaction.

The source code used in this article is available for download at the end of this page.

Reference

TransactionScoped (Java(TM) EE 7 Specification APIs)

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with 8+ years of experience in software development and architecture design. During this period his main focus was delivering software solutions both in banking and telecommunications area. 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.

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