Skip to main content

Migrating from EJB 2.x to EJB 3.0

January 23, 2007

alt="{cs.r.title}" border="0" align="left" hspace="10" vspace="0">



The EJB 3.0 specification makes programming much simpler. It
makes the container do more work and the developers do less work.
It decreases the number of programming artifacts for developers to
provide, eliminates the requirement to implement
ejb callback methods, and reduces the
complexity of the entity bean programming model. EJB 3.0 should
attract a larger developer community with this new programming
model.

The EJB 3.0 specification provides the following
advantages:

  1. Fewer number of classes and interfaces
  2. POJO/POJI-based components
  3. Deployment descriptors are optional
  4. JNDI lookups are no longer necessary
  5. Persistence API for lightweight persistence for OR mapping
  6. Interceptors

This article discusses possible migration strategies for moving
applications written using EJB 2.x or earlier versions to the new
EJB 3.0 programming model. With this in mind, this article
discusses the changes in the new specification in the context of
each of the different bean types. Also, this article is not very
exhaustive in illustrating the migration options.

Migrating Stateless Session Beans

In this section, we will look at the changes proposed for the
stateless session bean. As mentioned earlier, one of the major
advantages of EJB 3.0 is that it is built using POJOs and
lightweight components.

Stateless Session Beans in EJB 2.x

In EJB 2.x and earlier specifications, stateless session
beans required two interfaces: the remote interface (or local
interface) for defining business methods and the home interface for
defining lifecycle methods. A session bean can also implement
multiple interfaces.

A remote interface must extend from
javax.ejb.EJBObject. The remote interface defines the
business methods and must follow RMI-IIOP rules. The rules of
RMI-IIOP are simple: the method must throw
java.rmi.RemoteException, all the input parameters
must be serializable, and the return value must be serializable. A
home interface in EJB must extend from
javax.ejb.EJBHome and defines lifecycle methods. A
home interface for a stateless session bean must contain a
create() method with no parameters to instantiate the
EJB in the container. The bean implementation class must be public
and must implement the javax.ejb.SessionBean interface, and
you have to implement all the lifecycle methods of the
javax.ejb.SessionBean interface.

The following listing illustrates the example for the remote
interface, home interface, and bean implementation class of a
stateless session bean defined using the EJB 2.x specification. In the
next section, we will discuss how the EJB 3.0 specification makes
it easier for developers to develop session beans.

[prettify]
// Remote Interface in EJB 2.x - StockQuote.java
package stockquote;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface StockQuote extends EJBObject {
    public double getStockQuote(String Symbol) throws
        RemoteException;
}

// Home Interface in EJB 2.x - StockQuoteHome.java
package stockquote;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import javax.ejb.CreateException;
public interface StockQuoteHome extends EJBHome {
   public StockQuote create() throws RemoteException,
      CreateException;
}

// Bean Implementation Class in EJB 2.x - StockQuoteBean.java
package stockquote;
import java.util.HashMap;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class StockQuoteBean implements SessionBean {
    private SessionContext context = null;
    private HashMap mapSQ = null;
    public void setSessionContext(SessionContext context) {
        this.context = context.
    }
    public void ejbRemove() {
    }
    public void ejbActivate() {
    }
    public void ejbPassivate() {
    }
    public void ejbCreate() {
        System.out.println("ejbCreate() method called");
        mapSQ = new HashMap();
        mapSQ.put("INFY", new Double(44.85));
        mapSQ.put("SUNW", new Double(4.99));
        mapSQ.put("IBM", new Double(80.97));
        mapSQ.put("MSFT", new Double(25.70));
    }
    public double getStockQuote(String symbol) {
        double result = 0;
        Object obj = mapSQ.get(symbol);
        if(obj != null ) {
            result = ((Double) obj).doubleValue();
        }
        return result;
    }
}
[/prettify]

Deployment Descriptor in EJB 2.x:
META-INF\ejb-jar.xml

In EJB 2.x, the deployment descriptor files are XML files that
defines the behavior of one or more EJBs. The EJB will have a minimum
of two deployment descriptors:

  • ejb-jar.xml: Required by all EJBs; has been
    standardized by the EJB specification.
  • xxx.xml: A vendor-specific deployment descriptor
    used to define the behavior of the container. This is not
    portable.

