Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2) Part No. B15901-01 |
|
Previous |
Next |
EJB Entity beans represent a business entity. Entity beans can be shared by many users and are long-lived, able to survive a server failure. Essentially, entity beans are persistent data objects (objects with durable state that exist from one moment in time to the next).
This section describes entity bean development, as well as the following mapping topics and techniques:
An EJB implements a business task or a business entity. EJBs are server-side domain objects that fit into a component-based architecture for building enterprise applications using the Java language. EJBs are Java objects that the developer can install in an EJB server to make them distributed, transactional, and secure. OracleAS TopLink supports three kinds of EJBs under the EJB 2.0 specification: session beans, entity beans, and message-driven beans. Note that EJB 1.1 does not support message-driven beans.
An EJB bean resides in an EJB container that, in turn, resides in an EJB server. Although the EJB 2.0 specification does not define the container-server relationship, the accepted paradigm is that the server provides the bean with access to different services (security, transactions, and so on), and the container provides the execution context for the bean by managing its life cycle.
Deployment descriptors supply additional information that is required to install an EJB within its server. The deployment descriptors are a set of XML files that provide the security, transaction, relationship, and persistence information for the bean.
Session beans represent a business task, process, or operation. Although the use of a session bean may involve database access, the beans are not in themselves persistent because they do not directly represent a database entry. Session beans do not always retain conversational state. They can be stateful and retain client information between calls; they can be stateless and retain information only within a single method call.
You can use OracleAS TopLink to make the regular Java objects that are accessed by a session bean persistent, or to access OracleAS TopLink persistent entity beans. Session beans may also act as wrappers to other legacy applications.
Entity Beans
Entity beans represent a persistent data object that exists from one access to the next. You accomplish persistence by storing the object in an object database, relational database, or some other storage facility.
Two schemes exist for making entity beans persistent: bean-managed persistence (BMP) and container-managed persistence (CMP). BMP requires the bean developer to hand-code the methods that perform the persistence work. CMP uses information supplied by the developer to handle all aspects of persistence.
Message-driven beans process asynchronous Java Message Service (JMS) messages. A bean method is transactionally-invoked by a JMS message sent to the objects registered against the given topic. From a client perspective, a message-driven bean is simply a JMS consumer with no conversational state and no Home
or Remote
interfaces.
OracleAS TopLink provides a class oracle.toplink.ejb.bmp.BMPEntityBase
. This class provides you with a starting point when developing beans. The BMPEntityBase
class provides implementation for all EJB specification-required methods except ejbPassivate()
, which is excluded because of special requirements. By subclassing the BMPEntityBase
, you have an entity bean enabled by OracleAS TopLink.
To use the BMPEntityBase
, create the sessions.xml
file. For information about the sessions.xml
file, see "Session Manager". In addition, add an oracle.toplink.ejb.bmp.BMPWrapperPolicy
to each descriptor that represents an Entity Bean. This BMPWrapperPolicy
provides OracleAS TopLink with the information to create Remote objects for entity beans and to extract the data out of a Remote object. After this is performed, you must create the Home
and Remote
interfaces, create deployment descriptors, and deploy the beans.
If a more customized approach is required, OracleAS TopLink provides a hook into its functionality through the oracle.toplink.ejb.bmp.BMPDataStore
class. Use this class to translate EJB-required functionality into simple calls.
The BMPDataStore
provides implementations of LOAD
and STORE
, multiple finders, and REMOVE
functionality. The BMPDataStore
requires a sessions.xml
file and the session manager. A single instance of BMPDataStore
must exist for each bean type deployed within a session. When creating a BMPDataStore
, pass in the session name of the session that the BMPDataStore
must use to persist the beans and the class of the Bean type being persisted. Store the BMPDataStore
in a global location so that each instance of a Bean type uses the correct Store.
If you use a customized implementation, the full functionality of the server session and the UnitOfWork
is available.
To use BMP support with EJB 2.0, the Home
interface must inherit from the oracle.toplink.ejb.EJB20Home
. To make calls to the oracle.toplink.ejb.bmp.BMPEntityBase
, the FindAll()
method must call the EJB 2.0 version of the methods. These methods are prefixed with ejb20
.
For example, in the EJB 2.0 version, the findAll()
method appears as ejb20FindAll
.
Using Local Beans
To use local beans, use the oracle.toplink.ejb.EJB20LocalHome
setting instead of the default oracle.toplink.ejb.EJB20Home
.
Instead of the oracle.toplink.ejb.BMPWrapperPolicy
setting, use the oracle.toplink.ejb.bmp.BMPLocalWrapperPolicy
setting.
To accommodate both local and remote configurations, ensure the following:
For a bean that has a single interface, use the corresponding wrapper policy (local or remote) for the descriptor.
Beans can only participate in relationships only as either Local
or Remote
interfaces—not both.
OracleAS TopLink CMP is an extension of the OracleAS TopLink persistence framework. OracleAS TopLink CMP support provides container-managed persistence for EJBs deployed in a J2EE container.
OracleAS TopLink CMP support enables complex mappings from entity beans to relational database tables and enables you to model bean-to-bean and bean-to-regular Java object relationships. OracleAS TopLink provides a rich set of querying options and allows query definition at the bean-level, rather than the database level. OracleAS TopLink CMP supports the specification as defined by Sun Microsystems.
This section introduces the concepts required to use CMP facilities. It highlights the features that are specific to OracleAS TopLink CMP and explains any differences in the use of other core features.
The common mechanism for you to make beans persistent is to map beans to a relational database. The EJB specification describes the CMP entity bean as a type of bean for which the designer does not have to include calls to any particular persistence mechanism in the bean itself. The EJB Server and its tools use meta-information in the deployment descriptor to describe how the bean is to be persisted to a database. This function is commonly referred to as automatic persistence.
OracleAS TopLink provides support for EJB 2.0 entity beans. Here are some specific features of EJB 2.0 that OracleAS TopLink supports:
Local interfaces and local relationships
Generation of concrete bean subclasses
EJB QL
Automatic management of bidirectional relationships
Initializing a project from the ejb-jar.xml
file
Finders
Home methods
ejbSelect
Table 3-3 describes the components that Java objects contain:
Table 3-3 Java Object Components
Table 3-4 illustrates the components that entity beans contain:
Table 3-4 Entity Bean Components
For more information about the Enterprise JavaBeans and the EJB specification, see
http://java.sun.com/products/ejb/ http://java.sun.com/products/ejb/docs.html http://java.sun.com/j2ee/white/index.html
When one-to-one or many-to-many mappings are bidirectional, you must maintain the back-pointers as the relationships change. When the relationship is between two entity beans (in EJB 2.0), OracleAS TopLink automatically maintains the relationship. However, when the relationship is between an entity bean and a Java object, or when the application is built to the EJB 1.1 specification, the relationship must be maintained manually. To set the back-pointer under the EJB 1.1 specification, do one of the following:
Establish or modify the relationship the entity bean can then maintain the back-pointer.
The client must explicitly set the back-pointer.
If you set back-pointers within the entity bean, the client is freed of this responsibility. This has the advantage of encapsulating the mapping maintenance implementation in the bean.
Note: Under the EJB 1.1 specification, you must manually update all back-pointers. |
In a one-to-many mapping, a source bean may have several dependent target objects. For example, an EmployeeBean
may have several dependent phoneNumbers
. When a new dependent object (a phoneNumber
, in this example) is added to an employee record, the phoneNumber
's back-pointer to its owner (the employee) must also be set.
Example 3-26 Setting the Back-Pointer in the Entity Bean
To maintain a one-to-many relationship in the entity bean you must get the local object reference from the context of the EmployeeBean
and then update the back-pointer. The following code illustrates this technique:
// obtain owner and phoneNumber Employee owner = empHome.findByPrimaryKey(ownerId); PhoneNumber phoneNumber = new PhoneNumber("cell", "613", "5551212"); // add phoneNumber to the phoneNumbers of the owner owner.addPhoneNumber(phoneNumber);
The Employee's addPhoneNumber()
method maintains the relationship, as follows:
public void addPhoneNumber(PhoneNumber newPhoneNumber) { //get, then set the back pointer to the owner Employee owner = (Employee)this.getEntityContext().getEJBLocalObject(); newPhoneNumber.setOwner(owner); //add new phone getPhoneNumbers().add(newPhoneNumber); }
The EJB 1.1 specification recommends that you model entity beans so that all dependent objects are regular Java objects and not other entity beans. If you expose a dependent or privately owned object to the client application, it must be serializable (that is, it must implement the java.io.Serializable interface
) so that it can be sent to the client and back to the server.
Because entity beans are remote objects, they are referenced remotely in a pass-by-reference fashion. When an entity bean is returned to the client, a remote reference to the bean is returned.
Unlike entity beans, regular Java objects are not remote objects. Because of this, when regular Java objects are referenced remotely, they are passed by value (rather than by reference) and serialized (copied) from the remote system on which they originally resided.
One of the effects of serializing regular Java objects between servers and clients is a loss of object identity, due to the copying semantics inherent in serialization. When you serialize a dependent object from the server to the client and then back, two objects with the same primary key but different object identities exist in the server cache. These objects must be merged to avoid exceptions.
If relationships exist between entity beans and Java objects, and these objects are serialized back and forth between the client and server, either:
Use the OracleAS TopLink SessionAccessor
utility class to perform the merge for you.
Merge the objects yourself by adding merge methods on your regular Java objects and within your set methods.
Use the class oracle.toplink.ejb.WebLogic.SessionAccessor
to perform merges for you within the set methods (on your bean class) that take regular Java objects as their arguments.
Two static methods are defined on the SessionAccessor
that allow you to perform the register and merge operation:
registerOrMergeObject()
This method requires two arguments: the object to merge and the EntityContext
for the bean.
Example 3-27 Using the registerOrMergeObject() Method
public void setAddress(Address address) { this.address = (Address)SessionAccessor .registerOrMergeObject(address,this.ctx); }
The registerOrMergeObject()
method is not as simple to use for setters of collection mappings. It requires that you iterate through the collection and invoke the registerOrMergeObject()
for each element in the collection. You must also create a new collection, set in the entity bean, to hold the return values of the call.
Merging code may be required in methods that add elements to a collection.
For example:
/* The old version of this phone number is removed from the collection. It is assumed that equals() returns true for phones with the same primary key value. If this is not true, you must iterated through the phones to see if a phone with the same primary key already exists in the collection. */ public void addPhoneNumber(PhoneNumber phone) { phone.setOwner((Employee)this.ctx.getEJBObject()); //add to collection //merge new phone PhoneNumber serverSidePhone = (PhoneNumber)SessionAccessor.registerOrMergeObject(phone,this.ctx); //set back pointer getPhoneNumbers().addElement(serverSidePhone); }
registerOrMergeAttribute()
This method requires three arguments: the Java object to be merged, the name of the attribute, and the EntityContext
for the bean.
Example 3-28 Using the registerOrMergerAttribute() Method
public void setAddress(Address address) { this.address = (Address) SessionAccessor.registerOrMergeAttribute (address, ÒaddressÓ, this.ctx); }
To use the registerOrMergeAttribute()
call for collection mappings, pass the entire collection as the attribute object.
For example:
public void setPhones(Vector phones) { this.phones = (Vector)SessionAccessor.registerOrMergeAttribute(phones, "phones", this.ctx); //... additional logic to set back-pointers on the phones }
Note: This example requires merging code only if there is a risk that aPhone with the same primary key can be added twice. If the elements in a collection cannot be added more than once, then merging code is not required.
|
There are several ways to merge objects manually. For example, you can use a set()
method, as follows:
public void setAddress(Address address) { if(this.address == null){ this.address = address; } else{ this.address.merge(address); } }
You must merge objects when they are added to a collection on the entity bean unless the objects cannot be added more than once to a collection, in which case merging is not necessary.
Merging a collection requires more work. Determine if a copy of each object already exists in the collection, and if so, merge the two copies. If not, you need only add the new object to the collection.
Unlike EJBs, OracleAS TopLink dependent persistent objects can be sent back and forth between a client and the server. When objects are serialized, the risk exists that the objects can cause the cache to lose the identity of the objects or attempt to cache duplicate identical objects. To avoid potential problems, use the bean set methods when adding dependent objects to relationship collections. This enables OracleAS TopLink to handle merging of objects in the cache.
Collections generally use the equals()
method to compare objects. However, in the case of a Java object that contains a collection of entities, the EJBObjects
do not respond as expected to the equals()
method. If you manage a collection of entities under EJB 1.1, we recommend the use of the isIdentical()
method to avoid problems.
In addition, the standard collection methods, such as remove()
or contains()
, frequently return unexpected results and so must be avoided.
Note: The issue of collection of EJBObjects does not arise in the case of an entity that contains a collection of entities, because the EJB 2.0 container collection used handles equality appropriately. |
Several options are available when dealing with collections of EJBObjects
. One option is to create a helper class to assist with collection-type operations. Example 3-30 shows the use of a helper in the EJBCollectionHelper
distribution.
Example 3-30 Using a Helper Class to Manage a Collection of EJBObjects
public void removeOwner(Employee previousOwner){ EJBCollectionHelper.remove(previousOwner, getOwners()); }
Example 3-31 illustrates the implementation of remove()
and indexOf()
in EJBCollectionHelper
.
Example 3-31 Using remove() and indexOf() in the EJBCollectionHelper
public static boolean remove(javax.ejb.EJBObject ejbObject, Vector vector) { int index = -1; index = indexOf(ejbObject, vector); // indexOf returns -1 if the element is not found. if(index == -1){ return false; } try{ vector.removeElementAt(index); } catch(ArrayIndexOutOfBoundsException badIndex){ return false; } return true; } public static int indexOf(javax.ejb.EJBObject ejbObject, Vector vector) { Enumeration elements = vector.elements(); boolean found = false; int index = 0; javax.ejb.EJBObject current = null; while(elements.hasMoreElements()){ try{ current = (javax.ejb.EJBObject) elements.nextElement(); if(ejbObject.isIdentical(current)){ found = true; break; } }catch(ClassCastException wrongTypeOfElement){ . . . }catch (java.rmi.RemoteException otherError){ . . . } index++; //increment index counter } if(found){ return index; } else{ return -1; } }
You can create a special Collection class that uses isIdentical()
instead of equals()
for its comparison operations. To use isIdentical()
, properly define the equals()
method for the primary key class.