Skip Headers

Oracle9iAS Containers for J2EE Enterprise JavaBeans Developer's Guide and Reference
Release 2 (9.0.2)

Part Number A95881-01
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

4
BMP Entity Beans

You must implement the storing and reloading of data in a bean-managed persistent (BMP) bean. The bean implementation manages the data within callback methods. All the logic for storing data to your persistent storage is included in the ejbStore method, and reloaded from your storage in the ejbLoad method. The container invokes these methods when necessary.

This chapter demonstrates simple BMP EJB development with a basic configuration and deployment. Download the BMP entity bean example (bmpapp.jar) from the OC4J sample code page on the OTN site.

The following sections discuss how to implement data persistence:

Creating BMP Entity Beans

As Chapter 3, "CMP Entity Beans" indicates, the steps for creating an entity bean are as follows:

  1. Create a remote interface for the bean. The remote interface declares the methods that a client can invoke. It must extend javax.ejb.EJBObject.

  2. Create a home interface for the bean. The home interface must extend javax.ejb.EJBHome. It defines the create and finder methods, including findByPrimaryKey, for your bean.

  3. Define the primary key for the bean. The primary key identifies each entity bean instance. The primary key must be either a well-known class, such as java.lang.String, or defined within its own class.

  4. Implement the bean. This includes the following:

    1. The implementation for the methods declared in your remote interface.

    2. The methods that match the methods that are declared in your home interface. This includes the following:

      • The ejbCreate, which must create the persistent data, and ejbPostCreate methods with parameters matching each of the create methods defined in the home interface.

      • An ejbFindByPrimary key method, which corresponds to the findByPrimaryKey method of the home interface, retrieves the primary key and validates that it exists.

      • Any other finder methods that were defined in the home interface.

    3. The methods defined in the javax.ejb.EntityBean interface. The ejbCreate, ejbPostCreate, and ejbFindByPrimaryKey are already mentioned above. The other methods are as follows:

      • Persistent saving of the data within the ejbStore method.

      • Restoring the persistent data to the bean within your implementation of the ejbLoad method.

      • Passivation of the bean instance within the ejbPassivate method.

      • Activation of the passivated bean instance within the ejbActivate method.

  5. If the persistent data is saved to or restored from a database, you must ensure that the correct tables exist for the bean.

  6. Create the bean deployment descriptor. The deployment descriptor specifies properties for the bean through XML properties.

  7. Create an EJB JAR file containing the bean, the remote and home interfaces, and the deployment descriptor. Once created, configure application.xml, create an EAR file, and install the EJB in OC4J.

Remote and Home Interface

The BMP entity bean definition of the remote and home interfaces are identical to the CMP entity bean. For examples of how the remote and home interface are implemented, see "Creating Entity Beans".

BMP Entity Bean Implementation

Because the container is no longer managing the primary key nor the saving of the persistent data, the bean callback functions must include the implementation logic for these functions. The container invokes the ejbCreate, ejbFindByPrimaryKey, other finder methods, ejbStore, and ejbLoad methods when it is appropriate.

The ejbCreate Implementation

The ejbCreate method is responsible primarily for the creation of the primary key. This includes creating the primary key, creating the persistent data representation for the key, initializing the key to a unique value, and returning this key to the container. The container maps the key to the entity bean reference.

The following example shows the ejbCreate method for the employee example, which initializes the primary key, empNo. It should automatically generate a primary key that is the next available number in the employee number sequence. However, for this example to be simple, the ejbCreate method requires that the user provide the unique employee number.

In addition, because the full data for the employee is provided within this method, the data is saved within the context variables of this instance. After initialization, it returns this key to the container.

// The create methods takes care of generating a new empNo and returns
// its primary key to the container
public Integer ejbCreate (Integer empNo, String empName, Float salary) 
throws CreateException, RemoteException { this.empNo = empNo; this.empName = empName; this.salary = salary; return (empNo); }

The deployment descriptor defines only the primary key class in the <prim-key-class> element. Because the bean is saving the data, there is no definition of persistence data in the deployment descriptor. Note that the deployment descriptor does define the database the bean uses in the <resource-ref> element. For more information on database configuration, see "Modify XML Deployment Descriptors".

<enterprise-beans>
   <entity>
      <display-name>EmployeeBean</display-name>
      <ejb-name>EmployeeBean</ejb-name>
      <home>employee.EmployeeHome</home>
      <remote>employee.Employee</remote>
      <ejb-class>employee.EmployeeBean</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <resource-ref>
            <res-ref-name>jdbc/OracleDS</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Application</res-auth>
      </resource-ref>
   </entity>
</enterprise-beans>

Alternatively, you can create a complex primary key based on several data types. You define a complex primary key within its own class, as follows:

package employee;

public class EmployeePK implements java.io.Serializable
{
  public Integer empNo;
  public String empName;
  public Float salary;

  public EmployeePK(Integer empNo)
  {
    this.empNo = empNo;
    this.empName = null;
    this.salary = null;
  }

