As you saw in Chapter 12, session beans can interact directly with the
database as easily as they can manage the workflow 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 just one participant out of
many 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 would want to 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 12, 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 14-11 shows the stateful
session bean with the additional state.
The
SessionSynchronization
interface is defined as follows:
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 12. 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. This causes 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.