CHAPTER 9

image

Transactions

Transaction management is an important matter for enterprises. It allows applications to have consistent data and process that data in a reliable manner. Transaction management is a low-level concern that a business developer shouldn’t have to code himself. EJBs provide these services in a very simple way: either programmatically with a high level of abstraction or declaratively using metadata. Since Java EE 7, Managed Beans can also have declarative transactions in the same way.

Most of an enterprise application’s work is about managing data: storing them (typically in a database), retrieving them, processing them, and so on. Often this is done simultaneously by several applications attempting to access the same data. A database has low-level mechanisms to preserve concurrent access, such as pessimistic locking, and uses transactions to ensure that data stay in a consistent state. EJBs make use of these mechanisms.

I devote the first part of this chapter to understanding transaction management as a whole. Then I discuss the different types of transaction demarcation supported by EJBs: CMT and BMT. I finish with the new transaction interceptor that can be used by Managed Beans.

Understanding Transactions

Data are crucial for business, and they must be accurate regardless of the operations you perform and the number of applications concurrently accessing the data. A transaction is used to ensure that the data are kept in a consistent state. It represents a logical group of operations that must be performed as a single unit, also known as a unit of work. These operations can involve persisting data in one or several databases, sending messages to a MOM (message-oriented middleware), or invoking web services. Companies rely on transactions every day for their banking and e-commerce applications or business-to-business interactions with partners

These indivisible business operations are performed either sequentially or in parallel over a relatively short period of time. Every operation must succeed in order for the transaction to succeed (we say that the transaction is committed). If one of the operations fails, the transaction fails as well (the transaction is rolled back). Transactions must guarantee a degree of reliability and robustness and follow the ACID properties.

ACID

ACID refers to the four properties that define a reliable transaction: Atomicity, Consistency, Isolation, and Durability (described in Table 9-1). To explain these properties, I’ll take the classical example of a banking transfer: you need to debit your savings account to credit your current account.

Table 9-1. ACID Properties

Property Description
Atomicity A transaction is composed of one or more operations grouped in a unit of work. At the conclusion of the transaction, either these operations are all performed successfully (commit) or none of them is performed at all (rollback) if something unexpected or irrecoverable happens.
Consistency At the conclusion of the transaction, the data are left in a consistent state.
Isolation The intermediate state of a transaction is not visible to external applications.
Durability Once the transaction is committed, the changes made to the data are visible to other applications.

When you transfer money from one account to the other, you can imagine a sequence of database accesses: the savings account is debited using a SQL update statement, the current account is credited using a different update statement, and a log is created in a different table to keep track of the transfer. These operations have to be done in the same unit of work (Atomicity) because you don’t want the debit to occur but not the credit. From the perspective of an external application querying the accounts, only when both operations have been successfully performed are they visible (Isolation). With isolation, the external application cannot see the interim state when one account has been debited and the other is still not credited (if it could, it would think the user has less money than she really does). Consistency is when transaction operations (either with a commit or a rollback) are performed within the constraints of the database (such as primary keys, relationships, or fields). Once the transfer is completed, the data can be accessed from other applications (Durability).

Read Conditions

Transaction isolation can be defined using different read conditions (dirty reads, repeatable reads, and phantom reads). They describe what can happen when two or more transactions operate on the same data at the same time. Depending on the level of isolation you put in place you can totally avoid or allow concurrent access.

  • Dirty reads: Occur when a transaction reads uncommitted changes made by the previous transaction.
  • Repeatable reads: Occur when the data read are guaranteed to look the same if read again during the same transaction.
  • Phantom reads: Occur when new records added to the database are detectable by transactions that started prior to the insert. Queries will include records added by other transactions after their transaction has started.

Transaction Isolation Levels

Databases use several different locking techniques to control how transactions access data concurrently. Locking mechanisms impact the read condition described previously. Isolation levels are commonly used in databases to describe how locking is applied to data within a transaction. The four types of isolation levels are

  • Read uncommitted (less restrictive isolation level): The transaction can read uncommitted data. Dirty, nonrepeatable, and phantom reads can occur.
  • Read committed: The transaction cannot read uncommitted data. Dirty reads are prevented but not nonrepeatable or phantom reads.
  • Repeatable read: The transaction cannot change data that are being read by a different transaction. Dirty and nonrepeatable reads are prevented but phantom reads can occur
  • Serializable (most restrictive isolation level): The transaction has exclusive read. The other transactions can neither read nor write the same data.