The following listing illustrates a sample deployment descriptor
defined for a stateless session bean using the EJB 2.x specification.

[prettify]
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<enterprise-beans>
    <session>
        <ejb-name>StockQuoteEJB</ejb-name>
        <home>stockquote.StockQuoteHome</home>
        <remote>stockquote.StockQuote</remote>
        <ejb-class>stockquote.StockQuoteBean</ejb-class>
        <session-type>Stateless</session-type>
        <transaction-type>Container</transaction-type>
    </session>
</enterprise-beans>
</ejb-jar>
[/prettify]

Stateless Session Beans in EJB 3.0

In EJB 3.0, a session bean is a plain JavaBean, also known as a
POJO (Plain Old Java Object) handled by the EJB container. In EJB
3.0 session beans, metadata annotations are used to specify the
bean type: stateless or stateful. There is no concept of remote and
home interfaces, and only one interface is defined: the "business
interface," which is a POJI (Plain Old Java Interface). Whether
this interface is local or remote can also be indicated by
annotations.

It is important to notice here that defining the business
interface is not mandatory. Interfaces are optional for entity
beans and are required for session beans. If there is no business
interface defined for a session bean, then the EJB container
automatically generates a business interface by default. The type
of the generated interface (either local or remote) is dependent on
the annotation used in the bean class. All the public methods of
the bean class will be included as part of automatically generated
business interface.

Session beans do not need home interfaces. The client may get a
reference of a stateless session bean using annotations and
dependency injection.

Business Interface

The business interfaces are annotated using @Remote
and @Local annotations. Note that the
@Remote annotation lets the container know that the
bean will be accessed by remote clients and the @Local
annotation lets the container know that the bean will be accessed
by local clients. The business interface is a local interface
unless it is annotated with the @Remote
annotation.

The business interface looks pretty simple; there is no need to
put a "throws RemoteException" on every method. The
following example illustrates the business interface defined using
the EJB 3.0 specification.

[prettify]
// Business Interface in EJB 3.0 - StockQuote.java
package stockquote;
import javax.ejb.Remote;
@Remote
public interface StockQuote {
    public double getStockQuote(String Symbol);
}
[/prettify]

Bean Implementation Class

The bean implementation class in EJB 3.0 is a POJO. This class
uses annotations to define the type of the session bean--stateless
or stateful. This class should implement the business interface (if
defined already) or specify the annotation @Local with
the name of the local business interface class to be generated.

Now, the ejb callback methods need
not be written in the bean implementation class, making the coding
simpler and easier. The bean class no longer implements
the javax.ejb.SessionBean interface. Instead, the bean
class implements the business interface. The dependency injections
could be induced using the @Resource annotation. The bean
implementation class defined using the EJB 3.0 specification is
provided below:

[prettify]
// Bean Implementation Class in EJB 3.0 - StockQuoteBean.java
package stockquote;
import javax.ejb.Stateless;
@Stateless
public class StockQuoteBean implements stockquote.StockQuote {
private HashMap mapSQ = null;
    public StockQuoteBean() {
        mapSQ = new HashMap();
        mapSQ.put("INFY", new Double(44.85));
        mapSQ.put("SUNW", new Double(4.99));
        mapSQ.put("IBM", new Double(80.97));
        mapSQ.put("MSFT", new Double(25.70));
    }
    public double getStockQuote(String symbol) {
        double result = 0;
        Object obj = mapSQ.get(symbol);
        if(obj != null ) {
            result = ((Double) obj).doubleValue();
        }
        return result;
    }
}
[/prettify]

The annotation @Stateless lets the container know
that StockQuoteBean is a stateless session bean. If
the interface is local, then the @Local annotation
could be used.

[prettify]
@Stateless
@Local ({StockQuoteLocal.java})
public class StockQuoteBean implements stockquote.StockQuoteLocal {
............
............
}
[/prettify]

A session bean can implement multiple interfaces, each interface
targeting a different type of client. Both the annotations
@Remote and @Local can be specified in a
bean class.

[prettify]
@Stateless
@Local ({StockQuoteLocal.java})
@Remote ({StockQuote.java})
public class StockQuoteBean implements
    StockQuoteLocal, StockQuote {
...............
...............
...............
}
[/prettify]

In EJB 3.0, the deployment descriptor is optional and is no
longer required. The information that goes into the deployment
descriptor is now inferred by the container using annotations
provided in the bean implementation class. Deployment descriptors
may still be used if there is a need to override the
annotations.

