A couple of EJB 1.0 servers do not support entity beans. This is a legitimate choice, since support for entity beans is not required in EJB 1.0.[2] This section provides some strategies for developing systems that only use session beans for EJB 1.0 developers.
[2] Support for entity beans is required in Version 1.1 of EJB.
Entity bean support is required in EJB 1.1. If you're using EJB 1.1, you can skip this section.
Session beans that implement the SessionSynchronization interface (discussed in Chapter 8, "Transactions") can emulate some of the functionality of bean-managed entity beans. This approach provides a couple of advantages. First, these session beans can represent entity business concepts like entity beans; second, dependency on vendor-specific object-to-relational mapping tools is avoided.
Unfortunately, session beans were never designed to represent data directly in the database, so using them as a replacement for entity beans is problematic. Entity beans fulfill this duty nicely because they are transactional objects. When the attributes of a bean are changed, the changes are reflected in the database automatically in a transactionally safe manner. This cannot be duplicated in stateful session beans because they are transactionally aware but are not transactional objects. The difference is subtle but important. Stateful session beans are not shared like entity beans. There is no concurrency control when two clients attempt to access the same bean at the same time. In the case of the stateful session beans, each client gets its own instance, so many copies of the same session bean representing the same entity data can be in use concurrently. Database isolation can prevent some problems, but the danger of obtaining and using dirty data is high.
Other problems include the fact that session beans emulating entity beans cannot have find() methods in their home interfaces. Entity beans support find() methods as a convenient way to locate data. Find methods could be placed in the session bean's remote interface, but this would be inconsistent with the EJB component model. Also, a stateful session bean must use the SessionSynchronization interface to be transactionally safe, which requires that it only be used in the scope of the client's transaction. This is because methods like ejbCreate() and ejbRemove() are not transactional. In addition, ejbRemove() has a significantly different function in session beans than in entity beans. Should ejbRemove() end the conversation, delete data, or both?
Weighing all the benefits against the problems and risks of data inconsistency, it is recommended that you do not use stateful session beans to emulate entity beans. If your EJB server doesn't support entity beans, use the direct access or object-to-relational mapping options.
Perhaps the most straightforward and most portable option for using a server that only supports session beans is direct database access. We did some of this with the ProcessPayment bean and the TravelAgent bean in Chapter 7, "Session Beans". When entity beans are not an option, we simply take this a step further. The following code is an example of the TravelAgent bean's bookPassage() method, coded with direct JDBC data access instead of using entity beans:
public Ticket bookPassage(CreditCard card, double price) throws RemoteException, IncompleteConversationalState { if (customerID == 0 || cruiseID == 0 || cabinID == 0) { throw new IncompleteConversationalState(); } Connection con = null; PreparedStatement ps = null;; try { con = getConnection(); // Insert reservation. ps = con.prepareStatement("insert into RESERVATION "+ "(CUSTOMER_ID, CRUISE_ID, CABIN_ID, PRICE) values (?,?,?,?)"); ps.setInt(1, customerID); ps.setInt(2, cruiseID); ps.setInt(3, cabinID); ps.setDouble(4, price); if (ps.executeUpdate() != 1) { throw new RemoteException ( "Failed to add Reservation to database"); } // Insert payment. ps = con.prepareStatement("insert into PAYMENT "+ "(CUSTOMER_ID, AMOUNT, TYPE, CREDIT_NUMBER, CREDIT_EXP_DATE) "+ "values(?,?,?,?,?)"); ps.setInt(1, customerID); ps.setDouble(2, price); ps.setString(3, card.type); ps.setLong(4, card.number); ps.setDate(5, new java.sql.Date(card.experation.getTime())); if (ps.executeUpdate() != 1) { throw new RemoteException ( "Failed to add Reservation to database"); } Ticket ticket = new Ticket(customerID,cruiseID,cabinID,price); return ticket; } catch (SQLException se) { throw new RemoteException (se.getMessage()); } finally { try { if (ps != null) ps.close(); if (con!= null) con.close(); } catch(SQLException se){ se.printStackTrace(); } } }
No mystery here: we have simply redefined the TravelAgent bean so that it works directly with the data through JDBC rather than using entity beans. This method is transactionally safe because an exception thrown anywhere within the method will cause all the database inserts to be rolled back. Very clean and simple.
The idea behind this strategy is to continue to model workflow or processes with session beans. The TravelAgent bean models the process of making a reservation. Its conversational state can be changed over the course of a conversation, and safe database changes can be made based on the conversational state.
Object-to-relational mapping provides another mechanism for "direct" access to data in a stateful session bean. The advantage of object-to-relational mapping tools is that data can be encapsulated as object-like entity beans. So, for example, an object-to-relational mapping approach could end up looking very similar to our entity bean design. The problem with object-to-relational mapping is that most tools are proprietary and may not be reusable across EJB servers. In other words, the object-to-relational tool may bind you to one brand of EJB server. Object-to-relational mapping tools are, however, a much more expedient, safe, and productive mechanism to obtaining direct database access when entity beans are not available.
Copyright © 2001 O'Reilly & Associates. All rights reserved.