Generally speaking, as the isolation level becomes more restrictive the performance of the system decreases because transactions are prevented from accessing the same data. However, the  isolation level enforces data consistency. Note that not all RDBMS (Relational Database Management Systems) implement these four isolation levels.

JTA Local Transactions

Several components have to be in place for transactions to work and follow the ACID properties. Let’s first take the simplest example of an application performing several changes to a single resource (e.g., a database). When there is only one transactional resource, all that is needed is a local JTA transaction. A resource local transaction is a transaction that you have with a specific single resource using its own specific API. Figure 9-1 shows the application interacting with a resource through a transaction manager and a resource manager.

9781430246268_Fig09-01.jpg

Figure 9-1. A transaction involving one resource

The components shown in Figure 9-1 abstract most of the transaction-specific processing from the application.

  • The transaction manager is the core component responsible for managing the transactional operations. It creates the transactions on behalf of the application, informs the resource manager that it is participating in a transaction (an operation known as enlistment), and conducts the commit or rollback on the resource manager.
  • The resource manager is responsible for managing resources and registering them with the transaction manager. An example of a resource manager is a driver for a relational database, a JMS resource, or a Java connector.
  • The resource is the persistent storage from which you read or write (a database, a message destination, etc.).

It is not the application’s responsibility to preserve ACID properties. The application just decides to either commit or roll back the transaction, and the transaction manager prepares all the resources to successfully make it happen.

Distributed Transactions and XA

As you’ve just seen, a transaction using a single resource (shown previously in Figure 9-1) is called a JTA local transaction. However, many enterprise applications use more than one resource. Returning to the example of the fund transfer, the savings account and the current account could be in separate databases. You would then need transaction management across several resources, or resources that are distributed across the network. Such enterprise-wide transactions require special coordination involving XA and the Java Transaction Service (JTS).

Figure 9-2 shows an application that uses transaction demarcation across several resources. This means that, in the same unit of work, the application can persist data in a database and send a JMS message, for example.

9781430246268_Fig09-02.jpg

Figure 9-2. An XA transaction involving two resources

To have a reliable transaction across several resources, the transaction manager needs to use an XA (eXtended Architecture) resource manager interface. XA is a standard specified by the Open Group (www.opengroup.org) for distributed transaction processing (DTP) that preserves the ACID properties. It is supported by JTA and allows heterogeneous resource managers from different vendors to interoperate through a common interface. XA uses a two-phase commit (2pc) to ensure that all resources either commit or roll back any particular transaction simultaneously.

In our fund transfer example, suppose that the savings account is debited on a first database, and the transaction commits successfully. Then the current account is credited on a second database, but the transaction fails. We would have to go back to the first database and undo the committed changes. To avoid this data inconsistency problem, the two-phase commit performs an additional preparatory step before the final commit as shown in Figure 9-3. During phase 1, each resource manager is notified through a “prepare” command that a commit is about to be issued. This allows the resource managers to declare whether they can apply their changes or not. If they all indicate that they are prepared, the transaction is allowed to proceed, and all resource managers are asked to commit in the second phase.

9781430246268_Fig09-03.jpg

Figure 9-3. Two-phase commit

Most of the time, the resources are distributed across the network (see Figure 9-4). Such a system relies on JTS. JTS implements the Object Management Group (OMG) Object Transaction Service (OTS) specification, allowing transaction managers to participate in distributed transactions through Internet Inter-ORB Protocol (IIOP). Compared to Figure 9-2, where there is only one transaction manager, Figure 9-4 allows the propagation of distributed transactions using IIOP. This allows transactions to be distributed across different computers and different databases from different vendors. JTS is intended for vendors who provide the transaction system infrastructure. As an EJB developer, you don’t have to worry about it; just use JTA, which interfaces with JTS at a higher level.

9781430246268_Fig09-04.jpg

Figure 9-4. A distributed XA transaction

Transaction Specifications Overview

In Java EE 7, EJBs and Managed Beans handle transactions through the Java Transaction API (JTA) specified by JSR 907. JTA defines a set of interfaces for the application or the container to demarcate transactions’ boundaries, and it also defines APIs to deal with the transaction manager. The javax.transaction package defines these interfaces.

