EJB Transactions
WebLogic supports two flavors of EJB transactions:
Container-managed transactions
Here, the EJB container manages the transaction boundaries. The standard ejb-jar.xml deployment descriptor contains transaction attributes that determine how the EJB container handles transactions across method invocations.
Bean-managed transactions
Here, the EJB implementation is responsible for demarcating transaction boundaries. The bean provider typically uses the transaction methods on the Connection object or the UserTransaction object to ensure all updates occur atomically.
The transaction-type element in the standard ejb-jar.xml descriptor file indicates whether the EJB will manage its own transaction boundaries, or defer all transaction management responsibilities to the EJB container. Both models have their benefits and limitations, and both impose certain restrictions on your EJB code.
6.2.1 Container-Managed Transactions
Container-managed transactions simplify your EJB code because you no longer need to explicitly mark transaction boundaries. You can configure your EJBs so that WebLogic's EJB container automatically creates a new transaction context before delegating the call to the actual EJB method, and commits the transaction just before the method exits. The EJB container rolls back the transaction if a system exception is raised or if the method indicates a rollback should occur. Alternatively, you can indicate that an EJB method must never be executed within a transaction context. Of course, an EJB method need not support transactions at all. All of these options can be configured transparently, without any changes to your EJB code.
|
With container-managed transactions, the transactional behavior for your EJBs is determined at deployment time. The standard ejb-jar.xml deployment file lets you specify the transaction attributes for your EJB methods. A transaction attribute determines the scope of a transaction when an EJB method is invoked. It specifies whether an EJB method executes within an existing transaction context. An EJB method can have one of the following transaction attributes:
Never
The method must never execute within an existing transaction context. If the client invokes such a method within a transaction context, the EJB container must throw a RemoteException (EJBException for local client).
NotSupported
The EJB container ensures that the method doesn't execute in a transaction context. If one exists, the container suspends the transaction for the duration of the EJB method, and resumes the transaction once the method exits.
Supports
The method executes with or without an existing transaction context. A new transaction isn't created before the method starts, but if one exists, it will be used, and it may influence the outcome of the transaction.
Required
The method requires a transaction context before it is invoked. If one doesn't exist, a new transaction is created before the method starts.
Mandatory
The method must never execute without a transaction context. If an existing transaction context doesn't exist, the EJB container must throw a TransactionRequiredException.
RequiresNew
A new transaction is created regardless of whether a client transaction context exists. The existing transaction is suspended, the new transaction is created for the duration of the method call, and the existing transaction is resumed once the method terminates.
The following XML fragment shows how you can adjust the transaction settings for the remote EJB methods of the Registration EJB, using the container-transaction element in the ejb-jar.xml deployment descriptor:
RegistrationEJB Remote registerForCourse ... Required RegistrationEJB Remote getCourses ... Supports ...
The value of the trans-attribute element in the ejb-jar.xml descriptor sets the transaction attribute for EJB methods. In this case, the registerForCourse( ) EJB method must be invoked only from within an existing transaction. If the EJB client doesn't initiate a JTA transaction before invoking the Registration EJB, the EJB container will create one automatically before delegating the call to the method. The getCourses( ) EJB method is configured to support transactions. So, if an EJB client created a transaction before invoking the method, the method will participate in the eventual outcome of the transaction.
In WebLogic Server, if an EJB supports container-managed transactions, the default transaction attribute for the EJB methods is Supports. This means any EJB method that runs within the context of an existing transaction can affect its outcome. Thus, if an EJB method throws a system exception, the EJB container must roll back the existing transaction. However, if an EJB method has a transaction setting of NotSupported or RequiresNew, the EJB container needs to suspend the current transaction before it can delegate the call to the method. In this case, the EJB method will have no effect on the outcome of the original transaction.
An EJB method can cause the EJB container to roll back a transaction in two ways:
- It can throw a system exception (a RuntimeException or an EJBException).
- It can invoke the setRollbackOnly( ) method on the EJBContext.
In either case, any transaction-aware updates made by the EJB method will be rolled back. An EJB method that supports container-managed transactions must not interfere explicitly with an existing transaction context. So, within an EJB method:
- You must not invoke the commit( ), setAutoCommit( ), and rollback( ) methods on the Connection object.
- You must not call the getUserTransaction( ) method on the EJBContext or use the UserTransaction object.
These operations are allowed only if the EJB needs to explicitly demarcate transaction boundaries i.e., if the EJB methods support bean-managed transactions.
6.2.2 Bean-Managed Transactions
Container-managed transactions have a limitation. A call to an EJB method can be associated with, at most, one transaction at a time. If this constraint makes it difficult for you to write your EJB code, you should consider using bean-managed transactions. Bean-managed transactions allow you to explicitly set the transaction boundaries for your EJB methods. For instance, now you could write an EJB method that updates the database as follows:
begin transaction ... update table-A ... if (condition-x) { commit transaction } else if (condition-y) { update table-B commit transaction } else { rollback transaction begin transaction update table-C commit transaction } ...
If the EJB supports bean-managed transactions, you must ensure that the transaction-type element in the standard ejb-jar.xml descriptor file has the value Bean. In this case, the EJB method must use the UserTransaction interface to explicitly demarcate transaction boundaries. The following example shows how an EJB method can perform multiple updates using the UserTransaction interface:
public void doUpdates(int id) { try { //create a UserTransaction instance using the EJBContext for the bean UserTransaction tx = ctx.getUserTransaction( ); tx.begin( ); //ds is a reference to an XA-aware data source Connection con = ds.getConnection( ); Statement st = con.createStatement( ); st.executeUpdate("update tableA set ... where update tableB set ... where docEmphBold">tx.commit( ); } catch (Exception ex) { try { tx.rollback( ); throw new EJBException("Tx Failed:" + ex.getMessage( )); } catch (SystemException ese) { throw new EJBException("Tx Rollback Failed:" + ese.getMessage( )); } } finally { //release open connections } }
Here, the distributed transaction is bound by the calls to the begin( ) and commit( ) methods on the UserTransaction object. Note that we're using an XA-aware data source to return JDBC connections so that they can participate in the JTA transaction. You also can look in the JNDI tree under java:comp/UserTransaction in order to obtain a reference to the UserTransaction object:
InitialContext ctx = new InitialContext( ); UserTransaction tx = (UserTransaction) ic.lookup("java:comp/UserTransaction");
This is functionally equivalent to calling the getUserTransaction( ) method on the EJBContext.
With bean-managed transactions, your EJB code must explicitly specify when the JDBC updates are committed and when the changes are rolled back. The EJB container remains a silent spectator in this respect because the EJB method itself is entirely responsible for marking the boundaries of the transaction(s). If a transaction context does exist before the invocation of an EJB method that supports bean-managed transactions, the existing transaction is suspended for the duration of the EJB method and resumed once the method exits.
Bean-managed transactions impose certain restrictions on the EJB code as well:
- Only session and message-driven beans may implement bean-managed transactions. Entity beans may use only container-managed transactions.
- An EJB that uses bean-managed transactions must never invoke the getRollbackOnly( ) and setRollbackOnly( ) methods on the EJBContext interface. Instead, the EJB should use the getStatus( ) and rollback( ) methods on the UserTransaction interface.
- An EJB must not attempt to manipulate JDBC transactions from within the EJB method. Thus, you must not invoke the commit( ) or rollback( ) methods on the JDBC Connection object.
Because the EJB container is not involved in the transaction, the EJB method must explicitly commit (or roll back) the transaction before it exits. This holds true for stateless session beans and message-driven beans. A stateful session bean isn't required to do so. An EJB method may initiate a transaction and then complete without committing or rolling back the transaction. In this case, the EJB container is required to maintain the association between the transaction and subsequent method calls until the EJB instance commits (or rolls back) the transaction.