toring and retrieving information for most applications usually involves some form of interaction with a relational database. This has presented a fundamental problem for developers for quite some time since the design of relational data and object-oriented instances share very different relationship structures within their respective environments. Relational databases are structured in a tabular configuration and object-oriented instances are typically structured in a hierarchical manner. This “impedance mismatch” has led to the development of several different object-persistence technologies attempting to bridge the gap between the relational world and the object-oriented world. The Enterprise JavaBeans (EJB) framework provides is one such means of bridging this gap.
This article is the first in a series, which will discuss how three different object-persistence technologies (EJB, Java Data Objects, and Hibernate) attempt to simplify the chore of connecting relational databases and the Java programming language.
Object Persistence
The task of persisting Java objects to a relational database is currently being facilitated by a number of different tools that allow developers to direct persistence engines in converting Java objects to database columns/records and back. This task involves serializing hierarchically structured Java objects to a tabular-structured database and vice versa. Essential to this effort is the need to map Java objects to database columns and records in a manner optimized for speed and efficiency.
The Enterprise JavaBeans (EJB) framework provides a mechanism for object persistence. I will discuss this mechanism in this article, but first I’ll take a brief look at the overall architecture of EJB.
Enterprise JavaBeans
EJB technology can be defined as a Java-based, server-side, reusable component framework for distributed applications. This framework facilitates centrally managed business logic, declarative deployment, and object persistence. Some of the characteristics of the EJB framework are as follows:
- EJBs are Java objects executed within a server environment.
- EJBs can be distributed across many machines and accessed remotely as if they were on a local machine.
- EJBs are centrally managed by a container.
Central to the EJB specification and architecture is an artifact known as an Enterprise Java Bean (which, like the framework, is often given the acronym EJB). Even though the names from the EJB framework are very similar to the names from the JavaBeans framework, the specifications and designs of each framework are very different.
The EJB and JavaBeans specification names and component names are very similar, but this is where the similarities end. The JavaBeans specification and component model and the Enterprise JavaBeans and component model have very little in common. To illustrate this, I’ll look at the EJB concepts, components, and environment in detail.
EJB Runtime Environment
An Enterprise JavaBean is a Java object exposing a number of interfaces and which lives and executes inside of a runtime environment known as an EJB container. An Enterprise JavaBean cannot live outside of an EJB container. In fact, the relationship between an EJB container and an EJB is sometimes called ‘inversion of control’?or the “Hollywood Principle” (don’t call us; we’ll call you).
An EJB container is a runtime environment for managing enterprise beans. The container hosts and manages an enterprise bean in much the same manner that a Java servlet engine hosts a Java servlet. The EJB container instantiates and controls the enterprise beans and provides them with system-level services.
A client application wishing to interact with an enterprise bean does not have direct access to an enterprise bean. Rather, the bean is isolated from the client application by the container.
Services Provided by an EJB Container
A developer creating the classes and interfaces from which an enterprise bean object will be created can assume the following system-level services will be available from the EJB container:
- Transaction management for the bean
- Security for the bean
- Persistence of the bean
- Remote access to the bean
- Lifecycle management of the bean
- Database-connection pooling
- Instance pooling for the bean
Because the EJB container handles the bulk of infrastructure-level services for an enterprise bean, a programmer can concentrate on programming the business logic for the enterprise bean.
Types of Enterprise Beans
The EJB architecture defines three distinct types of enterprise beans:
- Message-driven beans
- Session beans
- Entity beans
Session beans and entity beans are invoked synchronously by an enterprise bean client. Message-driven beans (MDBs) are invoked by a message container, such as a publish/subscribe topic.
Message Driven Beans
A message-driven bean (MDB) is an enterprise bean that handles incoming messages asynchronously. An instance of an MDB typically acts as a message listener for messages passed from a JMS publish/subscribe topic. An MDB can receive any JMS-compatible message.
An EJB container manages the lifecycle of an MDB, however, unlike session and entity beans; client programs do not target the MDB with method calls. Instead, the MDB receives messages from a message source through a callback method, onMessage.
Session Beans
A session bean represents a single client and is not shared across clients. A client invokes the session bean’s methods, which are directed through the EJB container to the enterprise bean. The session bean performs the business logic for the client and the container returns control to the client. A session bean is not persisted across multiple sessions. There are two types of session beans: stateful and stateless.
Stateful Session Beans
A stateful session bean maintains a conversational state with one client for the duration of a single session. This implies that the stateful session bean can maintain instance variables across multiple invocations from one client during a single session.
Once the client finishes interacting with the enterprise bean and the EJB container removes the enterprise bean, the session for the bean ends and all state data for the bean are discarded.
Stateless Session Beans
A stateless session bean does not maintain a conversational state for each individual client. Each invocation of a stateless session bean should be considered as a request to a brand new object instance, as any instance-variable state will be lost between invocations.
Stateless session beans are not persisted to secondary storage by the EJB container; therefore a programmer must recognize that all data is transient between invocations for each client. The transient nature of stateless session beans allows an EJB container to reuse bean instances and therefore, usually optimize the performance of the beans.
Entity Beans
An entity bean is intended to represent the business logic for an entity existing in persistent storage, such as a relational database. Entity beans share some of the same qualities that you would find in a relational database, for example:
- Entity beans are persistent?An entity bean’s state exists beyond the lifetime of the application in which it is created, or for that matter, beyond the lifetime of the EJB container. This implies that the entity bean can be restored to its original state by the EJB container.
- Entity beans allow shared access?Beans may be shared by multiple clients and the concurrency is handled by the container.
- Entity beans have primary keys?Primary-key classes exist to identify an instance of an entity bean. The primary key contains all the information needed to find a persistent entity.
- Entity beans may participate in relationships?Local interfaces have been introduced to manage relationships between beans.
- Entity beans can participate in transactions?Because data can be accessed and changed by multiple clients, it is important for entity beans to be able to specify the transactional properties for their interaction. Transaction properties are specified declaratively in deployment descriptors, and transaction boundaries are handled by the container.
The object-relational mapping implied by entity beans requires that an entity bean be responsible for inserting, updating, selecting, and removing data within the data source. This process of managing the communication between the component and the data source is called persistence. In other words, persistence is this process of writing the information to an external data source. There are two types of persistence for entity beans: bean-managed persistence (BMP) and container-managed persistence (CMP).
![]() | |
Figure 1. Session Bean and Entity Bean Relationships: The figure shows how an EJB container acts as a proxy between the EJB client and the action EJB instance. This gives the container freedom to make invocations on EJB instances and to control the lifecycle of each EJB instance. |
Bean-Managed Persistence (BMP)
With bean-managed persistence (BMP), a programmer is responsible for writing all of the code within the entity bean to access the data source. BMP typically gives the programmer more flexibility because all access to the data source is controlled by the programmer.
Container-Managed Persistence (CMP)
With container-managed persistence the EJB container handles all database access required by the entity bean. As a result, the bean’s data-access code is not programmatically coupled to a specific data source. This frees the programmer from writing the data-access code and allows the entity bean to be deployed in different containers and/or against different data sources.
Figure 1 illustrates the relationships that exist between a client, an EJB container, session beans, and entity beans.
![]() | |
Figure 2. Web to EJB: The figure shows how a client request travels from the client tier to the Application tier. Once the controller servlet receives the request, it converts it to a business service request and invokes the appropriate business service in the Business Service tier. The business service uses one or more entity beans to load data from and save data to resources in the Data tier. |
The EJB Deployment and Runtime Environment
We will use JBoss 3.0.2 as our deployment and runtime environment for the examples that follow. We will design a simple Web application that allows user accounts to be created and retrieved using a Web browser communicating with a Java servlet, which communicates with a user service, which communicates with an entity EJB (see Figure 2).
Writing and Deploying an Entity Bean
The following four steps illustrate the typical process to develop and deploy an entity bean:
- Write the classes and interfaces for your entity bean
- Write a deployment descriptor
- Package the entity bean and associated files inside of a jar file
- Deploy the bean.
An entity bean is composed of a minimum of three classes/interfaces:
- The Component Interface?Because we are only concerned about accessing our EJB from the same JVM, we will create an interface that extends javax.ejb.EJBLocalObject.
- The Home Interface?Again, because we are only concerned about accessing our EJB from the same JVM, we will create an interface that extends javax.ejb.EJBLocalHome.
- The Enterprise Bean Class?The final class implements either javax.ejb.SessionBean or javax.ejb.EntityBean (see Listing 1).
package com.jeffhanson.datatier.ejb;import javax.ejb.EJBLocalObject;public interface LocalUser extends EJBLocalObject{ public String getUserID(); //primary key public String getFullName(); public String setAddress(String address); public String getAddress(); public String setCity(String city); public String getCity(); public String setState(String state); public String getState(); public String setZip(String zip); public String getZip();}
package com.jeffhanson.datatier.ejb;import javax.ejb.CreateException;import javax.ejb.FinderException;import javax.ejb.EJBLocalHome;import java.util.Collection;public interface LocalUserHome extends EJBLocalHome{ public LocalUser create(String userID, String fullName, String address, String city, String state, String zip) throws CreateException; public Collection findByFullName(String fullName) throws FinderException; public LocalUser findByPrimaryKey(String userID) throws FinderException;}
The Deployment Descriptor
To deploy an EJB into an EJB container’s environment, you must supply a deployment descriptor file to the EJB container. A deployment descriptor file is an XML document, named ejb-jar.xml, that specifies information about the bean such as its persistence type and transaction attributes. You need to package the Java classes and the deployment descriptor into a JAR or EAR file.
In addition to the standard ejb-jar.xml file, JBoss has an additional deployment descriptor for declaring the CMP persistence properties for enterprise beans, named jaws.xml. Listing 2 illustrates the ejb-jar.xml deployment descriptor for UserEJB.
Notice that Listing 2 specifies the type and name of the primary key for the EJB and the fields of the EJB that are persistent. A table is created with a name that is the same as that of the Bean class, which in this case will be named ‘UserEJB’. Listing 3 illustrates the jaws.xml deployment descriptor for UserEJB.
Notice in Listing 3 that each field is declared within a cmp-field element. This will instruct the EJB container to create a column for each field in the new database table and map each field to its particular column.
All you need to do to deploy UserEJB in JBoss is create a jar file with the compiled classes for the EJB and the deployment descriptors for the EJB and place it into the deploy directory of your particular JBoss server.
The EJB Client
The architecture of this application makes it possible for you to access the EJB from within the same JVM. This design simplifies a few things, specifically the type of casting needed in order to obtain the home interface for the EJB. Listing 4 illustrates the UserService object, which will access the EJB.
Notice that the UserService in Listing 4 first attempts to find an instance of the EJB by calling the findByPrimaryKey method and passing a user ID. If the instance is not found, the UserService calls the create method on the EJB’s home instance in order to create the EJB. The findByPrimaryKey method is intercepted by the EJB container, which attempts to retrieve a record from the database with a primary key named by the user ID. The create method will add a new record to the database with a primary key named by the user ID.
EJB Query Language
EJB offers an object-query language, called EJB QL, used to write queries for container-managed entity beans. The queries are translated by the EJB container to the query language of the storage facility into which the EJB is deployed. For this EJB, the container will convert your queries into SQL.
EJB QL is used to express queries for the find methods defined in an EJB’s home interface and perform internal select methods defined on the entity bean class. EJB QL queries contain a SELECT clause and a FROM clause, and optionally a WHERE clause. The query is placed in an application’s ejb-jar.xml file.
The following XML code (defined in your jaws.xml file) maps a query to a findByState method in the UserEJB’s Home interface. The UserEJB sample application uses this query and method to find the user that corresponds to a given state.
... findByState java.lang.String [!CDATA[ SELECT DISTINCT object(u) FROM UserEJB u WHERE u.STATE = ?1]]
Referring to the preceding query definition, the ejb-ql element defines the actual query that will be used. The OBJECT keyword is required when returning a single object type. Parameterized queries, as shown above, are facilitated with the use of logical numbered placeholders. Each number represents a parameter in the method’s parameter list, starting from an index of 1.
In the example above, the number 1 will be replaced by the state parameter in the findByState method. Entity beans that employ CMP can define a findAll and/or a findByPrimaryKey method, which will be implemented and executed transparently by the EJB container.
The EJB QL Query in Action
In Listing 5, the getUsersByState method is defined in the UserService class. This method makes a call to the findByState method on the home interface of UserEJB. The EJB container will intercept the call and execute the query defined in the jaws.xml file.
The architectural differences between Java object hierarchies and relational database tables make the task of persisting Java object data to and from relational databases quite daunting for developers. The “impedance mismatch” between relational tables and Java object hierarchies has led to the development of several different object-persistence technologies attempting to close the gap between the relational world and the object-oriented world. The Enterprise JavaBeans framework defines a container-managed persistence mechanism, which has given programmers a tool that can simplify this problem, when used with the proper amount of care.