JTS is a specification for building a transaction manager which supports the JTA interfaces at the high level and the standard Java mapping of the CORBA Object Transaction Service 1.1 specification at the low level. JTS provides transaction interoperability using the CORBA standard IIOP protocol for transaction propagation between servers. JTS is intended for vendors that provide the transaction system infrastructure for enterprise middleware.

Regarding transaction management, it is unlikely that you want to use the raw JTS/JTA APIs in Java. Instead you delegate transactions to the EJB container that contains a transaction manager (which internally uses JTS and JTA).

A Brief History of JTA

JTA is the general API for managing transactions in Java EE. It allows you to start, commit, and roll back transactions in a resource neutral way. If more than one resource participates in such a transaction, JTA also allows you to do XA transactions.

JTA 1.0 was introduced in 1999 and had some maintenance releases to reach a 1.1 version in 2002. It remained unchanged for a decade and was finally updated to JTA 1.2 with Java EE 7.

What’s New in JTA 1.2?

Historically in Java EE, transactions were delegated to EJBs so developers didn’t have to use the JTA APIs directly in their code. Both JTA (JSR 907) and EJBs (JSR 345) had been around since 1999. The EJB specification kept on evolving but not JTA, which was already a pretty mature and solid specification. With Managed Bean alignment happening in Java EE 7, one of the biggest challenges was to bring transactions to Managed Beans and not only EJBs. To accomplish that, the JTA specification needed to be updated. JTA 1.2 brings support for container-managed transactions independent of EJB and a @TransactionScope annotation for CDI bean scope.

The JTA API consists of classes and interfaces grouped in two packages described in Table 9-2.

Table 9-2. Main JTA Packages

Package Description
javax.transaction Contains the core JTA APIs
javax.transaction.xa Interfaces and classes to accomplish distributed XA transactions

Reference Implementation

The reference implementation for JTA is the GlassFish Transaction Manager. It is a module of GlassFish but is not usable outside the server itself. Other open source or commercial stand-alone JTA implementations are available such as JBoss Transaction Manager, Atomikos, and Bitronix JTA.

Transaction Support in EJBs

When you develop business logic with EJBs, you don’t have to worry about the internal structure of transaction managers or resource managers because JTA abstracts most of the underlying complexity. With EJBs, you can develop a transactional application very easily, leaving the container to implement the low-level transaction protocols, such as the two-phase commit or the transaction context propagation. An EJB container is a transaction manager that supports JTA as well as JTS to participate in distributed transactions involving other EJB containers and/or other transactional resources. In a typical Java EE application, session beans establish the boundaries of a transaction, call entities to interact with the database, or send JMS messages in a transaction context.

From its creation, the EJB model was designed to manage transactions. In fact, transactions are natural to EJBs, and by default each method is automatically wrapped in a transaction. This default behavior is known as a container-managed transaction (CMT), because transactions are managed by the EJB container (a.k.a. declarative transaction demarcation). You can also choose to manage transactions yourself using bean-managed transactions (BMTs), also called programmatic transaction demarcation. Transaction demarcation determines where transactions begin and end.

Container-Managed Transactions

When managing transactions declaratively, you delegate the demarcation policy to the container. You don’t have to explicitly use JTA in your code (even if JTA is used underneath); you can leave the container to demarcate transaction boundaries by automatically beginning and committing transactions based on metadata. The EJB container provides transaction management services to session beans and MDBs (see Chapter 13 for more on MDBs). In an enterprise bean with a container-managed transaction, the EJB container sets the boundaries of the transactions.

In Chapter 7, you saw several examples of session beans, annotations, and interfaces, but never anything specific to transactions. Listing 9-1 shows the code of a stateless session bean using CMT. As you can see, there is no extra annotation added or any special interface to implement. EJBs are by nature transactional. With configuration by exception, all the transaction management defaults are applied (REQUIRED is the default transaction attribute as explained later in this section).

Listing 9-1.  A Stateless Bean with CMT

@Stateless
public class ItemEJB {
 
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em ;
  @Inject
  private InventoryEJB inventory ;
 
