Session beans can interact
directly with the database as easily as they can manage the taskflow
of other enterprise beans. The ProcessPayment EJB, for example, makes
inserts into the PAYMENT
table when the
byCredit( )
method is invoked, and the TravelAgent
EJB queries the database directly when the
listAvailableCabins( )
method is invoked.
Stateless session beans such as the ProcessPayment EJB have no
conversational state, so each method invocation must make changes to
the database immediately. With stateful session beans, however, we
may not want to make changes to the database until the transaction is
complete. Remember, a stateful session bean can be one of many
participants in a transaction, so it may be advisable to postpone
database updates until the entire transaction is committed or to
avoid updates if it is
rolled back.
There are several different scenarios in which a stateful session
bean might cache changes before applying them to the database. For
example, think of a shopping cart implemented by a stateful session
bean that accumulates several items for purchase. If the stateful
bean implements SessionSynchronization
, it can
cache the items and write them to the database only when the
transaction is complete.
The
javax.ejb.SessionSynchronization
interface allows
a session bean to receive additional notification of the
session’s involvement in transactions. The addition
of these transaction callback methods by the
SessionSynchronization
interface expands the
EJB’s awareness of its life cycle to include a new
state, the Transactional Method-Ready state.
This third state, although not discussed in Chapter 11, is always a part of the life cycle of a
transactional stateful session bean. Implementing the
SessionSynchronization
interface simply makes it
visible to the EJB. Figure 16-11 shows the stateful
session bean with the additional state.
The
SessionSynchronization
interface is defined:
package javax.ejb; public interface javax.ejb.SessionSynchronization { public abstract void afterBegin( ) throws RemoteException; public abstract void beforeCompletion( ) throws RemoteException; public abstract void afterCompletion(boolean committed) throws RemoteException; }
When a method of the SessionSynchronization
bean
is invoked outside of a transaction scope, the method executes in the
Method-Ready state, as discussed in Chapter 11.
However, when a method is invoked within a transaction scope (or
creates a new transaction), the EJB moves into the Transactional
Method-Ready state.
The SessionSynchronization
methods are called in
the
Transactional Method-Ready state.
When a transactional method is invoked on
a SessionSynchronization
bean, the stateful bean
becomes part of the transaction, causing the afterBegin( )
callback method defined in the
SessionSynchronization
interface to be invoked.
This method should take care of reading any data from the database
and storing the data in the bean’s instance fields.
The afterBegin( )
method is called before the EJB
object delegates the business-method invocation to the EJB instance.
When the afterBegin( )
callback method completes,
the business method originally invoked by the client is executed on
the EJB instance. Any subsequent business methods invoked within the
same transaction will be delegated directly to the EJB instance.
Once a stateful session bean is a part of a transaction—whether
it implements SessionSynchronization
or
not—it cannot be accessed by any other transactional context.
This is true regardless of whether the client tries to access the EJB
with a different context or the EJB’s own method
creates a new context. If, for example, a method with a transaction
attribute of RequiresNew
is invoked, the new
transactional context causes an error to be thrown. Since the
NotSupported
and Never
attributes specify a different transactional context (no context),
invoking a method with these attributes also causes an error. A
stateful session bean cannot be removed while it is involved in a
transaction. This means that invoking ejbRemove( )
while the SessionSynchronization
bean is in the
middle of a transaction will cause an error to be thrown.
At some point, the transaction in which the
SessionSynchronization
bean has been enrolled will
come to an end. If the transaction is committed, the
SessionSynchronization
bean will be notified
through its beforeCompletion( )
method. At this
time, the EJB should write its cached data to the database. If the
transaction is rolled back, the beforeCompletion( )
method will not be invoked, avoiding the pointless effort
of writing changes that won’t be committed to the
database.
The afterCompletion( )
method is always invoked,
whether the transaction ended successfully with a commit or
unsuccessfully with a rollback. If the transaction was a
success—which means that beforeCompletion( )
was invoked—the committed
parameter of the
afterCompletion( )
method will be
true
. If the transaction was unsuccessful,
committed
will be false
.
It may be desirable to reset the stateful session
bean’s instance variables to some initial state if
the afterCompletion( )
method indicates that the
transaction was rolled back.