ejb Callback Methods

With EJB 3.0, the bean implementation class no longer needs to
implement all the lifecycle callback methods of the
javax.ejb.SessionBean interface. But it is possible to
designate any arbitrary method as a callback method to receive
notifications for lifecycle events. Any callback method has to be
annotated with one of the pre-defined lifecycle event callback
annotations.

The following lifecycle event callbacks are supported for
stateless session beans:

  • PostConstruct: Callback occurs after any
    dependency injection has been performed by the container and before
    the first business method invocation on the bean.
  • PreDestroy: Callback occurs at the time the bean
    instance is destroyed. Both the callbacks are executed in an
    unspecified transaction and security context.

Migrating Stateful Session Beans

In this section, we will look at the changes required for the
stateful session bean.

Stateful Session Beans in EJB 2.x

In the EJB 2.x specification, the primary difference between the
stateless and stateful session beans is in their deployment
descriptors. One more visible coding difference between stateless
and stateful session beans is that the home interface of the
stateful session bean can have overloaded
create methods. However, for each
create in the home interface, there must
be a matching ejbCreate in the bean
implementation class. For code examples on the stateful session bean and
its deployment descriptor, kindly refer to the sample code download
in the Resources section.

Stateful Session Beans in EJB 3.0

The business interface of a stateful session bean in EJB 3.0 is
also a POJI similar to that of the stateless session bean. The bean
implementation class in EJB 3.0 uses annotations to define the type
of the session bean--stateless or stateful. This class should
implement the business interface (if defined already) or specify
the annotation @Local with the name of the local
business interface class to be generated. A stateful session bean
must be annotated with the @Stateful annotation.
Optionally, stateful session beans can also implement the
javax.ejb.SessionSynchronization interface. The
following example illustrates the bean implementation class of a
stateful session bean using the EJB 3.0 specification.

[prettify]
//Bean Implementation Class in EJB 3.0 - CountBean.java
package count;
import javax.ejb.Remove;
import javax.ejb.Stateful;
@Stateful
public class CountBean implements Count {
    private int val;
    public int count() {
        System.out.println("count() called");
        return ++val;
    }
    @Remove
    public void remove() {
        val  = 0;
    }
}
[/prettify]

The @Remove annotation denotes a lifecycle method
of a stateful session bean. This method would be called when the
client application calls the remove method. This annotation is
applicable for stateful session beans only.

ejb Callback Methods

The EJB 3.0 specification defines annotations for each of the
lifecycle events in a stateful session bean. The container
automatically calls the annotated methods at the different stages
of the lifecycle. The annotations that can be used in a stateful
session bean for the lifecycle events are:

  • @PostConstruct: Same as stateless session
    bean.
  • @PreDestroy: Same as stateless session bean.
  • @PrePassivate: The annotated method is called by
    the container before it passivates the instance. (Applicable only
    to Stateful session bean.)
  • @PostActivate: The annotated method is called
    when an instance is activated by the container. The method is
    called when the activated instance is ready. (Applicable only to
    Stateful session bean.)
  • @Init: The annotated method is the initialization
    method of the stateful session bean. This is different from
    @PostConstruct. @Init is called first and
    then @PostConstruct is called.

Migrating Session Bean Clients

In this section, we will discuss the changes required for
session bean clients to adapt to the newer EJB 3.0 programming
model. We will first discuss the client accessing a session bean
using EJB 2.x, and then move on to accessing a session bean using
EJB 3.0.

Session Bean Clients in EJB 2.x

In EJB 2.x, a client for a session bean obtains a session object
using a JNDI name. The client shown below obtains a local home
object using the JNDI name StockQuoteHome, and then
calls the create() method.

[prettify]
//Session EJB Client in EJB 2.x - StockQuoteClient.java
package client;
import javax.rmi.PortableRemoteObject;
import javax.naming.InitialContext;
import stockquote.StockQuoteHome;
import stockquote.StockQuote;
public class StockQuoteClient {
   public static void main(String arg[]) {
      try {
         InitialContext context = new InitialContext ();
         Object objRef = context.lookup("StockQuoteHome");
         StockQuoteHome homeObj = (StockQuoteHome)
            PortableRemoteObject.narrow(objRef,
               StockQuoteHome.class);
         StockQuote sqObj = homeObj.create();
         double result = sqObj.getStockQuote("INFY");
         System.out.println("Stock Price of Symbol is:"
            +result);
         } catch (Exception err) {
            System.out.println(err.getMessage());
         }
   }
}
[/prettify]

