Migrating from EJB 2.x to EJB 3.0 Blog

Version 2

    {cs.r.title}



    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 implementejb<method> 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 fromjavax.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 throwjava.rmi.RemoteException, all the input parameters must be serializable, and the return value must be serializable. A home interface in EJB must extend fromjavax.ejb.EJBHome and defines lifecycle methods. A home interface for a stateless session bean must contain acreate() method with no parameters to instantiate the EJB in the container. The bean implementation class must be public and must implement the javax.ejb.SessionBeaninterface, 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.

     
    // 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; } } 
    
    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.

     
    <?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> 
    
    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 @Remoteand @Local annotations. Note that the@Remote annotation lets the container know that the bean will be accessed by remote clients and the @Localannotation 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 @Remoteannotation.

    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.

     
    // Business Interface in EJB 3.0 - StockQuote.java package stockquote; import javax.ejb.Remote; @Remote public interface StockQuote { public double getStockQuote(String Symbol); } 
    
    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<method> callback methods need not be written in the bean implementation class, making the coding simpler and easier. The bean class no longer implements thejavax.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:

     
    // 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; } } 
    

    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.

     
    @Stateless @Local ({StockQuoteLocal.java}) public class StockQuoteBean implements stockquote.StockQuoteLocal { ............ ............ } 
    

    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.

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

    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<Method>Callback Methods

    With EJB 3.0, the bean implementation class no longer needs to implement all the lifecycle callback methods of thejavax.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 overloadedcreate<Method> methods. However, for eachcreate<Method> in the home interface, there must be a matching ejbCreate<Method> 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 thejavax.ejb.SessionSynchronization interface. The following example illustrates the bean implementation class of a stateful session bean using the EJB 3.0 specification.

     
    //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; } } 
    

    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<Method>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.

     
    //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()); } } } 
    

    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 <ejb-ref-name /> and<ejb-ref-type/> tags.

     
    <ejb-ref-name>StockQuoteEJB</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>stockquote.StockQuoteHome</home> <remote>stockquote.StockQuote</remote> 
    
    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 @WebMethodannotation.

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

     
    //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(); } } } 
    

    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 @EJBannotation. 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 theStockQuoteBean 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:

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

    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., @EJBannotation is not used generally. The business interface class is looked up in the JNDI context.

     
    ............... ............... InitialContext context = new InitialContext(); StockQuote stockquote = (StockQuote)context.lookup (StockQuote.class.getName()); ............... ............... 
    

    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.xSession Bean in EJB 3.0
    Remote interfaceBusiness interface
    Extends EJBObject orEJBLocalObjectSimple POJI
    All methods must be declared in the throws clause ofRemoteExceptionRemote or local access is annotated with the help of@Remote or @Local
    Home interfaceNo home interface
    Extends EJBHome or EJBLocalHome 
    Must have at least one create method 
    Create methods must throw CreateException andRemoteException 
    Bean Implementation ClassBean Implementation Class
    Implements the SessionBean interfaceImplements the business interface
    Must define all methods of the SessionBeaninterfaceIs a simple POJO
    Defines business logic for the methods in the remote interfaceDefines business logic for the methods in the business interface
    The type of the bean is defined in the deployment descriptor fileThe type of the bean is annotated using @Statelessor @Stateful
    Callbacks are supported through the lifecycle methods of theSessionBean interfaceCallbacks are supported through the@PostConstruct, @PreDestroy,@PrePassivate, @PostActivate, and@Init annotations
    Deployment DescriptorDeployment Descriptor
    Two XML files are requiredDeployment descriptors are optional
    ejb-jar.xml: Standardized by the specificationUsed only if there is a need to override the annotation
    xxx.xml: Vendor-specific file 
    Session Bean ClientsSession Bean Clients
    Clients lookup for the home object using JNDI contextUses dependency injections and annotations instead of JNDI lookups. (Uses @EJB annotation in application clients)
    To locate another EJB, ejb-ref is used in deployment descriptorNo longer required (@Resource annotation is used)
    Uses JNDI lookup to obtain reference to any resourceUses @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 thejavax.ejb.MessageDrivenBean andjavax.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, @MessageDrivenspecifies the destination monitored by this MDB. The bean class need not implement the javax.ejb.MessageDrivenBeaninterface. However, the bean class needs to implement theMessageListener interface, which defines only one method, onMessage(), which takesjavax.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 likePostConstruct 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:

     
    // 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(); } } } 
    

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

     
    @MessageDriven(mappedName = "jms/Queue") 
    

    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.xMessage-Driven Bean in EJB 3.0
    Bean class implements the MessageDrivenBeaninterfaceBean class is annotated with @MessageDriven
    The message destination type, name, etc., are specified in the deployment descriptor fileThe bean class is annotated with@ActivationConfigProperty
    MessageDrivenContext can be acquired using thesetMessageDrivenContext() methodDependency injection is used to acquireMessageDrivenContext @Resource javax.ejb.MessageDrivenContext mdc;
    Resources and references are specified using deployment descriptor and JNDI lookupAchieved 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 thejavax.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 Resourcessection.

    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 @Transientannotation 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.

     
    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(); } } 
    

    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 @Tableannotation. 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 @Idannotation. 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 @Tableannotations 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 @Transientannotation.

    Entity Lifecycle Callback Methods

    Since the entity class in EJB 3.0 no longer implements thejavax.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 MethodsEJB 3.0 Entity lifecycle Methods
    ejbCreateImplement logic in init methods or constructors
    ejbPostCreate@PostPersist
    ejbRemove@PreRemove
    setEntityContext,unsetEntityContextNot Applicable
    ejbActivate@PostLoad
    ejbPassivateNot 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.

     
    // 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(); ............ 
    
    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 theEntityManager class. Some of the commonly used methods of EntityManager are:

    • persist(Object entity): Makes the entity persistent.
    • createQuery(String ejbQL): Creates aQuery 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:

     
    // 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); } } 
    

    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.xEntity in EJB 3.0
    Remote interface (local interface)No remote or local interface
    Extends EJBObject orEJBLocalObjectEJB 3.0 does not support entities as remote objects
    All methods must be declared in the throws clause ofRemoteException (in case of remote objects) 
    Home interfaceNo home interface
    Extends EJBHome or EJBLocalHome 
    Must have at least one create method 
    Create methods are declared in the throws clause ofCreateException and RemoteException 
    Must have at least one finder method declared:findByPrimaryKey 
    Bean Implementation ClassBean Implementation Class
    public and abstractPOJO representing lightweight persistent domain object
    Implements EntityBean interfaceAnnotated with @Entity annotation
    Must define ejbCreate andejbPostCreate methodsLogic goes inside the public no argument constructor
    Must define all methods of the EntityBeaninterfaceFinder methods are identified using @NamedQueryand @NamedQueries annotations
    Abstract get and set accessor methods for persistent and relationship fieldsAccessor methods are complete and are well-defined
    Callbacks are supported through the lifecycle methods of the entity bean interfaceCallbacks are supported through the @PrePersist,@PostPersist, @PreRemove,@PostRemove, @PreUpdate,@PostUpdate, and @PostLoadannotations
    Implements ejbHome methodsDuring 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 DescriptorDeployment Descriptor
    Several XML files are required; the standard ejb-jar.xmland others for vendor-specific persistenceOnly persistence.xml is required for JPA
    Entity Bean ClientsEntity Clients
    Clients lookup for the home object using JNDI contextClients are always local
    Uses JNDI lookup to obtain reference to any resourceUses 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

      
    http://today.java.net/im/a.gif