  public List<Book> findBooks() {
    TypedQuery<Book> query = em .createNamedQuery(FIND_ALL, Book.class);
    return query.getResultList();
  }
 
  public Book createBook(Book book) {
    em.persist(book);
    inventory.addItem(book);
    return book;
  }
}

You might ask what makes the code in Listing 9-1 transactional. The answer is the container. Figure 9-5 shows what happens when a client invokes the createBook() method. The client call is intercepted by the container, which checks immediately before invoking the method whether a transaction context is associated with the call. By default, if no transaction context is available, the container begins a new transaction before entering the method and then invokes the createBook() method. Once the method exits, the container automatically commits the transaction or rolls it back (if a particular type of exception is thrown, as you’ll see later in the “Exceptions and Transactions” section).

9781430246268_Fig09-05.jpg

Figure 9-5. The container handles the transaction

The default behavior is that whatever transaction context is used for 3:createBook() (from the client or created by the container) is applied to 4:addItem(). The final commit happens if both methods have returned successfully. This behavior can be changed using metadata (annotation or XML deployment descriptor). Depending on the transaction attribute you choose (REQUIRED, REQUIRES_NEW, SUPPORTS, MANDATORY, NOT_SUPPORTED, or NEVER), you can affect the way the container demarcates transactions: on a client invocation of a transactional method, the container uses the client’s transaction, runs the method in a new transaction, runs the method with no transaction, or throws an exception. Table 9-3 defines the transaction attributes.

Table 9-3. CMT Attributes

Attribute Description
REQUIRED This attribute (default value) means that a method must always be invoked within a transaction. The container creates a new transaction if the method is invoked from a nontransactional client. If the client has a transaction context, the business method runs within the client’s transaction. You should use REQUIRED if you are making calls that should be managed in a transaction, but you can’t assume that the client is calling the method from a transaction context.
REQUIRES_NEW The container always creates a new transaction before executing a method, regardless of whether the client is executed within a transaction. If the client is running within a transaction, the container suspends that transaction temporarily, creates a second one, commits or rolls it back, and then resumes the first transaction. This means that the success or failure of the second transaction has no effect on the existing client transaction. You should use REQUIRES_NEW when you don’t want a rollback to affect the client.
SUPPORTS The EJB method inherits the client’s transaction context. If a transaction context is available, it is used by the method; if not, the container invokes the method with no transaction context. You should use SUPPORTS when you have read-only access to the database table.
MANDATORY The container requires a transaction before invoking the business method but should not create a new one. If the client has a transaction context, it is propagated; if not, a javax.ejb.EJBTransactionRequiredException is thrown.
NOT_SUPPORTED The EJB method cannot be invoked in a transaction context. If the client has no transaction context, nothing happens; if it does, the container suspends the client’s transaction, invokes the method, and then resumes the transaction when the method returns.
NEVER The EJB method must not be invoked from a transactional client. If the client is running within a transaction context, the container throws a javax.ejb.EJBException.

Figure 9-6 illustrates all the possible behaviors that an EJB can have depending on the presence or not of a client’s transaction context. For example, if the createBook() method doesn’t have a transaction context (top part of the figure) and invokes addItem() with a MANDATORY attribute, an exception is thrown. The bottom part of Figure 9-6 shows the same combinations but with a client that has a transaction context.

9781430246268_Fig09-06.jpg

Figure 9-6. Two calls made to InventoryEJB with different transaction policies

To apply one of these six demarcation attributes to your session bean, you have to use the @javax.ejb.TransactionAttribute annotation or the deployment descriptor (setting the <trans-attribute> element in the ejb-jar.xml). These metadata can be applied either to individual methods or to the entire bean. If applied at the bean level, all business methods will inherit the bean’s transaction attribute value. Listing 9-2 shows how the ItemEJB uses a SUPPORT transaction demarcation policy and overrides the createBook() method with REQUIRED.

Listing 9-2.  A Stateless Bean with CMT

@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class ItemEJB {
 
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em;
  @Inject
  private InventoryEJB inventory;
 
  public List<Book> findBooks() {
    TypedQuery<Book> query = em.createNamedQuery(FIND_ALL, Book.class);
    return query.getResultList();
  }
 
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public Book createBook(Book book) {
    em.persist(book);
    inventory.addItem(book);
    return book;
  }
}

