Transaction Management
Another critical factor in many enterprise applications is transaction management. This is because in high-volume applications many users may be attempting to access the same data simultaneously. And in single- and multi-user situations you may often want a particular operation to either entirely succeed or fail.
Transaction ManagementFunctional Comparison
To illustrate the functionality capabilities of Spring and EJB 3.0 to provide transactionality consider the case of purchasing a ticket in the flight booking scenario (see Figure 2).
 | |
Figure 2. Purchase Ticket Sequence Diagram. If any step in the purchase ticket sequence fails then the system should be left in a consistent state. |
The unit test shown below verifies that the purchase ticket operation functions in a transactional manner:
public void testPurchaseTicket_DebitFailure() throws Exception
{
// Creates a pending ticket
Ticket ticket = createTicket();
// Store original ticket count from DB
int count = ticketDAO.findAll().size();
// Setup a credit authorizer which will throw an exception on debit
setupMockAuthorizer(true);
try
{
bookingAgent.purchaseTicket(ticket, true);
fail("System failure was not thrown as was intended");
}
catch (InsufficientFundsException e)
{
// Correct behavior since we're testing transactional semantics
}
// Verify transaction rolled-back
assertEquals(count, ticketDAO.findAll().size());
}
This test can also be successfully executed against both Spring and EJB 3.0 implementations. The Spring implementation is shown below:
public Ticket purchaseTicket(Ticket ticket)
{
creditAuthorizer.authorize(ticket.getPrice(), null);
ticket.setStatus(Status.PURCHASED);
ticketDAO.save(ticket);
creditAuthorizer.debit(ticket.getPrice());
return ticket;
}
Compare this to the EJB 3.0 implementation:
public Ticket purchaseTicket(Ticket ticket)
{
creditAuthorizer.authorize(ticket.getPrice(), null);
ticket.setStatus(Status.PURCHASED);
ticketDAO.save(ticket);
creditAuthorizer.debit(ticket.getPrice());
return ticket;
}
Notice that they are exactly the same and that neither has to explicitly deal with transaction management! This illustrates well the value of declarative programming. Developers can write code that focuses on the business problem, and cross-cutting concerns such as transactionality can be modularized and applied across an application. In this case both Spring and EJB 3.0 offer comparable functionality in dealing with transactions and both succeed modularizing this functionality in a way that it doesn't clutter business logic.
Transaction ManagementNon-Functional Comparison
Import aspects of transaction management to consider include demarcation, propagation, and isolation. The process of bounding a unit of work with transactional semantics is termed demarcation. And if multiple components are collaborating on a single unit of work, this transaction should be shared (propagated) between them. Lastly, isolation levels allow developers to tune the degree of isolation that the transaction has from other transactions.
In Spring, transaction demarcation, propagation, and isolation are all declared through its AOP facilities. Transactional proxies can be explicitly defined in its XML configuration files or can be applied using aspect semantics. The approach of wiring in a transactional proxy is illustrated below:
<bean id="bookingAgent" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="bookingAgentSpring" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="bookingAgentSpring" class="org.jug.flight.booking.spring.BookingAgentSpring">
<property name="flightDAO" ref="flightDAO" />
<property name="ticketDAO" ref="ticketDAO" />
<property name="screener" ref="screener" />
</bean>
EJB 3.0 on the other hand applies transactional semantics automatically on all public methods of session beans so no special configuration is required. Developers can tune the transaction propagation levels via annotations or XML:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Ticket purchaseTicket(Ticket ticket)
{
...
}
The EJB 3.0 specification declares transaction isolation to be resource-manager specific, thus there is no standard way to express this requirement in EJB 3.0.
Spring offers integration with many different types of transaction APIs including JDBC, Hibernate, and JTA. EJB 3.0 only supports JTA transactions. The choice of transaction API is important in two primary cases. First, not all containers (such as Tomcat) support JTA, although all EJB containers do. Second, in order to join multiple resources (such as JDBC and JMS) in a single distributed transaction, a JTA implementation is required. If you require JTA and are running in an environment that does not provide a JTA implementation you can consider open-source implementations of JTA such as the Java Open Transaction Manager (JOTM).
Transaction ManagementSummary
Both Spring and EJB 3.0 offer an integrated approach to transaction management that is compatible with their persistence mechanisms (see Table 3).
Table 3. Equivalent Transaction Management Functionality in Both Spring and EJB 3.0.
Feature
|
Spring
|
EJB 3.0
|
Declarative Transactions |
√ |
√ |
Programmatic Transactions |
√ |
√ |
Demarcation |
AOP |
Session bean methods |
Supported Transaction Types |
JDBC, Hibernate, JTA |
JTA |
Support for Distributed Transactions |
√ (With JTA) |
√ |
Configuration |
XML |
Transactional by default, override with annotations or XML |
Standard |
√ (With JTA) |
√ |