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.
The essential steps to use interceptors for handling transactions include:
The Handling transactions manually recipe in Chapter 6 explains how to implement bean managed transactions.
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
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.
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 |
Mandatory |
If no transaction exists throw an exception |
Rollback the transaction and throw a |
RequiresNew |
Create a new transaction |
Rollback the transaction and throw a |
18.117.93.0