image Note  Client transaction context does not propagate with asynchronous method invocation (@Asynchronous). MDBs support only the REQUIRED and NOT_SUPPORTED attributes as explained in Chapter 13.

Marking a CMT for Rollback

You’ve seen that the EJB container demarcates transactions automatically and invokes begin, commit, and rollback operations on your behalf. But, as a developer, you might want to prevent the transaction from being committed if some error or business condition is encountered. It is important to stress that a CMT bean is not allowed to roll back the transaction explicitly. Instead, you need to use the EJB context (see the “Session Context” section in Chapter 7) to inform the container to roll back.

As you can see in Listing 9-3, the InventoryEJB has a oneItemSold() method that accesses the database through the persistence manager and sends a JMS message to inform the shipping company that an item has been sold and should be delivered. If the inventory level is equal to zero (which means no more items are available), the method needs to explicitly roll back the transaction. To do so, the stateless bean first needs to obtain the SessionContext through dependency injection and then call its setRollbackOnly() method. Calling this method doesn’t roll back the transaction immediately; instead, a flag is set for the container to do the actual rollback when it is time to end the transaction. Only session beans with CMT demarcation can use this method (BMT session beans roll back transactions directly, as shown in the “Bean-Managed Transactions” section).

Listing 9-3.  A Stateless Bean CMT Marks the Transaction for Rollback

@Stateless
public class InventoryEJB {
 
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em;
  @Resource
  private SessionContext ctx ;
 
  public void oneItemSold(Item item) {
    item.decreaseAvailableStock();
    sendShippingMessage();
 
    if (inventoryLevel(item) == 0)
      ctx.setRollbackOnly();
  }
}

Similarly, a bean can call the SessionContext.getRollbackOnly() method, which returns a boolean, to determine whether the current transaction has been marked for rollback.

Another way to programmatically inform the container to roll back is by throwing specific types of exceptions.

Exceptions and Transactions

Exception handling in Java has been confusing since the creation of the language (as it involves both checked exceptions and unchecked exceptions). Associating transactions and exceptions in EJBs is also quite intricate. Before going any further, I just want to say that throwing an exception in a business method will not always mark the transaction for rollback. It depends on the type of exception or the metadata defining the exception. In fact, the EJB 3.2 specification outlines two types of exceptions.

  • Application exceptions: Exceptions related to business logic handled by the EJB. For example, an application exception might be raised if invalid arguments are passed to a method, the inventory level is too low, or the credit card number is invalid. Throwing an application exception does not automatically result in marking the transaction for rollback. As detailed later in this section in Table 9-4, the container doesn’t roll back when checked exceptions (which extend java.lang.Exception) are thrown, but it does for unchecked exceptions (which extend RuntimeException).
  • System exceptions: Exceptions caused by system-level faults, such as JNDI errors, JVM errors, failure to acquire a database connection, and so on. A system exception must be a subclass of a RuntimeException or java.rmi.RemoteException (and therefore a subclass of javax.ejb.EJBException). Throwing a system exception results in marking the transaction for rollback.

With this definition, we know now that if the container detects a system exception, such as an ArithmeticException, ClassCastException, IllegalArgumentException, or NullPointerException, it will roll back the transaction. Application exceptions depend on various factors. As an example, let’s change the code from Listing 9-3 and use an application exception as shown in Listing 9-4.

Listing 9-4.  A Stateless Bean Throwing an Application Exception

@Stateless
public class InventoryEJB {
 
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em;
 
  public void oneItemSold(Item item) throws InventoryLevelTooLowException{
    item.decreaseAvailableStock();
    sendShippingMessage();
 
    if (inventoryLevel(item) == 0)
      throw new InventoryLevelTooLowException();
  }
}

InventoryLevelTooLowException is an application exception because it’s related to the business logic of the oneItemSold() method. It is then a business concern to know whether you want to roll back the transaction or not. An application exception is one that extends from a checked or unchecked exception and is annotated with @javax.ejb.ApplicationException (or the XML equivalent in the deployment descriptor). This annotation has a rollback element that can be set to true to explicitly roll back the transaction. Listing 9-5 shows the InventoryLevelTooLowException as an annotated checked exception.

Listing 9-5.  An Application Exception with rollback = true

