efore showing you how to migrate your EJBs from EJB 2.1 to EJB 3.0, it’s worth exploring what you gain by making the move. Basically, EJB 3.0 reduces the number of classes, interfaces, and deployment descriptors needed to create EJBs. The EJB 3.0 specification simplifies EJB development by letting you replace abstract bean classes with Plain Old Java Objects (POJOs) and replace Component and Home interfaces with Plain Old Java Interfaces (POJIs). The latter are optional?you don’t have to include them at all.
- The deployment descriptor ejb-jar.xml, which specifies the EJB name, the bean class name, the interfaces, the finder methods, the container-managed relationships (CMR), and any vendor-specific deployment descriptors are no longer required; they’re replaced by metadata annotations in the bean classes. That’s why you need JDK 5.0 to develop EJB 3.0 EJBs?because they use annotations, which weren’t available until JDK 5.0.
- EJB 3.0 replaces the create() and finder methods of EJB 2.1 with the javax.persistence.EntityManager API. Client applications for EJB 2.1 obtain a reference to entity and session bean objects using the JNDI name, while EJB 3.0 clients obtain them using the @Resource, @Inject, and @EJB annotations.
- In EJB 2.1, you developed entity and session beans using the javax.ejb package classes and interfaces, where a session bean implements the SessionBean interface and an entity bean implements the EntityBean interface. In contrast, EJB 3.0 session and entity bean classes are POJOs, and do not implement the SessionBean and EntityBean interfaces.
- An EJB 2.1 session bean class specifies one or more ejbCreate methods, callback methods, the setSessionContext method, and business methods. Similarly, an EJB 2.1 entity EJB specifies the ejbCreate(), ejbPostCreate(), callback, Container-managed persistence (CMP), getter and setter CMR, and getter, setter, and business methods. An EJB 3.0 session bean class specifies only the business methods. Similarly, an EJB 3.0 entity bean specifies business methods, getter/setter methods for the different bean properties, and getter/setter methods for bean relationships.
- The EJB 2.1 home interface extends the javax.ejb.EJBHome interface, and a local home interface extends the javax.ejb.EJBLocalHome interface. The EJB 2.1 remote interface extends the javax.ejb.EJBObject interface, and a local interface extends the javax.ejb.EJBLocalObject interface. EJB 3.0 doesn’t specify the component and home interfaces?they’re replaced with POJI business interfaces. If a session bean class doesn’t specify a business interface, the EJB server generates a POJI business interface from the session bean class.
With these changed features in mind, the rest of this article concentrates on showing you the details you need to know to migrate an example session bean and an example entity bean from EJB 2.1 to EJB 3.0.
Migrating Session Beans
The example EJB 2.1 session bean class, called BookCatalogBean, specifies an ejbCreate method, a business method called getTitle(), and the callback methods shown below:
// BookCatalogBean.java import javax.ejb.SessionBean; import javax.ejb.SessionContext; public class BookCatalogBean implements SessionBean { private SessionContext ctx; public String getEdition(String title) { if(title.equals("Java & XML")) return new String("2nd edition"); if(title.equals("Java and XSLT")) return new String("1st edition"); } public void ejbCreate(){} public void ejbRemove() {} public void ejbActivate() {} public void ejbPassivate() {} public void setSessionContext(SessionContext ctx) {this.ctx=ctx;} }
|
In EJB 3.0 session beans, you use metadata annotations to specify the bean type? Stateful or Stateless?with the annotations @Stateful and @Stateless respectively. You replace component and home interfaces in a session bean with a business interface. The business interface is a POJI and you can specify it as either local or remote using the annotations @Local and @Remote. A session bean may implement both local and remote interfaces.
If you don’t specify an interface (local or remote) in the bean class, the EJB server automatically generates a local business interface by default. You may also specify the interface class in the @Local and @Remote annotations.
The EJB 3.0 session bean shown below is a POJO named BookCatalogBean.java that is a migrated version of the EJB 2.1 stateless session bean shown earlier. Note that it uses the @Stateless annotation, implements a local business interface, and specifies the local interface class name in the @Local annotation.
// BookCatalogBean.java EJB 3.0 Session Bean @Stateless @Local ({BookCatalogLocal.java}) public class BookCatalogBean implements BookCatalogLocal { public String getEdition(String title) { if(title.equals("Java & XML")) return new String("2nd edition"); if(title.equals("Java and XSLT")) return new String("1st edition"); } }
Also note that the preceding EJB 3.0 bean class replaces the component and home interfaces of the EJB 2.1 version with a local business interface (POJI), annotated with the @Local annotation.
Migrating EJB Session Bean Clients
A client for an EJB 2.1 session bean obtains a session bean object using a JNDI name. The client shown below obtains a local home object using the JNDI name BookCatalogLocalHome, and then calls the create() method. Subsequently, the client outputs the edition value for a specified title with the getEdition(String) business method.
import javax.naming.InitialContext; public class BookCatalogClient { public static void main(String[] argv) { try{ InitialContext ctx=new InitialContext(); Object objref=ctx.lookup( "BookCatalogLocalHome"); BookCatalogLocalHome catalogLocalHome = (BookCatalogLocalHome)objref; BookCatalogLocal catalogLocal = (BookCatalogLocal) catalogLocalHome. create(); String title="Java and XML"; String edition = catalogLocal.getEdition(title); System.out.println("Edition for Title: " + title + " " + edition); } catch(Exception e){} }
}
In EJB 3.0 you obtain a reference to a session bean object via dependency injection, which you implement with the @Inject annotation, the @Resource annotation, or the @EJB annotation. The EJB 3.0 session bean client class shown below injects the BookCatalogBean class with the @Inject annotation. It still obtains the edition value for a title using the getEdition(String) business method.
public class BookCatalogClient { @Inject BookCatalogBean; BookCatalogBean catalogBean; String title="Java and XML"; String edition=catalogBean.getEdition(edition); System.out.println("Edition for Title: " + title + " " + edition); }
Migrating Entity Beans
This section shows how to migrate an EJB 2.1 entity bean to the EJB 3.0 specification. An EJB 2.1 entity bean implements the EntityBean interface and consists of getter and setter CMP field methods, getter and setter CMR field methods, callback methods, and ejbCreate/ejbPostCreate methods. The example entity bean, BookCatalogBean.java (see Listing 1), consists of the CMP fields title, author, and publisher, and the CMR field editions.
The ejb-jar.xml deployment descriptor for this EJB 2.1 entity bean (see Listing 2), specifies the EJB classes and interfaces, CMP fields, EJB QL queries, and CMR relationships. The BookCatalogBean entity bean defines a finder method, findByTitle(), and a CMR field, editions.
In contrast, the EJB 3.0 entity bean class that corresponds to the EJB 2.1 bean class is a Plain Old Java Object (POJO), and it’s far simpler (see Listing 3). The EJB 3.0 version of this bean class uses the metadata annotation @Entity. The finder methods, specified in the EJB 2.1 deployment descriptor ejb-jar.xml file with
Table 1. EJB 3.0 Metadata Annotations: The table shows common EJB 3.0 metadata annotations, a brief description, and when appropriate, the elements affected by the annotation.
Annotation | Description | Annotation Elements |
@Entity | Annotates an entity bean class. | |
@Table | Annotates the entity bean table. If @Table is not specified, the table name is same as the EJB name. | name, schema |
@Id | Annotates a primary key property or field. | |
@Transient | Annotates a non persistent property or field. | |
@Column | Annotates a mapped column for a persistent entity bean property. | name, primaryKey, nullable, length.Default column name is the property or field name. |
@NamedQueries | Annotates a group of named queries. | |
@NamedQuery | Annotates a named query or a query associated with a finder method | name, queryString |
@OneToMany | Annotates a one-to-many association. | Cascade |
@OneToOne | Annotates a one-to-one association. | Cascade |
@ManyToMany | Annotates a many-to-many association. | Cascade |
@ManyToOne | Annotates a many-to-one association. | Cascade |
You’ll find the EJB 3.0 annotation type corresponding to the finder method findByTitle() in the EJB 2.1 bean class, the EJB 3.0 version uses the @NamedQuery annotation. Corresponding to the CMR relationship BookCatalog-Editions in the EJB 2.1 entity bean, the EJB 3.0 entity bean uses a @OneToMany annotation. The @Id annotation annotates the identifier property title. The @Column annotation specifies the database column that corresponds to the identifier property title. If a persistent entity bean property is not annotated with @Column annotation, the EJB server assumes the column name is same as the entity bean property name. Transient entity bean properties are annotated with the @Transient annotation.
Migrating EJB Entity Bean Clients
You create an EJB 2.1 entity bean home or local home object with the create() method in the entity bean home or local home interface. A client for an EJB 2.1 entity bean obtains a local or remote object for the entity bean with JNDI lookup. Here’s an example code snippet that creates an EJB 2.1 entity bean local home object.
InitialContext ctx=new InitialContext(); Object objref=ctx.lookup("BookCatalogLocalHome"); BookCatalogLocalHome catalogLocalHome = (BookCatalogLocalHome)objref;
In the code snippet above, BookCatalogLocalHome is the JNDI name of the BookCatalogBean entity bean.
After obtaining the reference, EJB 2.1 clients create a local object created with the create() method.
BookCatalogLocal catalogLocal = (BookCatalogLocal) catalogLocalHome.create(title);
In EJB 2.1, you obtain a local or remote object from a local home or home object with the finder methods. For example, you can obtain a local object with the findByPrimaryKey method as shown below.
BookCatalogLocal catalogLocal = (BookCatalogLocal) catalogLocalHome.findByPrimaryKey(title);
In EJB 2.1, you remove an entity bean instance with the remove() method:
catalogLocal.remove();
EJB 3.0 implements persistence, lookup, and removal through the javax.persistence.EntityManager class. Table 2 lists some of the commonly used methods in the EntityManager class that replace the EJB 2.1 methods.
Table 2. EntityManager Class Methods: The table shows commonly used EntityManager methods and their descriptions.
EntityManager Method | Description |
persist(Object entity) | Makes an entity bean instance persistent. |
createNamedQuery(String name) | Creates an instance of Query object to execute a named query. |
find(Class entityClass, Object primaryKey) | Finds a entity bean instance. |
createQuery(String ejbQl) | Creates a Query object to run an EJBQL query. |
remove(Object entity) | Removes an entity bean instance. |
In a client class for an EJB 3.0 entity bean, you inject the EntityManager object using the @Resource annotation.
@Resource private EntityManager em;
You persist an entity bean instance by calling the EntityManager.persist() method, for example:
BookCatalogBean catalogBean = new BookCatalogBean (title); em.persist(catalogBean);
Similarly, you obtain an entity bean instance by calling the EntityManager.find() method.
BookCatalogBean catalogBean = (BookCatalogBean) em.find("BookCatalogBean", title);
You can define an EJB 3.0 client class finder method (not the same as an EJB 2.1 finder method) corresponding to the named query findByTitle. In the finder method, obtain a Query object with the createNamedQuery(String) method.
Query query=em.createNamedQuery("findByTitle");
Set the Query object parameters with the setParameter(int paramPosition, String paramValue) method or the setParameter(String parameterName, String value) method. The parameter position is zero-based.
query.setParameter(0, title);
Obtain a Collection of the BookCatalogBean objects with the Query.getResultList() method. If a query returns a single result, use the getSingleResult() method instead.
java.util.Collection catalogBeanCollection = (BookCatalogBean)query.getResultList();
Finally, you remove an entity bean instance with the EntityManager.remove(Object entity) method.
BookCatalogBean catalogBean; em.remove(catalogBean);
Listing 4 shows the complete stateless session bean client class for the EJB 3.0 entity bean.
In this article, you’ve seen an example of the code changes needed to migrate an example session bean and an example entity bean from EJB 2.1 to EJB 3.0. Migration from EJB 2.0 is similar.
As of this writing, several application servers already support the EJB 3.0 specification, including the JBoss application server, the Oracle application server, and the Caucho application server. Unfortunately, the implementations may vary among the different application servers?they may not implement all the EJB 3.0 features, so be sure to check the documentation for your application server.