  public EmployeePK(Integer empNo, String empName, Float salary)
  {
    this.empNo = empNo;
    this.empName = empName;
    this.salary = salary;
  }
  
}

For a primary key class, you define the class in the <prim-key-class> element, which is the same for the simple primary key definition.

<enterprise-beans>
   <entity>
      <display-name>EmployeeBean</display-name>
      <ejb-name>EmployeeBean</ejb-name>
      <home>employee.EmployeeHome</home>
      <remote>employee.Employee</remote>
      <ejb-class>employee.EmployeeBean</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>employee.EmployeePK</prim-key-class>
      <reentrant>False</reentrant>
      <resource-ref>
            <res-ref-name>jdbc/OracleDS</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Application</res-auth>
      </resource-ref>
   </entity>
</enterprise-beans>

The employee example requires that the employee number is given to the bean by the user. Another method would be to generate the employee number by computing the next available employee number, and use this in combination with the employee's name and office location.

After defining the complex primary key class, you would create your primary key within the ejbCreate method, as follows:

public EmployeePK ejbCreate(Integer empNo, String empName, Float salary)
    throws CreateException, RemoteException
{
  pk = new EmployeePK(empNo, empName, salary);
  ...
}

The other task that the ejbCreate (or ejbPostCreate) should handle is allocating any resources necessary for the life of the bean. For this example, because we already have the information for the employee, the ejbCreate performs the following:

  1. Retrieves a connection to the database. This connection remains open for the life of the bean. It is used to update employee information within the database. It should be released in ejbPassivate and ejbRemove, and reallocated in ejbActivate.

  2. Updates the database with the employee information.

This is executed, as follows:

public EmployeePK ejbCreate(Integer empNo, String empName, Float salary)
    throws CreateException, RemoteException
{
  pk = new EmployeePK(empNo, empName, salary);
  conn = getConnection(dsName);
  ps = conn.prepareStatement(INSERT INTO EMPLOYEEBEAN (EmpNo, EmpName, SAL) 
	VALUES ( this.empNo.intValue(), this.empName, this.salary.floatValue());
  ps.close();
  return pk;
}

The ejbFindByPrimaryKey Implementation

The ejbFindByPrimaryKey implementation is a requirement for all BMP entity beans. Its primary responsibility is to ensure that the primary key is valid. Once it is validated, it returns the primary key to the container, which uses the key to return the remote interface reference to the user.

This sample verifies that the employee number is valid and returns the primary key, which is the employee number, to the container. A more complex verification would be necessary if the primary key was a class.

public Integer ejbFindByPrimaryKey(Integer empNoPK)
    throws FinderException, RemoteException
{
  if (empNoPK == null) {
       throw new FinderException("Primary key cannot be null");
  }

  ps = conn.prepareStatement(SELECT EMPNO FROM EMPLOYEEBEAN 
		WHERE EMPNO = ?);
  ps.setInt(1, empNoPK.intValue());
  ps.executeQuery();
  ResultSet rs = ps.getResultSet();
  if (rs.next()) {
   /*PK is validated because it exists already*/
  } else {
  throw new FinderException("Failed to select this PK");
 }

 ps.close();
 
 return empNoPK;
}

Other Finder Methods

You can create other finder methods beyond the single ejbFindByPrimaryKey.

To create other finder methods, do the following:

  1. Add the finder method to the home interface.

  2. Implement the finder method in the BMP bean implementation.

These finder methods need only to gather the primary keys for all of the entity beans that should be returned to the user. The container maps the primary keys to references to each entity bean within either a Collection (if multiple references are returned) or to the single class type.

The following example shows the implementation of a finder method that returns all employee records.

public Collection ejbFindAll() throws FinderException, RemoteException
{
  Vector recs = new Vector();

  ps = conn.prepareStatement(SELECT EMPNO FROM EMPLOYEEBEAN);
  ps.executeQuery();
  ResultSet rs = ps.getResultSet();

  int i = 0;

  while (rs.next()) 
  {
   retEmpNo = new Integer(rs.getInt(1));
   recs.add(retEmpNo);
  }

 ps.close();
 return recs;
}

The ejbStore Implementation

The container invokes the ejbStore method when the persistent data should be saved to the database. This includes whenever the primary key is "dirtied", or before the container passivates the bean instance or removes the instance. The BMP bean is responsible for ensuring that all data is stored to some resource, such as a database, within this method.

public void ejbStore() throws RemoteException
{
 //Container invokes this method to instruct the instance to
 //synchronize its state by storing it to the underlying database
 ps = conn.prepareStatement(UPDATE EMPLOYEEBEAN SET EMPNAME=?,
		 SALARY=? WHERE EMPNO=?);
 ps.setString(1, this.empName);
 ps.setFloat(2, this.salary.floatValue());
 ps.setInt(3, this.empNo.intValue());
 if (ps.executeUpdate() != 1) {
        throw new RemoteException("Failed to update record");
 }
 ps.close();
}

The ejbLoad Implementation

The container invokes the ejbLoad method after activating the bean instance. The purpose of this method is to repopulate the persistent data with the saved state. For most ejbLoad methods, this implies reading the data from a database into the instance data variables.

public void ejbLoad() throws RemoteException
{
  //Container invokes this method to instruct the instance to
  //synchronize its state by loading it from the underlying database
  this.empNo = ctx.getPrimaryKey();
  ps = conn.prepareStatement(SELECT EMP_NO, EMP_NAME, SALARY WHERE EMPNAME=?");
  ps.setInt(1, this.empNo.intValue());
  ps.executeQuery();
  ResultSet rs = ps.getResultSet();
  if (rs.next()) {
    this.empNo = new Integer(rs.getInt(1));
    this.empName = new String(rs.getString(2));
    this.salary = new Float(rs.getFloat(3));
  } else {
    throw new FinderException("Failed to select this PK");
  }
  ps.close();
}

The ejbPassivate Implementation

The ejbPassivate method is invoked directly before the bean instance is serialized for future use. Normally, this is invoked when the instance has not been used in a while. It will be re-activated, through the ejbActivate method, the next time the user invokes a method on this instance.

Before the bean is passivated, you should release all resources and release any static information that would be too large to be serialized. Any large, static information that can be easily regenerated within the ejbActivate method should be released in this method.

In our example, the only resource that cannot be serialized is the open database connection. It is closed in this method and reopened in the ejbActivate method.

public void ejbPassivate()
{
 // Container invokes this method on an instance before the instance
 // becomes disassociated with a specific EJB object
 conn.close();
}

The ejbActivate Implementation

As the ejbPassivate method section states, the container invokes this method when the bean instance is reactivated. That is, the user has asked to invoke a method on this instance. This method is used to open resources and rebuild static information that was released in the ejbPassivate method.

Our employee example opens the database connection where the employee information is stored.

public void ejbActivate() throws RemoteException
{
 // Container invokes this method when the instance is taken out
 // of the pool of available instances to become associated with
 // a specific EJB object
 conn = getConnection(dsName);
}

The ejbRemove Implementation

The container invokes the ejbRemove method before removing the bean instance itself or by placing the instance back into the bean pool. This means that the information that was represented by this entity bean should be removed--both by the instance being destroyed and removed from within persistent storage. The employee example removes the employee and all associated information from the database before the instance is destroyed. Close the database connection.

public void ejbRemove() throws RemoveException, RemoteException
{
 //Container invokes this method befor it removes the EJB object 
 //that is currently associated with the instance
 ps = conn.prepareStatement(DELETE FROM EMPLOYEEBEAN WHERE EMPNO=?);
 ps.setInt(1, this.empNo.intValue());
 if (ps.executeUpdate() != 1) {
        throw new RemoteException("Failed to delete record");
 }
 ps.close();
 conn.close();
}

Modify XML Deployment Descriptors

In addition to the configuration described in "Creating Entity Beans", you must modify and add the following to your ejb-jar.xml deployment descriptor:

  1. Configure the persistence type to be "Bean" in the <persistence-type> element.

  2. Configure an resource reference for the database persistence storage in the <resource-ref> element.

    Our employee used the database environment element of "jdbc/OracleDS". This is configured in the <resource-ref> element as follows:

    <resource-ref>
     <res-ref-name>jdbc/OracleDS</res-ref-name>
     <res-type>javax.sql.DataSource</res-type>
     <res-auth>Application</res-auth>
    </resource-ref>
    
    
    

The database specified in the <res-ref-name> element maps to a <ejb-location> element in the data-sources.xml file. Our "jdbc/OracleDS" database is configured in the data-sources.xml file, as shown below:

<data-source
  class="com.evermind.sql.DriverManagerDataSource"
  name="Oracle"
  location="jdbc/OracleCoreDS"
  pooled-location="jdbc/pool/OraclePoolDS"
  ejb-location="jdbc/OracleDS"
  xa-location="jdbc/xa/OracleXADS"
  connection-driver="oracle.jdbc.driver.OracleDriver"
  url="jdbc:oracle:thin:@localhost:5521:orcl"
  username="scott"
  password="tiger"
  max-connections="300"
  min-connections="5"
  max-connect-attempts="10"
  connection-retry-interval="1"
  inactivity-timeout="30"
  wait-timeout="30"
/>

Create Database Table and Columns for Entity Data

If your entity bean stores its persistent data within a database, you need to create the appropriate table with the proper columns for the entity bean. This table must be created before the bean is loaded into the database. The container will not create this table for BMP beans, but it will create it automatically for CMP beans.

In our employee example, you must create the following table in the database defined in the data-sources.xml file:

Table Columns

EMPLOYEEBEAN

  • employee number (EMPNO)

  • employee name (EMPNAME)

  • salary (SALARY)

The following shows the SQL commands that create these fields.

CREATE TABLE EMPLOYEEBEAN (
 EMPNO NUMBER NOT NULL,
 EMPNAME VARCHAR2(255) NOT NULL,
 SALARY FLOAT NOT NULL,
 CONSTRAINT EMPNO PRIMARY KEY
)


Go to previous page Go to next page
Oracle
Copyright © 2002 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index