@ApplicationException(rollback = true)
public class InventoryLevelTooLowException extends Exception {
 
  public InventoryLevelTooLowException() { }
 
  public InventoryLevelTooLowException(String message) {
    super(message);
  }
}

If the InventoryEJB in Listing 9-4 throws the exception defined in Listing 9-5, it will mark the transaction for rollback, and the container will do the actual rollback when it is time to end the transaction. That’s because the InventoryLevelTooLowException is annotated with @ApplicationException(rollback = true). Table 9-4 shows all the possible combinations with application exceptions. The first line of the table could be interpreted as “If the application exception extends from Exception and has no @ApplicationException annotation, throwing it will not mark the transaction for rollback.”

Table 9-4. Combination of Application Exceptions

Extends from @ApplicationException The transaction is marked for rollback
Exception No annotation No
Exception rollback = true Yes
Exception rollback = false No
RuntimeException No annotation Yes
RuntimeException rollback = true Yes
RuntimeException rollback = false No

Bean-Managed Transactions

With CMT, you leave the container to do the transaction demarcation just by specifying a transaction attribute and using the session context or exceptions to mark a transaction for rollback. In some cases, the declarative CMT may not provide the demarcation granularity that you require (e.g., a method cannot generate more than one transaction). To address this issue, EJBs offer a programmatic way to manage transaction demarcations with BMT. BMT allows you to explicitly manage transaction boundaries (begin, commit, rollback) using JTA.

To turn off the default CMT demarcation and switch to BMT mode, a bean simply has to use the @javax.ejb.TransactionManagement annotation (or the XML equivalent in the ejb-jar.xml file) as follows:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class ItemEJB {...}

With BMT demarcation, the application requests the transaction, and the EJB container creates the physical transaction and takes care of a few low-level details. Also, it does not propagate transactions from one BMT to another.

The main interface used to carry out BMT is javax.transaction.UserTransaction. It allows the bean to demarcate a transaction, get its status, set a timeout, and so on. The UserTransaction is instantiated by the EJB container and made available through dependency injection, JNDI lookup, or the SessionContext (with the SessionContext.getUserTransaction() method). Table 9-5 describes the API.

Table 9-5. Methods of the javax.transaction.UserTransaction Interface

Method Description
begin Begins a new transaction and associates it with the current thread
commit Commits the transaction attached to the current thread
rollback Rolls back the transaction attached to the current thread
setRollbackOnly Marks the current transaction for rollback
getStatus Obtains the status of the current transaction
setTransactionTimeout Modifies the timeout for the current transactions

Listing 9-6 shows how to develop a BMT bean. First of all, we get a reference of the UserTransaction using injection through the @Resource annotation. The oneItemSold() method begins the transaction, does some business processing, and then, depending on some business logic, commits or rolls back the transaction. Notice also that the transaction is marked for rollback in the catch block (I’ve simplified exception handling for better readability).

Listing 9-6.  A Stateless Bean with BMT

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class InventoryEJB {
 
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em;
  @Resource
  private UserTransaction ut;
 
  public void oneItemSold(Item item) {
    try {
      ut.begin();
 
      item.decreaseAvailableStock();
      sendShippingMessage();
 
      if (inventoryLevel(item) == 0)
        ut.rollback();
      else
        ut.commit();
 
    } catch (Exception e) {
      ut.rollback();
    }
    sendInventoryAlert();
  }
}

The difference with the CMT code shown in Listing 9-3 is that with CMT the container starts the transaction before the method execution and commits it immediately after. With the BMT code shown in Listing 9-6, you manually define transaction boundaries inside the method itself.

Transaction Support in Managed Beans

As you’ve just seen, CMT are one of the original ease-of-use facilities of EJB. With declarative transaction on enterprise bean classes or methods, the container can create new transactions (REQUIRED, REQUIRES_NEW), inherit from existing ones (SUPPORTS), or throw an exception if the transaction has not been already created (MANDATORY). That’s because the container intercepts the corresponding method calls and interposes the necessary operations to initiate, suspend, or complete JTA transactions.

As part of better aligning Managed Beans across the platform, one of the improvements of Java EE 7 is the extension of CMT beyond EJBs. This was made possible thanks to interceptors and interceptor binding (see Chapter 2). Transaction management on Managed Beans has been implemented using a CDI interceptor binding as defined in Listing 9-7.

