Using interceptors to handle transactions

Transaction processing is used in many applications. However, the implementation of transactions can clutter up business logic. The use of declarative annotations can make transaction processing easier to use. But there are still times when programmatic transaction is necessary such as with long running transactions. When it is, interceptors can be useful.

Getting ready

The essential steps to use interceptors for handling transactions include:

  1. Creating a transaction interceptor
  2. Annotating the target class to use bean-managed transactions
  3. Annotating the target method with the transaction interceptor

    The Handling transactions manually recipe in Chapter 6 explains how to implement bean managed transactions.

How to do it...

To illustrate the use of interceptors to effect transaction processing, add a TransactionInterceptor class to the packt package. Using dependency injection, inject a UserTransaction object.

Next, add a verifyAccess method using the @AroundInvoke annotation. Within the method, begin the transaction, invoke the proceed method and then commit the transaction.

public class TransactionInterceptor {
@Resource
private UserTransaction userTransaction;@AroundInvoke
public Object verifyAccess(InvocationContext context) throws Exception {
userTransaction.begin();
System.out.println("Beginning transaction");
Object result = context.proceed();
System.out.println("Committing the transaction");
userTransaction.commit();
return result;
}
}

In the RegistrationManager class, add the @TransactionManagement annotation at the class level and specify bean-managed transactions. Also add an EntityManager instance for your application.

@TransactionManagement(TransactionManagementType.BEAN)
public class RegistrationManager {
...
@PersistenceContext(unitName = "RegistrationApplication-ejbPU")
private EntityManager entityManager;

Next, add a method called bulkRegister. The intent of this method is to provide a way of adding multiple attendees all from the same company at one time. It is passed an array of names and titles along with the company name. It then iterates through the arrays adding one attendee at a time. Add the @Interceptor annotation to the method using the transaction interceptor.

@Interceptors({TransactionInterceptor.class})
public void bulkRegister(String names[], String titles[], String company) {
for(int i=0; i<names.length; i++) {
attendeeFacade.create(new Attendee(names[i], titles[i],company));
}
}

Modify the RegistrationServlet to test the interceptor. Add two arrays for the names and titles along with a company variable. Initialize the arrays then invoke the bulkRegister method.

String names[] = {"John", "Paul", "Karen"};
interceptors, using to handle transactionsbulkRegister methodString titles[] = {"Lead", "Programmer", "Adminsitrator"};
String company = "Acme Software";
registrationManager.bulkRegister(names, titles, company);

Execute the servlet. Depending on which interceptors are in place for the application, you should see an output sequence reflecting this registration process.

INFO: Beginning transaction

INFO: InternalMethod: Invoking method: bulkRegister

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: InternalMethod: Returned from method: bulkRegister

INFO: Committing the transaction

How it works...

When the bulkRegister method was called, the verifyAccess method of the TransactionInterceptor class was called first. The method started a transaction using the begin method and then invoked the target method, bulkRegister, with the proceed method. When this method returned, the transaction was committed using the commit method.

There's more...

Transaction processing can be more complex than illustrated in the previous example. When a method is invoked, it may or may not be part of another transaction. Exceptions may be thrown which force the roll back of a transaction. These and potentially other issues must be taken into consideration when using an interceptor to handle transactions.

To illustrate the issues involved, the following table outlines the steps needed to handle container-based transactions based on the transaction attribute of the target method. Pre-processing refers to those activities which should be performed before the proceed method is executed. The post-processing activities are those which should be performed when the proceed method returns.

Transaction attribute

Pre-processing

Post-processing

Required

Create a new transaction if one is not present

Rollback the transaction and throw a TransactionRolledbackException if the transaction is marked for rollback or an exception is thrown. Otherwise commit the transaction.

Mandatory

If no transaction exists throw an exception

Rollback the transaction and throw a TransactionRolledbackException if the transaction is marked for rollback or an exception is thrown.

RequiresNew

Create a new transaction

Rollback the transaction and throw a TransactionRolledbackException if the transaction is marked for rollback or an exception is thrown. Otherwise commit the transaction.

See also

The Handling transactions manually recipe in Chapter 6 covers bean-managed transactions in more depth.

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

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