Wednesday, June 22, 2011

EJB 3 Transactions

Transactions in EJB 3 are provided through the Java Transaction API (JTA).
For the most part a developer uses the javax.transaction.UserTransaction interface, this is because the container takes care of most transaction management. The developer simply tells the container where the transaction begins and ends (transaction demarcation) and wether to rollback or commit.
There are two ways of managing transactions: CMT (Container-manager Transactions) and BMT (Bean-managed Transactions). The first one is a declarative transaction with annotations or the deployment descriptor. The BMT, on the other hand, requires the developer to manage transactions programmatically. 
In this version of EJB only session beans and message driven beans support BMT, while entity doesn't dipend directly on CMT or BMT but can plug indifferently into any transaction environment while inside of a container. 

Container-managed Transactions: 
In container-managed transactions, the container starts, commits or rollbacks the transaction on our behalf. The start and end of a transactions are always marked the start and end of EJB business methods. 
All we have to do is tell the container to manage the transaction by using annotations or deployment descriptors. By default the container assumes the we use CMT to manager transactions. 

I show you now an example on how to use CMT by using annotations. 


In this picture we can see how to set a container managed transaction for our session bean. We have used the annotation @TransactionManagement specifiyng the type as CONTAINER. If we don't use this annotation the container assumes that we want to use CMT anyway. 



In the pics above I used some of the TransactionAttributes we could use to annotate the CMT methods.
Although the container manages the transaction on our behalf we still need to tell him how to do so. Let's consider for example that the transaction we use on our method has been started from the client that has called our method that is another EJB business method...or maybe our method has been called from the web container and is the method that has to start a new transaction to accomplish the business. So how the container acts? Does it uses the same transaction of the method that is calling or does it starts another transaction to be used on our method? And what if any of the methods does not support a transaction? The @TransactionAttribute tells the container how to handle all these situations. 
I describe the values that this annotation can have below:

REQUIRED: This is the most used attribute type. This attribute type means that the EJB method must always be executed within a transaction. If a transaction does not exist when the method is called than the container starts a new transaction before the method is called and finish it when the method returns. If the method is called from a transactional context, the method joins the transaction.
REQUIRES_NEW: This attribute value means that the container must create a new transaction every time the method is invoked. If the method is invoked outside a from a transactional context, the container creates a new transaction. On the other hand, if the method is called from a transactional context, the container pauses that transaction and creates a new transaction to execute the method. At the end of the method the transaction is either committed or rolled back and the transaction previously paused is resumed. 
SUPPORTED: This attribute means that the method accepts whatever the caller context is: if the caller has a transaction it joins the transaction to execute the method, else if it is not transactional the method is executed on the same context without a transaction.
MANDATORY: This attribute means that the client must have a transactional context before calling this method otherwise the container throws an EJBTransactionRequiredException. This attribute is used when you want that the client receives a rollback whenever something goes wrong during this method execution. 
NOT_SUPPORTED: A method annotated with this attribute value, doesn't accept to be execute on a transactional context. If a client with a transactional context calls the method, the client's transaction will be paused to execute the method and then resumed after the method execution. 
NEVER: This method's attribute means that the method annotated with it can't be invoked from a transactional context, else the container throws an EJBException.

Because of the fact that an MDB could not be called directly by a client but is the container to invoke it when a messages arrives, it can only support REQUIRED and NOT_SUPPORTED transaction attributes, because there isn't a client that can manage the caller's transaction to execute the business logic inside the MDB class.



If the business conditions arise, the CMT method could ask the container to roll back the transaction as soon as possible. We could do that by using the setRollbackOnly() method from the SessionContext object.
It is worth to say that the transaction is not rolled back immediately, instead a flag is checked and the transaction is rolled back before it ends.
From the picture above, we can see that we ask the container to rollback the transaction before throwing the DatabaseException. Before calling the setRollbackOnly method of the SessionContext, you must be sure the method is executing in a transaction context, and you could be sure of that if the method has a TransactionAttributeType with the value MANDATORY, REQUIRED or REQUIRES_NEW. If there is not a transaction context, calling this method results in an IllegalStateException.
You could use another method of the SessionContext object to know if the underlying transaction has been marked to rollback, it is the getRollbackOnly. This method returns a boolean value telling you if the transaction has been rolled back or not.
This method is very useful in the case you have a very long transaction to execute; calling this method before execute the transaction saves a lot of time if the transaction has been just rolled back.

Bean-managed transactions:
The limit of a container-managed transaction is that you are limited to having the transaction boundaries set at the beginning and end of a method and relying on the container to determine when a transaction starts, commits or rolls back.
On the other hand the bean-managed transactions allows the programmer to determine all these details programmatically, in a way similar to JDBC transaction model. Over this kind of transaction management, the container must be much more aware of the underlying JTA Transaction primarily the javax.transaction.UserTransaction interface.

In the same picture I show you how to say the container that it hasn't to worry about because the transaction because we are managing it:


As you can see from the picture we annotated the bean with the TransactionManagement annotation specifying the attribute type to BEAN, meaning the programmer manages the underlying transaction programmatically.
We are also injecting a resource: the UserTransaction interface that will be used to manage the transaction from his methods (begin, commit, rollback the main). The UserTransaction interface could also be get by JNDI lookup in this way:

This way is used when you need to use it in a nonmanaged class or a EJB helper class or in the web tier where dependency injection is not supported.

You could also get the UserTransaction by calling the getUserTransaction() method of the EJBContext. This way is used when you're using the SessionContext or MessageContext for some other purpose and using the resource injection results redundant. You could use it only if you're in a Bean Transaction Management Type else it throws an IllegalStateException. You cannot use the setRollbackOnly and getRollbackOnly methods of the EJBContext because these could only be used in a Container Managed Transaction Type, else it throws an IllegalStateException.


In the following picture I am going to show you the use of the main UserTransaction methods to manage the transaction:


From the picture you can see that in a Bean Managed Transaction method we don't need to set any attribute as e do for the Container Managed methods, because we are managing the transaction programmatically and through the UserTransaction methods we could start, commit or rollback it according to the business we do want to generate.
Calling the begin method we are associating a new transaction at the current thread; if we do call the begin method twice before committing or rolling back the transaction the container would throw the NotSupportedException because Java does not support nested exceptions. The commit and rollback methods removes the transaction associated to the current thread: the commit method sends a success signal to the underlying transaction manager, while the rollback method abandons the transaction.
The UserTransaction interface has other interesting methods that could be used to improve the management of the transaction. We could find the setRollbackOnly method that flags the transaction to rollback; it also has the counterpart of the getRollbackOnly method that we can find on the CMT transaction, it is the getStatus() and returns the status of the current transaction.
Another interesting method that could improve the performance of the application is the setTransactionTimeout that allows us to specify, in seconds, the transaction timeout. It is set by default to different values depending on the application server.


No comments:

Post a Comment