Listing 9-7.  The @javax.transaction.Transactional Interceptor Binding

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Transactional {
 
  TxType value() default TxType.REQUIRED ;
  Class[] rollbackOn () default {};
  Class[] dontRollbackOn () default {};
 
  public enum TxType {
    REQUIRED,
    REQUIRES_NEW,
    MANDATORY,
    SUPPORTS,
    NOT_SUPPORTED,
    NEVER
  }
}

The javax.transaction.Transactional annotation (Listing 9-7) provides the application with the ability to control transaction boundaries on CDI Managed Beans, as well as Servlets, JAX-RS, and JAX-WS endpoints declaratively. This provides the semantics of EJB transaction attributes in CDI without dependencies on other EJB services such as RMI or timer service.

For example, Listing 9-8 shows a JAX-RS web service (more on that in Chapter 15) using the @Transactional annotation so it can use the EntityManager to persist books to the database. This code wasn’t possible until Java EE 7. You had to either annotate the RESTful web service with @Stateless or delegate the persistence to a session bean layer.

Listing 9-8.  A Book RESTful Web Service Creating Books with Transactions

@Path("book")
@ Transactional
public class BookRestService {
 
  @Context
  private UriInfo uriInfo;
  @PersistenceContext(unitName = "chapter09PU")
  private EntityManager em ;
 
  @POST
  @Consumes(MediaType.APPLICATION_XML)
  public Response createBook(Book book) {
    em.persist (book);
    URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId().toString()).build();
    return Response.created(bookUri).build();
  }
 
  @GET
  @Produces(MediaType.APPLICATION_XML)
  @ Transactional(Transactional.TxType.SUPPORTS)
  public Books getAllBooks() {
    TypedQuery<Book> query = em.createNamedQuery(Book.FIND_ALL, Book.class);
    Books books = new Books(query.getResultList());
    return books;
  }
}

Like EJBs you can also change the default transactional policy (e.g., SUPPORTS) to a different one just by using the annotation attributes. As shown in Listing 9-8 the @Transactional annotation can be put on a class and/or on a method.

Exceptions and Transactions

Exceptions and transactions are slightly different in Managed Beans than in EJBs. As in EJBs, they use the same default exception handling: application exceptions (i.e., checked exceptions) do not result in the transactional interceptor marking the transaction for rollback, and system exceptions (i.e., unchecked exceptions) do. But this default behavior can be overridden in the @Transactional annotation using the rollbackOn and dontRollbackOn attributes. When you specify a class for either of these attributes, the designated behavior applies to subclasses of that class as well. If you specify both attributes, dontRollbackOn takes precedence.

To override the default behavior and cause transactions to be marked for rollback for all application exceptions you need to write the following:

@Transactional(rollbackOn={Exception.class})

On the other hand, if you want to prevent transactions from being marked for rollback by the interceptor when an IllegalStateException (unchecked exception) or any of its subclasses is caught, you can use the dontRollbackOn as follows:

@Transactional(dontRollbackOn={IllegalStateException.class})

You can use both attributes to refine the transactional behavior. Each attribute takes an array of classes and can be used as follows:

@Transactional(rollbackOn={SQLException.class},
           dontRollbackOn={SQLWarning.class, ArrayIndexOutOfBoundsException.class})

image Note  There is no special, built-in knowledge about EJB application exceptions (i.e., exceptions annotated with @ApplicationException). As far as the interceptor is concerned, these would be treated just as any other exceptions unless otherwise specified with the rollbackOn and dontRollbackOn attributes.

Summary

In this chapter, I have shown you how to handle transactions within EJBs and Managed Beans. You can define transaction management either declaratively or programmatically.

Transactions allow the business tier to keep the data in an accurate state even when accessed concurrently by several applications. They follow the ACID properties and can be distributed across several resources (databases, JMS destinations, web services, etc.). CMT allows you to easily customize the EJB container behavior in terms of transaction demarcation. You can influence this behavior by marking a transaction for rollback through the EJB context or exceptions. You can always use BMT if you need finer control of the transaction demarcation directly using JTA. A novelty in Java EE 7 is to bring these concepts to other components such as Servlets or Web Services thanks to CDI interceptor binding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.69.163