If a bean is acting as a client for another enterprise bean,
then the reference of the bean must be specified in the deployment
descriptor using and
tags.

[prettify]
<ejb-ref-name>StockQuoteEJB</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>stockquote.StockQuoteHome</home>
<remote>stockquote.StockQuote</remote>
[/prettify]

Session Bean Clients in EJB 3.0

Well-defined interfaces simplify the development and maintenance
of Java EE applications. However, the type of client accessing the
enterprise bean must be decided up front: remote, local, or web
service.

Web service clients can access enterprise beans either by
accessing a web service created using JAX-WS or by invoking the
business method of the stateless session bean. Web service clients
can invoke only those business methods directly on the stateless
session bean that are annotated using @WebMethod
annotation.

An example of application client in EJB 3.0 specification
accessing StockQuoteEJB remotely is given below:

[prettify]
//EJB 3.0-Session EJB Client - StockQuoteClient.java
package client;
import javax.ejb.EJB;
import stockquote.StockQuote;
public class StockQuoteClient {
   @EJB
   private static StockQuote stockquote;
   public static void main(String[] args) {
      try {
         double result = stockquote.getStockQuote("INFY");
         System.out.println("Stock Price of Symbol is:
            "+result);
      } catch (Exception ex) {
         System.err.println("Caught an exception!");
         ex.printStackTrace();
      }
   }
}
[/prettify]

EJB 3.0 addresses the encapsulation of environmental
dependencies and JNDI access through the use of annotations,
dependency injection mechanisms, and simple lookup mechanisms.
Injection of resources or references can occur through either
annotation of the injection target or specification of the target
in the deployment descriptor file.

Application clients in EJB 3.0 refer to enterprise bean
instances by annotating static fields with the @EJB
annotation. The annotated static field represents the enterprise
bean's business interface, which gets resolved to the session bean
instance when the application client container injects the resource
references at runtime. The field is declared static because the
client class runs in a static context.

Calling a business method is easy. The business method is simply
invoked on the injected StockQuote object. The EJB
container will invoke the corresponding method on the
StockQuoteBean instance that is running on the
server.

A bean declares a dependency upon a resource or other entry in
its environment context through a dependency annotation. A
dependency annotation specifies the type of object or resource on
which the bean is dependent, its characteristics, and the name
through which it is to be accessed. Dependency annotations may be
attached to the bean class or to its instance variables or methods.
Examples of dependency annotations are provided below:

[prettify]
@EJB(name="StockQuoteEJB", beanInterface=StockQuote.class)
@Resource(name="sampleDB", type="javax.sql.DataSource.class")
[/prettify]

The @EJB annotation is used to inject EJB stubs
specifically, whereas @Resource is used to inject
dependency on resources in a more generic way. The resource could
be a database, a JMS destination such as queue or topic, or a timer
service. The service objects are injected using the object's JNDI
name.

For web clients, the code for invoking business method on a bean
is similar to that of the application client. However, the
dependency injection is not used; i.e., @EJB annotation
is not used generally. The business interface class is looked up in
the JNDI context.

[prettify]
...............
...............
InitialContext context = new InitialContext();
StockQuote stockquote = (StockQuote)context.lookup
    (StockQuote.class.getName());
...............
...............
[/prettify]

EJB 2.x Session Beans Versus EJB 3.0 Session Beans

The following table explains the significant differences of
a session bean between the EJB 2.x and EJB 3.0 specifications:

Session Bean in EJB 2.x Session Bean in EJB 3.0
Remote interface Business interface
Extends EJBObject or
EJBLocalObject
Simple POJI
All methods must be declared in the throws clause of
RemoteException
Remote or local access is annotated with the help of
@Remote or @Local
Home interface No home interface
Extends EJBHome or EJBLocalHome  
Must have at least one create method  
Create methods must throw CreateException and
RemoteException
 
Bean Implementation Class Bean Implementation Class
Implements the SessionBean interface Implements the business interface
Must define all methods of the SessionBean
interface
Is a simple POJO
Defines business logic for the methods in the remote
interface
Defines business logic for the methods in the business
interface
The type of the bean is defined in the deployment descriptor
file
The type of the bean is annotated using @Stateless
or @Stateful
Callbacks are supported through the lifecycle methods of
the SessionBean interface
Callbacks are supported through the
@PostConstruct, @PreDestroy,
@PrePassivate, @PostActivate, and
@Init annotations
Deployment Descriptor Deployment Descriptor
Two XML files are required Deployment descriptors are optional
ejb-jar.xml: Standardized by the specification Used only if there is a need to override the annotation
xxx.xml: Vendor-specific file  
Session Bean Clients Session Bean Clients
Clients lookup for the home object using JNDI context Uses dependency injections and annotations instead of JNDI
lookups. (Uses @EJB annotation in application clients)
To locate another EJB, ejb-ref is used in
deployment descriptor
No longer required (@Resource annotation is
used)
Uses JNDI lookup to obtain reference to any resource Uses @Resource annotation to obtain reference to
any resource

Migrating Message-Driven Beans

A Message-Driven Bean (MDB) is an EJB that allows applications
to process messages asynchronously. MDBs consume messages from
message destinations. MDBs are not visible to the clients; hence
they do not have home or remote interfaces. MDBs can send messages.
In this section, we will see the differences between the MDB defined
using EJB 2.x or earlier specifications and the MDB defined using the EJB
3.0 specification.

Message-Driven Beans in EJB 2.x

MDBs in EJB 2.x implement
the javax.ejb.MessageDrivenBean and
javax.jms.MessageListener interfaces. When the message
destination, Queue for example, receives a message, the
EJB container invokes the onMessage method of the
message-driven bean. The MDB can consume the message and process it
accordingly. Similar to session beans, MDBs also have deployment
descriptor files. The deployment descriptor (ejb-jar.xml) of
an MDB will have some information like the name of the MDB, the class
of the MDB, the message destination, and its type. In the vendor-specific deployment descriptor file, the information such as
initial pool size and JNDI name for the message destination is
specified. For code examples on MDB and its deployment descriptor,
kindly refer to the sample code download under the Resources
section.

Message-Driven Beans in EJB 3.0

In EJB 3.0, a message-driven bean is a POJO handled by the EJB
container. In EJB 3.0 MDBs, metadata annotations are used to
specify the bean type. In this case, @MessageDriven
specifies the destination monitored by this MDB. The bean class
need not implement the javax.ejb.MessageDrivenBean
interface. However, the bean class needs to implement the
MessageListener interface, which defines only one
method, onMessage(), which takes
javax.jms.Message as an argument.

Resources and EJB references can be injected into MDBs in the
same way as session beans. The context can also be injected into
the MDB. EJB 3.0 supports callback methods like
PostConstruct and PreDestroy that are
the same as stateless session beans, provides the Dependency Injection
pattern for access to resources, and allows interceptor method
definition for message-driven beans. A sample message-driven bean
written in the EJB 3.0 specification is provided below:

[prettify]
// EJB 3.0 Message Driven Bean - SampleMessageBean.java
import javax.ejb.MessageDrivenContext;
import javax.ejb.ActivationConfigProperty;
import javax.jms.MessageListener;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.JMSException;
import javax.annotation.Resource;
@MessageDriven(activateConfig =
{  @ActivationConfigProperty(propertyName="destinationType",
    propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
    propertyValue="mdb/queue")
})
public class SampleMessageBean implements MessageListener {
    @Resource
    private MessageDrivenContext mdc;
    public void onMessage(Message message) {
        TextMessage msg = null;
        try {
            if (message instanceof TextMessage) {
                msg = (TextMessage) message;
                System.out.println(msg.getText());
            } else {
                System.out.println("Message of wrong type: "
                        + message.getClass().getName());
            }
        } catch (JMSException e) {
            e.printStackTrace();
            mdc.setRollbackOnly();
        } catch (Throwable te) {
            te.printStackTrace();
        }
    }
}
[/prettify]

The MessageDriven annotation typically contains a
mappedName element that specifies the JNDI name of the
destination from which the bean can consume messages.

[prettify]
@MessageDriven(mappedName = "jms/Queue")
[/prettify]

EJB 2.x MDB Versus EJB 3.0 MDB

The following table summarizes the changes in MDB between EJB
2.x and EJB 3.0:

Message-Driven Bean in EJB 2.x Message-Driven Bean in EJB 3.0
Bean class implements the MessageDrivenBean interface Bean class is annotated with @MessageDriven
The message destination type, name, etc., are specified in the
deployment descriptor file
The bean class is annotated with
@ActivationConfigProperty
MessageDrivenContext can be acquired using
the setMessageDrivenContext() method
Dependency injection is used to acquire MessageDrivenContext
@Resource javax.ejb.MessageDrivenContext mdc;
Resources and references are specified using deployment
descriptor and JNDI lookup
Achieved using dependency injection
@Resource
(name="mdb/simpleQueue") private QueueConnectionFactory
qcFactory;

Migrating Entity Beans

The migration of the EJB 2.x persistence model to EJB 3.0 can happen
in three different ways:

