| CONTENTS | PREV | NEXT | INDEX | Designing Enterprise Applications with the J2EETM Platform, Second Edition |
While you are free to write your application and enterprise bean code according to your own needs, we do recommend certain guidelines.
- Keep the code in enterprise beans as "client-neutral" as possible. Enterprise beans are meant to contain business logic that can be used by many client types. Methods in enterprise beans that serve only one client type make any logic within that method inaccessible to other client types. Code that is specific for a particular client type belongs with the software managing that client type. In particular, Web-tier and HTTP-related functions and data do not belong in an enterprise bean.
- Keep as much business logic as possible in the EJB tier. By doing so, you take advantage of the EJB container services and simplify your programming effort.
- Use stateful session beans to manage conversational state rather than managing conversational state in the client or Web tier. As with the previous point, this leverages the advantages of enterprise beans.
In addition to the guidelines discussed previously for choosing specific bean types, there are other design choices that you need to make when developing objects for the EJB tier. These choices include the types of objects that should be enterprise beans, and the role an enterprise bean may play in a group of collaborating components.
You may also need to consider the services provided by enterprise beans and the EJB container, and decide whether these services benefit your application. You should take into consideration these EJB services and advantages:
- The EJB architecture handles such system services as transaction management, security, scalability, persistent data access, distributed processing, and concurrency. An enterprise bean developer does not have to include code to handle these services. As a developer, you can focus on the application and business logic.
- Enterprise beans support multiple types of components.
- Enterprise beans support applications with a complex series of operations and accumulation of conversation state over time.
- Enterprise beans are portable across hardware platforms, operating systems, server implementations, and databases .
- Enterprise beans can be easily customized in the runtime environment.
- Enterprise beans are reusable software modules.
As noted earlier, an entity bean must implement a local client view to be the target of a container-managed relationship. However, the requirements of the application might necessitate that the same entity bean implement a remote view as well. It is possible for an entity bean to use a local view and have the advantages of container-managed relationships, and to expose its functionality to remote clients. This can be done in the following two ways.
A bean provider can implement a session bean with a remote client view that serves as a facade to the local entity beans implementing the container-managed relationships. Clients use the methods of the remote session bean, which in turn provides a conduit to the functionality of the local entity bean or beans. Because the session bean implements a remote view, clients are not restricted to the same Java Virtual Machine as the session bean. The functionality of the local entity beans is available to remote clients through this session bean.
Alternatively, the bean provider can implement an entity bean with both a local and remote client view. This dual-purpose entity bean may have one or more entity beans with local interfaces behind it. The remote entity bean interface provides a coarse-grained view of the persistent data that is modeled by the network of entity beans related through their local interfaces. The remote entity bean may be accessed directly by clients of the EJB tier, or the bean provider may implement a remote session bean that serves as a facade to the entity bean with both the local and remote view. Note that if the same method is exposed through both local and remote interfaces, the method will be called with the pass-by-reference semantics in the local case, and with the pass-by-value semantics in the remote case. The bean provider needs to take care while writing and invoking the method to avoid any unintended side-effects.
A facade provides a unified interface to a set of interfaces. This section describes when and how to use a session bean as a facade to entity beans.
Entity beans represent an object-oriented view of data and provide business logic to manipulate this data. In an enterprise environment, entity beans often need to be shared among different applications representing different tasks. In such cases, use of application-specific stateful session beans to manage the interaction of various entity beans provides a simpler interface to the client by giving the client a central point of entry. The client always interacts with this session bean and is unaware of the existence of other entity beans in the system. However, if the client interacts with only a few entity beans in a relatively simple way, the entity beans can be exposed directly.
Stateful session beans are logical extensions of the client programs. The decision to use one or many session bean facades depends on the types of clients the application supports. Since the sample application has only a single type of client, the shopping client, the sample application uses a single stateful session bean called ShoppingClientFacadeLocal. It's easy to imagine another client that would provide administration functionality such as inventory and order status monitoring. The work flow of such a client would be entirely different from a shopping client. Therefore, it is advisable to define another stateful session bean that encapsulates this administrative work flow. However, it is not recommended that a session bean be created as a facade for every entity bean in the system, as that approach would waste server resources.
Entity beans with local interfaces provide efficient access to fine-grained objects. Model these objects as local entity beans when you can assure that their clients are co-located on the same JVM, or implement the business objects with a local view and provide a remote enterprise bean facade to them.
As mentioned earlier, enterprise beans that are implemented as remote objects may consume significantly more system resources and network bandwidth to execute. Because of the overhead of remote access, remote entity beans should not be used for fine-grained access. Therefore, only model a business object as a remote entity bean if its clients are distributed. In such a case, keep the entity bean access coarse grained, and use a value object to pass data across the remote interface.
A value object is a serializable Java object that can be passed by value to the client. A value object can be used to aggregate state extracted from a remote entity bean for use by the client. By avoiding the use of fine-grained remote method calls to retrieve the persistent state of an entity bean, value objects help reduce the network overhead involved in remote access.
The sample application models the details of an order as a value object representing the state of a particular order in the database and providing getter methods to query the state of this account. An administration client makes just one remote call to execute getOrdersByStatus on the remote object account and gets back a collection of the serialized OrderDetails objects. The client can then query the state of these orders locally via the methods provided with the OrderDetails object.
In the sample application, an address and credit card information are modeled as value objects. The definition of the Address class is shown in Code Example 5.6.
public class Address implements java.io.Serializable {
public Address (String streetName1, String streetName2,
String city, String state, String zipCode, String country){
this.streetName1 = streetName1;
this.streetName2 = streetName2;
...
}
public String getStreetName1() {
return streetName1;
}
...
private String streetName1;
private String streetName2;
...
}
Code Example 5.6 Address value object
|
An Address does not exhibit complex behavior, but is merely a data structure that contains only data fields. An address is fine-grained, having only get and set methods. Moreover, it only has meaning if it is associated with an account.
When making the object pass-by-value it is important to make it immutable to reinforce the idea that the value object is not a remote object and changes to its state will not be reflected on the server; in other words, it is just a copy and not the remote reference. To make an Address object immutable, declare all its instance data private and supply only get methods. To change a pass-by-value object, the client must first remove it and then create a new object with the desired field values.
In a master-detail relationship, one object serves as a pointer to another. Typically such a relationship is represented to the user as a list of items from which to select. This list is called a master record and its contents are provided by the master object. Selecting an item from this list leads to an expanded view of that item. The expanded view is provided by a detail object.
A master-detail relationship is a one-to-many type relationship among data sets. A set of purchase orders and a set of line items belonging to each purchase order is an example of a master-detail relationship. An application can use this master-detail relationship to enable users to navigate through the purchase order data and see the detail data for line items only when needed.
Entity beans with container-managed persistence are the preferred way to implement master-detail relationship modelling, since they support container-managed relationships. Further, container-managed relationships provide a cascade-delete facility that automatically enables the lifetime of the detail object to be dependent on the lifetime of the master, capturing an important aspect of this dependency.
In situations where the use of container-managed persistence is not suitable, data access objects can be used to encapsulate access to persistent data. A data access object (DAO) design pattern separates the interfaces to a system resource from the underlying strategy used to access that resource. Both entity beans with bean-managed persistence and session beans can use DAOs.
A DAO class provides an abstract API for manipulating a data source. This abstract API makes no reference to how the data source is implemented. The DAO simply has to know how to load itself from persistent store based on some identity information (a primary key or a file name, for example), how to store itself, and so on.
By encapsulating data access calls, data access objects allow adapting data access to different schemas or even to different database types. Data access objects for different schemas and databases can share a common interface enabling the application assembler to choose the appropriate object from among several at assembly time. Section 6.4.3 on page 188 extends this approach to show how access objects can be used to create an integration layer.
The DAO pattern is not limited to representing data in a database. It can also encapsulate XML data sources as DAO classes.
5.7.5.1 Clarifying Bean ImplementationsWhen a session bean or an entity bean with bean-managed persistence needs to access a database within a method implementation, a corresponding method in the data access object implements the actual logic of fetching or updating data in the database. This removes the data access logic from the enterprise bean implementation. The bean's business logic is not cluttered with data access calls, such as JDBC calls, making it much cleaner and readable.
For example, consider the Catalog session bean. The business method getProducts needs to return all the products for a category. Whenever getProducts needs to operate on data residing in the database, it hands over control to a data access object. The data access object formulates the query, fetches the result set, and returns the data in the desired format to the bean's calling method.
In the sample application, the implementation of the Catalog session bean is provided by CatalogEJB. The code for CatalogEJB.getProducts appears in Code Example 5.7; the code for the corresponding data access object appears in Code Example 5.8.
public Page getProducts(String categoryId, int start, int count,
Locale l) {
try {
... // initialize dao to an instance of CatalogDAOImpl
return dao.getProducts(categoryId, start, count, l);
} catch (.....) {
// catch exceptions and throw an EJBException
}
}
Code Example 5.7 CatalogEJB.getProducts
|
public Page getProducts(String categoryId, int start, int count, Locale
l) {
... // initialize database connection and other variables
String query = "select COUNT(*) from (product a join"
+ "product_details b on a.productid=b.productid) "
+ "where locale = ? and a.catid = ? ";
PreparedStatement ps = con.prepareStatement(query,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ps.setString(1, l.toString());
ps.setString(2, categoryID);
ResultSet rs = ps.executeQuery();
... // Create page from the result set and return it
}
Code Example 5.8 CatalogDAOImpl.getProducts
|
Potential consequences of using the data access object pattern include:
- Resource vendor independence
When a component uses a vendor-specific API, it is locked into that vendor's product line. The DAO pattern provides a layer of indirection that isolates vendor-specific code in a class or several classes, where it can easily be replaced if necessary or desirable.
- Resource implementation independence
- Similar types of resources are often available in various formats through various access methods. For example, persistent data can be implemented by a relational database, an object database, flat files in a file system, interaction with a remote persistence server, and so on. The DAO pattern separates the management of data access mechanism details from the behavior of application objects. Easier migration to container-managed persistence
A data access object, by encapsulating persistence logic and separating it from business logic, facilitates migrating from bean-managed persistence to container-managed persistence.
- Enhanced extensibility
It is easier to add new data source types using data access objects. Each such type requires only a new DAO class, plus integration of that class into the existing framework.
There are times when you do not want to provide create methods in an entity bean's home interface. Omit the create methods when you do not want the entity bean to create persistent data. Typically in these situations, some other system or process is responsible for creating the data underlying the entity bean's persistent representation. For example, database functions insert new data into a database, and clients use the entity bean to retrieve data.
References between enterprise beans are common in many applications. Entity beans reference other related beans, stateful session beans may keep references to entity beans in conversational state, and stateless session beans and message-driven beans may look up and pass references to other beans. Developers can represent references between entity beans in the following ways:
- Use a container-managed relationship
With the EJB 2.0 architecture, model persistent references between entity beans using container-managed persistence as container-managed relationships. Container-managed relationships automatically maintain persistent references between entity beans and collections of entity beans.
- Store a reference to a home or component interface in a non-persistent field of an entity bean with bean-managed persistence or in an instance variable of a session or message-driven bean. Beans can retain references to other entity beans in instance fields.
- Use the referenced entity bean's primary key
If a referenced entity bean's home interface is known, the referencing bean can use the
findByPrimaryKeymethod in the referenced bean's home interface to find a bean's component interface.- Do not pass
thisas a reference to an enterprise bean. Doing so circumvents the EJB container and causes errors.