|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
by Sangeetha S. and Subrahmanya S. V. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 |
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.
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.
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:
// 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 a
mappedName element that specifies the JNDI name of the
destination from which the bean can consume messages.
@MessageDriven(mappedName = "jms/Queue")
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; |
The migration of the EJB 2.x persistence model to EJB 3.0 can happen in three different ways:
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 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.
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.
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 @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.
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@PostLoadThe 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 |
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();
............
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:
// 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.
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 |
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 |
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.
Sangeetha S. works as a technical architect at the E-Commerce Research Labs at Infosys Technologies
Subrahmanya S. V. is an associate vice president at Infosys Technologies and the founder of the E-Commerce Research Labs
View all java.net Articles.
|
|