[ Team LiB ] |
Transactions and Session BeansTransactions are a mechanism by which your application performs several tasks as a single unit of work. A typical example of transactions would be a banking application executing a funds transfer. This process involves debiting one account and crediting another. If either of these two processes fails, the whole transaction is rolled back. In this chapter, we don't discuss transactions in detail. We look only at different transaction management strategies available to you when you use the WebLogic Server container. You can learn more about transactions in Chapter 9, "Processing Transactions with the Java Transaction API." There are essentially two types of transaction management for EJBs: container-managed transactions and bean-managed transactions. In this section, we look at these types of transaction management in the context of Session Beans. Container-Managed TransactionsContainer-managed transactions, or CMTs, are a mechanism by which the WebLogic EJB container manages all transactions for you. Your application can focus on the business logic, rather than worrying about nuances of transaction management. CMTs are also often referred to as declarative transaction management. To use declarative transaction management in your bean, simply include the following stanza within the <session> (under <ejb-jar><enterprise-beans>) block of your ejb-jar.xml deployment descriptor: <session> ... <transaction-type>Container</transaction-type> </session> After you specify that the container has to manage transactions, you must let the container know which methods require what type of transaction management support. To do this, you introduce a block called <container-transaction> in your ejb-jar.xml (under <ejb-jar><assembly-descriptor>). You can provide a default transaction behavior for all methods of your bean by using the wildcard character (*) for the method name. (Refer to Chapter 20 for a more detailed description of the <method> tag.) Consider the following sample snippet for the AirlineReservation EJB: <assembly-descriptor> <container-transaction> <method> <ejb-name>AirlineReservationBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Supports</trans-attribute> </container-transaction> </assembly-descriptor> Here we indicate to the container that all (*) methods of the bean named AirlineReservationBean support transactions. Chapter 20 describes the transaction attributes that can be set for this tag in detail. We can further refine transactional behavior of some of our methods. For instance, in the following snippet, we tell the container that all methods of this bean support transactions by default, but the reserveSeats method specifically requires a transaction to be present. Thus, the container will start a new transaction if one isn't already present. The container chooses the Required attribute over the Supports attribute because the method defined more specifically indicates the reserveSeats method rather than the generic wildcard method name. <container-transaction> <method> <ejb-name>AirlineReservationBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Supports</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>AirlineReservationBean</ejb-name> <method-name>reserveSeats</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> You can even specify transaction attributes for different flavors of the same method by specifying the <method-params> stanza within the method to qualify the method name. Session SynchronizationOne thing you must remember about Stateful Session Beans is that the beans themselves are nontransactional. In other words, if you alter the values stored within the bean during a transaction, those values will not be rolled back if the entire transaction rolls back. Therefore, it becomes necessary for Stateful Session Beans to be aware of transactional activity such as begin transaction and end transaction so that they can perform additional operations during those times to make the bean transactional. Session synchronization provides your bean implementation with the capability to make the bean transactional. To achieve this, your bean implementation can optionally implement the javax.ejb.SessionSynchronization interface and define three callback methods. The container invokes these methods at appropriate times in the life cycle of the transaction, allowing you to perform such tasks. These methods are
Rolling Back Container-Managed TransactionsWe know that the container manages the transaction boundaries for us in the case of declarative transaction management. How does the container know that the transaction has failed and must be rolled back? The container automatically rolls back the transaction when the bean fails. A bean is considered to fail when it throws a system exception. Based on this explanation, an application exception isn't considered a failure, and the transaction isn't rolled back. If your bean picks up a condition that indicates the transaction must be rolled back, the bean implementation typically throws an application exception. By definition, application exceptions cannot extend RuntimeException. Runtime exceptions are not expected, but business exceptions are. So, how does your bean instance inform the container that you want this transaction rolled back? To do this, you invoke the setRollbackOnly method on the SessionContext object. This method tells the container that this transaction must be rolled back, irrespective of the outcomes of other operations that participate in this transaction. As an example, the following snippet of code indicates to the container that the transaction should never be committed because it violated a business rule: ... If (balance < 200) { context.setRollbackOnly(); throw new BalanceTooLowException(); } Along the same lines, beans that will be part of a transaction can check to see whether other beans have already set the rollback-only option, and avoid processing certain tasks and saving time. This can be done by invoking the getRollbackOnly method on the SessionContext object, before it does any processing: if (context.getRollbackOnly()) { return ; } // continue with processing ... Bean-Managed TransactionsAlthough declarative transaction management can be a very powerful tool, in certain (remote) cases, your application might want more control over transaction management. In such cases, you could use the second type of transaction management known as bean-managed transactions, or BMT for short. To do this, first set the <transaction-type> attribute (under <ejb-jar><enterprise-beans><session>) in the ejb-jar.xml to Bean. This ensures that the container won't interfere in transaction management of your bean. After this is done, you should code your bean implementation to take care of transactions within it. If you want your bean to participate in global transactions, you can use the java.transaction.UserTransaction interface to manage the transactions within your bean. The container provides you with the implementation of the UserTransaction interface in the SessionContext object. As you might remember, the container gives you the SessionContext object by invoking the setSessionContext method of your bean implementation. The following code snippet illustrates the use of UserTransaction in a bean implementation for managing transactions: public void beanMethod() { try { // context = instanceof SessionContext, set by the // setSessionContext method. UserTransaction txn = context.getUserTransaction(); txn.begin(); // your business logic goes here // if all tasks were successful txn.commit(); // else txn.rollback(); } catch (Exception ex) { // Exception handling logic goes here } } As you can see, we've obtained the UserTransaction object from the session context. Your bean can obtain this object in another way: by looking up the object from the JNDI tree, and looking up the name java:comp/env/UserTransaction. The following code snippet illustrates this process: Context ctx = new InitialContext(); UserTransaction txn = (UserTransaction) ctx.lookup("java:comp/env/UserTransaction"); This second technique may be used by utility classes invoked by your beans, which don't have access to the session context. Note that this lookup returns null if it's used in the context of container-managed transactions. Note also that getUserTransaction() throws an exception if it's used in the context of container-managed transactions. NOTE For more information about the UserTransaction interface and how to use the Java transaction architecture, refer to Chapter 9. For more information about JNDI lookups, refer to Chapter 8. In the case of Stateless Session Beans, transactions that are begun in one method must be ended (committed or rolled back) in the same method. This is because Session Beans aren't tied to a client. If this rule isn't imposed, different clients could potentially invoke the two methods (one that begins the transaction, and one that ends it), which is incorrect. However, for Stateful Session Beans, you may begin the transaction in one method and end it in a different method. Be very careful while doing this because your bean implementation does not have control over when or if the second method is called by the client. Your bean implementation relies on the client of your bean to invoke the methods in the appropriate order. A preferable alternative to this is to write another bean method that invokes the two methods in the appropriate order, and use container-managed persistence to set the appropriate transaction attribute for this method. When a bean managed transaction is invoked by a client that's already in an active transaction context, the container first suspends the client's transaction and then invokes the bean implementation. The container does this irrespective of whether the bean implementation starts its own transaction. |
[ Team LiB ] |