Skip Headers

Oracle® Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide
10g Release 2 (10.1.2)
Part No. B15505-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

BMP Entity Bean Implementation

Because the container is not managing the primary key or 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 appropriate.

The following sections talk about how you add the implementation for managing your BMP bean:

The ejbCreate Implementation

The ejbCreate method is responsible primarily for the creation of the primary key. This includes the following:

  1. Creating the primary key.

  2. Creating the persistent data representation for the key.

  3. Initializing the key to a unique value and ensuring no duplication.

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


Note:

All Try blocks within the samples have been removed in this discussion. However, the entire BMP entity bean example, including the Try blocks, is available on OTN from the OC4J sample code page at http://www.oracle.com/technology/tech/java/oc4j/demos/ on the OTN Web site.

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
{
 /* in this implementation, the client gives the employee number, so
    only need to assign it, not create it. */
 this.empNo = empNo;
 this.empName = empName;
 this.salary = salary;
  
  /* insert employee into database */
  conn = getConnection(dsName);
  ps = conn.prepareStatement("INSERT INTO EMPLOYEEBEAN (EmpNo, EmpName, SAL) 
	VALUES ( "+this.empNo.intValue()+", "+this.empName+"," 	+this.salary.floatValue()+")");
  ps.executeUpdate();
  ps.close();

  /* return the new primary key.*/
  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>
      <local-home>employee.EmployeeHome</local-home>
      <local>employee.Employee</local>
      <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>
      <local-home>employee.EmployeeHome</local-home>
      <local>employee.Employee</local>
      <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
{
  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
{
  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.executeUpdate();
  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 corresponds to a valid bean. Once it is validated, it returns the primary key to the container, which uses the key to return the bean 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
{
  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 in addition to 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.

Finders can retrieve one or more beans according to the WHERE clause. If more than a single bean is returned, then a Collection of primary keys must be returned by the BMP finder method. 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
{
  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 synchronizes the state of the instance to the entity in the underlying database. For example, the container invokes 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()
{
 //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 EJBException("Failed to update record");
 }
 ps.close();
}

The ejbLoad Implementation

The container invokes the ejbLoad method whenever it needs to synchronize the state of the bean with what exists in the database. This method is invoked after activating the bean instance to refresh it with the state that is in the database. 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()
{
  //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. 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

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.


Note:

The container may call the ejbActivate() method multiple times when the bean is associated with several wrappers.

In addition, the container invokes this method after the start of any transaction.

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

public void ejbActivate()
{
 // 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 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
{
 //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 RemoveException("Failed to delete record");
 }
 ps.close();
 conn.close();
}