  1. Migrate the EJB 2.x entity beans into EJB 3.0 entities.
  2. Migrate JDBC data access objects to use EJB 3.0.
  3. Create a new EJB 3.0 entity model.

The first two approaches involve code changes and are intended
to minimize the code in the application. The third is a simple and
straightforward way of a creating EJB 3.0 entity model. This can be
easily achieved with the help of IDE tools like NetBeans. In this
article, we are going to discuss the first approach.

Entity Beans in EJB 2.x

Entity beans are representations of persistent data and
therefore survive a server crash. EJB 2.x allows bean-to-bean
relationships and flexible relational designs. An O-R mapping is
required to move between memory and the persistent store. An entity
bean with Container-Managed Persistence (CMP) offers several
advantages. In EJB 2.x, entity beans are preferred to be local
objects. A local interface defines the business methods and extends
from javax.ejb.EJBLocalObject. A home interface in EJB
extends from javax.ejb.EJBLocalHome and is the primary
mechanism that enables application code to locate and manage entity
beans. The code of the CMP entity bean implementation class must
meet the container-managed persistence syntax requirements. The
bean class must implement all the methods of the
javax.ejb.EntityBean interface:

  • public void ejbActivate()
  • public void ejbPassivate()
  • public void ejbRemove()
  • public void setEntityContext(EntityContext
    context)
  • public void unsetEntityContext()
  • public void ejbLoad()
  • public void ejbStore()

For complete code examples on the CMP entity bean defined using EJB
2.x and its deployment descriptor, kindly refer to the sample code
download in the Resources section.

The bean implementation class is abstract. Abstract
accessor methods are provided for all the fields that the container
should manage. The bean implementation class must always use
accessor methods to access the persistent field. The container
handles persistence and lifecycle methods of the CMP entity beans
that are declared empty in the bean implementation class.

The container automatically synchronizes the state of the entity
bean with the database by calling the lifecycle methods. The
deployment descriptors for the entity beans need to be written or
generated using tools.

In EJB 2.x, the configuration of XML deployment descriptors for
the CMP entity bean was a major bottleneck. Therefore, one of the
important advantages of the EJB 3.0 specification is to shield the
developer from having to work with XML files. Kindly refer to the
sample code under the Resources section for a sample XML deployment
descriptor for an EJB 2.x CMP entity bean.

EJB 3.0 Entities

The EJB 3.0 entity class is a lightweight persistent domain object.
The entity class is a POJO. These entities are marked with
the @Entity annotation and all properties in the entity
class that are not marked with the @Transient
annotation are considered persistent.

In EJB 3.0, the Persistence API defines metadata annotations to
define persistence and relationship criteria on the lines of
object-relational mapping concepts. Entity classes do not need home
and local interfaces. It is important to notice that EJB 3.0 does
not support entities as remote objects. The finder methods are
specified with the @NamedQuery and
@NamedQueries annotations.

[prettify]
package account;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "Account")
@NamedQueries( {@NamedQuery(name = "Account.findByAccountId",
query = "SELECT a FROM Account a WHERE a.accountId = :accountId"),
@NamedQuery(name = "Account.findBigAccounts", query = "SELECT a
FROM Account a WHERE a.balance > :balance")})

public class Account implements Serializable {
    @Id
    @Column(name = "id", nullable = false)
    private String accountId;

    @Column(name = "acctType")
    private String accountType;

    @Column(name = "bal")
    private double balance;

    public Account() {
    }

    public Account(String accountId) {
        this.accountId = accountId;
    }

    public void setAccountId(String accountId) {
       this.accountId = accountId;
    }

    public String getAccountId() {
         return this.accountId;
    }

    public void setAccountType(String accountType) {
      this.accountType = accountType;
    }

    public String getAccountType() {
         return this.accountType;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
           return this.balance;
       }

       public int hashCode() {
           int hash = 0;
        hash += (this.accountId != null ?
            this.accountId.hashCode() : 0);
        return hash;
    }

    public boolean equals(Object object) {
        if (object == null || !this.getClass().equals
        (object.getClass())) {
                return false;
        }
           Account other = (Account)object;
        if (this.accountId != other.accountId &,&
            (this.accountId == null || !this.accountId.
                equals(other.accountId))) {
            return false;
        }
           return true;
     }

     public String toString() {
        return "" + this.accountId;
     }

     public double deposit(double amount) {
        setBalance(getBalance() + amount);
           return getBalance();
     }

     public double withdraw (double amount)
         throws MinimumBalanceException {
            if (amount > getBalance()) {
                throw new MinimumBalanceException("Request to
                    withdraw $" + amount +"; is more than
                    balance $" + getBalance() +" in account"
                    + getAccountId());
            }
            setBalance(getBalance() - amount);
            return getBalance();
        }
}
[/prettify]

The entity class is no longer abstract and all the accessor
methods are well defined. The class is identified as an entity with
the help of the @Entity annotation. The table
represented by this entity is denoted using the @Table
annotation. If the entity name is the same as the table name, the
@Table annotation is not required. The finder methods
are specified using @NamedQueries and
@NamedQuery annotations. If the entity has some CMR
relationships, they are also identified using annotations. The
primary key column is identified using the @Id annotation.
The @Column annotation specifies the database column
that corresponds to the property name. If a property is not
annotated with the @Column annotation, it is assumed that
the column name is same as that of the property name. Hence
@Column and @Table annotations are
required only if the names are different from that of the database.
If some of the properties of the entity are not persistent, they
may be denoted using @Transient annotation.

Entity Lifecycle Callback Methods

Since the entity class in EJB 3.0 no longer implements the
javax.ejb.EntityBean interface, there is no need to
implement the lifecycle methods of the bean. But there are few
callback methods supported in EJB 3.0 entity class to support these
lifecycle methods. Entity lifecycle callback methods can be
defined directly on an entity class using annotations. The
lifecycle callback methods supported in the entity are:

  • @PrePersist
  • @PostPersist
  • @PreRemove
  • @PostRemove
  • @PreUpdate
  • @PostUpdate
  • @PostLoad

The following table summarizes the comparison of the lifecycle
callback methods:

EJB 2.x Entity Bean Lifecycle Methods EJB 3.0 Entity lifecycle Methods
ejbCreate Implement logic in init methods or constructors
ejbPostCreate @PostPersist
ejbRemove @PreRemove
setEntityContext,
unsetEntityContext
Not Applicable
ejbActivate @PostLoad
ejbPassivate Not Applicable
ejbLoad @PreUpdate
ejbStore @PrePersist or @PostUpdate

Migrating Entity Bean Clients

Entity Bean Clients in EJB 2.x

The application code that makes use of EJB 2.x entity beans is
tightly coupled to the home and remote interfaces. The following
code snippet demonstrates a sample client application that tries to
invoke the entity bean locally.

[prettify]
// Sample Code Snippet of local client in EJB 2.x Entity Bean

InitialContext ctx = new InitialContext();
Object objref = ctx.lookup("AccountLocalHome");
AccountLocalHome acctHome = (AccountLocalHome)objref;
............
AccountLocal acctLocal = (AccountLocal)
      acctHome.create(acctId, initBal, acctType);
............
AccountLocal acctLocal = (AccountLocal)
      acctHome.findByPrimaryKey(acctId);
............
acctLocal.remove();
............
[/prettify]

Clients for Entity in EJB 3.0

In EJB 3.0, entities, being POJOs, are always local and never remote objects. They are obtained and queried using the
EntityManager API. Client code must change its use of entity home
and component-specific methods to use EntityManager to
access entities on which it operates. EJB 3.0 implements
persistence, lookup, and removal of entities using the
EntityManager class. Some of the commonly used methods
of EntityManager are:

  • persist(Object entity): Makes the entity
    persistent.
  • createQuery(String ejbQL): Creates a Query object
    to run an EJB QL query.
  • createNamedQuery(String name): Creates an
    instance of the Query object to execute a named query.
  • find(Class entityClass, Object primaryKey): Finds
    an instance of an entity.
  • remove(Object entity): Removes an instance of
    an entity.

The instance of EntityManager can be obtained by
injecting the entity manager into the application
component. The following code snippet shows an example of a stateless
session bean accessing an entity:

[prettify]
// Stateless Session Bean Client accessing Account Entity using EJB 3.0

import javax.ejb.Stateless;
import javax.ejb.Local;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;

@Stateless
@Local
public class AccountClient implements AccountLocal {

   @PersistenceContext
   private EntityManager em;

   public void create(String acctId,double bal,String acctType){
      Account acct = new Account(acctId, bal, acctType);
      em.persist(acct);
   }

   public Account findByPrimaryKey(String acctId) {
      return (Account)em.find("Account", acctId);
   }

   public java.util.Collection findBigAccounts(double bal) {
      Query query = em.createNamedQuery("findBigAccounts");
      query.setParameter(0, bal);
      return (Account)query.getResultList();
   }

   public void remove(Account account) {
      em.remove(account);
   }
}
[/prettify]

To summarize, migrating EJB 2.x entity beans to EJB 3.0 is a
complex task having an impact on clients, and hence requires careful
planning.

EJB 2.x Entity Beans Versus EJB 3.0 Entity Classes

The following table summarizes the differences between the CMP
entity bean in EJB 2.x and EJB 3.0:


Entity Bean in EJB 2.x Entity in EJB 3.0
Remote interface (local interface) No remote or local interface
Extends EJBObject or
EJBLocalObject
EJB 3.0 does not support entities as remote objects
All methods must be declared in the throws clause of
RemoteException (in case of remote objects)
 
Home interface No home interface
Extends EJBHome or EJBLocalHome  
Must have at least one create method  
Create methods are declared in the throws clause of
CreateException and RemoteException
 
Must have at least one finder method declared:
findByPrimaryKey
 
Bean Implementation Class Bean Implementation Class
public and abstract POJO representing lightweight persistent domain object
Implements EntityBean interface Annotated with @Entity annotation
Must define ejbCreate and
ejbPostCreate methods
Logic goes inside the public no argument constructor
Must define all methods of the EntityBean
interface
Finder methods are identified using @NamedQuery
and @NamedQueries annotations
Abstract get and set accessor methods for persistent and
relationship fields
Accessor methods are complete and are well-defined
Callbacks are supported through the lifecycle methods of the
entity bean interface
Callbacks are supported through the @PrePersist,
@PostPersist, @PreRemove,
@PostRemove, @PreUpdate,
@PostUpdate, and @PostLoad
annotations
Implements
ejbHome methods
During migration, ejbHome methods can be left as
instance methods since no state is assumed or be turned into static
methods
  Persistent fields and relationship fields are identified with
annotations
Deployment Descriptor Deployment Descriptor
Several XML files are required; the standard
ejb-jar.xml and others for vendor-specific persistence
Only persistence.xml is required for JPA
Entity Bean Clients Entity Clients
Clients lookup for the home object using JNDI context Clients are always local
Uses JNDI lookup to obtain reference to any resource Uses EntityManager API to persist, find, and remove
entities
  EntityManager is injected into the application
client components

Summary

The EJB 3.0 specification facilitates easy creation of EJBs by
simplified development, facilitating test-driven development and
focuses more on the POJO-based persistence model. The EJB 3.0
Persistence Java API standardizes the persistence API for the Java
platform. It simplifies the use of transparent persistence by using
metadata annotations. Finally, EJB 3.0 looks very promising for
enterprise Java developers. The specification supports migration
from different perspectives as well as version interoperability.
Each J2EE application is unique; hence it is difficult to outline
all aspects of the migration process. Whatever is best possible has
been discussed here.

Resources


width="1" height="1" border="0" alt=" " />
Sangeetha S. works as a technical architect at the E-Commerce Research Labs at Infosys Technologies
Scott Andersen B. Scott Andersen is a principal software engineer with Verocel, Inc., a company specializing in the verification of safety-critical software.
Related Topics >> EJB   |