Skip Headers
Oracle® Containers for J2EE Enterprise JavaBeans Developer's Guide
10g Release 3 (10.1.3)
B14428-02
  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
 

13 Implementing an EJB 2.1 Entity Bean

This chapter explains how to implement an EJB 2.1 entity bean, including:


Note:

You can download EJB code examples from: http://www.oracle.com/technology/tech/java/oc4j/demos.

For more information, see:

Implementing an EJB 2.1 CMP Entity Bean

Table 13-1 summarizes the important parts of an EJB 2.1 CMP entity bean and the following procedure describes how to implement these parts. For a typical implementation, see "Using Java". For more information, see "What is an EJB 2.1 CMP Entity Bean?".

Table 13-1 Parts of an EJB 2.1 CMP Entity Bean

Part Description

Home Interface (remote or local)

Extends javax.ejb.EJBHome for the remote home interface, javax.ejb.EJBLocalHome for the local home interface, and requires a single create() factory method, with no arguments, and a single remove() method.

Component Interface (remote or local)

Extends javax.ejb.EJBObject for the remote interface and javax.ejb.EJBLocalObject for the local interface. It defines the business logic methods, which are implemented in the bean implementation.

Bean implementation

Implements EntityBean. This class must be declared as public, contain a public, empty, default constructor, no finalize() method, and implements the methods defined in the component interface. Must contain one or more ejbCreate methods to match the create methods in the home interface. Contains empty implementations for the container service methods, such as ejbRemove, and so on.


  1. Create the home interfaces for the bean (see "Implementing the EJB 2.1 Home Interfaces").

    The remote home interface defines the create and finder methods that a client can invoke remotely to instantiate your bean. The local home interface defines the create and finder methods that a collocated bean can invoke locally to instantiate your bean.

    For more information about finders, see "Understanding Finder Methods"

    1. To create the remote home interface, extend javax.ejb.EJBHome (see "Implementing the Remote Home Interface").

    2. To create the local home interface, extend javax.ejb.EJBLocalHome (see "Implementing the Local Home Interface").

  2. Create the component interfaces for the bean (see "Implementing the EJB 2.1 Component Interfaces").

    The remote component interface declares the business methods that a client can invoke remotely. The local interface declares the business methods that a collocated bean can invoke locally.

    1. To create the remote component interface, extend javax.ejb.EJBObject (see "Implementing the Remote Component Interface").

    2. To create the local component interface, extend javax.ejb.EJBLocalObject (see "Implementing the Local Component Interface").

  3. Define the primary key for the bean (see "Configuring an EJB 2.1 CMP Entity Bean Primary Key").

    The primary key identifies each entity bean instance and is a serializable class. You can use a simple data type class, such as java.lang.String, or define a complex class, such as one with two or more objects as components of the primary key.

  4. Implement the CMP entity bean:

    1. Implement the abstract get and set methods that correspond to the get and set method(s) declared in the home interfaces.

      For a CMP entity bean, the get and set methods are public abstract because the container is responsible for their implementation.

    2. Implement the business methods that you declared in the home and component interfaces (if any). The signature for each of these methods must match the signature in the remote or local interface, except that the bean does not throw the RemoteException. Since both the local and the remote interfaces use the bean implementation, the bean implementation cannot throw the RemoteException.

      For an entity bean, these methods are often delegated to a session bean (see "What is a Session Bean?").

    3. Implement any methods that are private to the bean or package used for facilitating the business logic. This includes private methods that your public methods use for completing the tasks requested of them.

    4. Implement the ejbCreate methods that correspond to the create method(s) declared in the home interfaces. The container invokes the appropriate ejbCreate method when the client invokes the corresponding create method.

      The return type of all ebjCreate methods is the type of the bean's primary key.

      For a CMP entity bean, provide create methods that allow the client to pass in values that the container will persist to your database.

    5. Provide an empty implementation for each of the javax.ejb.EntityBean interface container callback methods

    6. Implement a setEntityContext method (that takes an instance of EntityContext) and unsetEntityContext method (see "Implementing the setEntityContext and unsetEntityContext Methods" ).

    7. Optionally, define zero or more public, abstract select methods (see "Understanding Select Methods") for use within the business methods of your entity bean.

  5. Create the appropriate database schema (tables and columns) for the entity bean.

    For a CMP entity bean, you can specify how persistence attributes should be stored in the database or you can configure the container to manage table creation for you.

    For more information, see:

    You can configure the container to create the required tables for CMP beans.

  6. Configure your ejb-jar.xml file to match your bean implementation and to reference a data source defined in your data-sources.xml file (see "Using Deployment XML").

  7. Complete the configuration of your entity bean (see "Using EJB 2.1 CMP Entity Bean API").

Using Java

Example 13-1 shows a typical implementation of an EJB 2.1 CMP entity bean. Example 13-2 shows the corresponding remote home interface and Example 13-3 shows the corresponding remote component interface.

Example 13-1 EJB 2.1 CMP Entity Bean Implementation

package cmpapp;

import javax.ejb.*;
import java.rmi.*;

public abstract class EmployeeBean implements EntityBean
{

    private EntityContext ctx;

    // cmp fields accessors
    public abstract Integer getEmpNo();
    public abstract void setEmpNo(Integer empNo);
 
    public abstract String getEmpName();
    public abstract void setEmpName(String empName);
 
    public abstract Float getSalary();
    public abstract void setSalary(Float salary);
 
    public void EmployeeBean()
    {
        // Empty constructor, don't initialize here but in the create().
        // passivate() may destroy these attributes in the case of pooling
    }

    public EmployeePK ejbCreate(Integer empNo, String empName, Float salary)
        throws CreateException
    {
        setEmpNo(empNo);
        setEmpName(empName);
        setSalary(salary);
        return new EmployeePK(empNo);
    }
 
    public void ejbPostCreate(Integer empNo, String empName, Float salary)
        throws CreateException
    {
        // when just after bean created
    }
 
    public void ejbStore()
    {
        // when bean persisted
    }
 
    public void ejbLoad()
    {
        // when bean loaded
    }
 
    public void ejbRemove()
    {
        // when bean removed
    }
 
    public void ejbActivate()
    {
        // when bean activated
    }
 
    public void ejbPassivate()
    {
        // when bean deactivated
    }
 
    public void setEntityContext(EntityContext ctx)
    {
        this.ctx = ctx;
    }
 
    public void unsetEntityContext()
    {
        this.ctx = null;
    }

}

Example 13-2 EJB 2.1 CMP Remote Home Interface

package cmpapp;

import java.rmi.*;
import java.util.*;
import javax.ejb.*;

public interface EmployeeHome extends EJBHome
{
     public Employee create(Integer empNo, String empName, Float salary)
         throws CreateException, RemoteException;

     public Employee findByPrimaryKey(EmployeePK pk)
         throws FinderException, RemoteException;

     public Collection findByName(String empName)
         throws FinderException, RemoteException;

     public Collection findAll()
         throws FinderException, RemoteException;
}

Example 13-3 EJB 2.1 CMP Remote Component Interface

package cmpapp;

import javax.ejb.*;
import java.rmi.*;

public interface Employee extends EJBObject
{
    // cmp fields accessors
    public Integer getEmpNo() throws RemoteException;
    public void setEmpNo(Integer empNo) throws RemoteException;

    public String getEmpName() throws RemoteException;
    public void setEmpName(String empName) throws RemoteException;

    public Float getSalary() throws RemoteException;
    public void setSalary(Float salary) throws RemoteException;
}

Using Deployment XML

Example 13-4 shows the ejb-jar.xml file entity element corresponding to the CMP entity bean shown in Example 13-1.

Example 13-4 ejb-jar.xml For an EJB 2.1 CMP Entity Bean

...
    <enterprise-beans>
        <entity>
            <description>no description</description>
            <display-name>EmployeeBean</display-name>
            <ejb-name>EmployeeBean</ejb-name>
            <home>cmpapp.EmployeeHome</home>
            <remote>cmpapp.Employee</remote>
            <ejb-class>cmpapp.EmployeeBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>EmployeeBean</abstract-schema-name>
            <prim-key-class>cmpapp.EmployeePK</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-field><field-name>empNo</field-name></cmp-field>
            <cmp-field><field-name>empName</field-name></cmp-field>
            <cmp-field><field-name>salary</field-name></cmp-field>
            <query>
                <description></description>
                <query-method>
                <method-name>findAll</method-name>
                <method-params/>
                </query-method>
                <ejb-ql>Select OBJECT(e) From EmployeeBean e</ejb-ql>
            </query>
            <query>
                <description></description>
                <query-method>
                <method-name>findByName</method-name>
                <method-params>
                <method-param>java.lang.String</method-param>
                </method-params>
                </query-method>
                <ejb-ql>Select OBJECT(e) From EmployeeBean e where e.empName = ?1</ejb-ql>
            </query>
        </entity>
    </enterprise-beans>
...

Implementing an EJB 2.1 BMP Entity Bean

Table 13-2 summarizes the important parts of an EJB 2.1 BMP entity bean and the following procedure describes how to implement these parts. For a typical implementation, see "Using Java". For more information, see "What is an EJB 2.1 BMP Entity Bean?".

Table 13-2 Parts of an EJB 2.1 BMP Entity Bean

Part Description

Home Interface (remote or local)

Extends javax.ejb.EJBHome for the remote home interface, javax.ejb.EJBLocalHome for the local home interface, and requires a single create() factory method, with no arguments, and a single remove() method.

Component Interface (remote or local)

Extends javax.ejb.EJBObject for the remote interface and javax.ejb.EJBLocalObject for the local interface. It defines the business logic methods, which are implemented in the bean implementation.

Bean implementation

Implements EntityBean. This class must be declared as public, contain a public, empty, default constructor, no finalize() method, and implements the methods defined in the component interface. Must contain one or more ejbCreate methods to match the create methods in the home interface. Contains complete implementations for the container service methods, such as ejbStore, ejbLoad, ejbRemove, and so on.


  1. Create the home interfaces for the bean (see "Implementing the EJB 2.1 Home Interfaces").

    The remote home interface defines the create method that a client can invoke remotely to instantiate your bean. The local home interface defines the create method that a collocated bean can invoke locally to instantiate your bean.

    1. To create the remote home interface, extend javax.ejb.EJBHome (see "Implementing the Remote Home Interface").

    2. To create the local home interface, extend javax.ejb.EJBLocalHome (see "Implementing the Local Home Interface").

  2. Create the component interfaces for the bean (see "Implementing the EJB 2.1 Component Interfaces").

    The remote component interface declares the business methods that a client can invoke remotely. The local interface declares the business methods that a collocated bean can invoke locally.

    1. To create the remote component interface, extend javax.ejb.EJBObject (see "Implementing the Remote Component Interface").

    2. To create the local component interface, extend javax.ejb.EJBLocalObject (see "Implementing the Local Component Interface").

  3. Define the primary key for the bean.

    The primary key identifies each entity bean instance and is a serializable class. You can use a simple data type class, such as java.lang.String, or define a complex class, such as one with two or more objects as components of the primary key.

  4. Implement the BMP entity bean:

    1. Provide a complete implementation of the get and set methods that correspond to the get and set method(s) declared in the home interfaces.

      For a BMP entity bean, the get and set methods are public because you are responsible for their implementation.

    2. Implement the business methods that you declared in the home and component interfaces (if any). The signature for each of these methods must match the signature in the remote or local interface, except that the bean does not throw the RemoteException. Since both the local and the remote interfaces use the bean implementation, the bean implementation cannot throw the RemoteException.

      For an entity bean, these methods are often delegated to a session bean (see "What is a Session Bean?").

    3. Implement any methods that are private to the bean or package used for facilitating the business logic. This includes private methods that your public methods use for completing the tasks requested of them.

    4. Implement the ejbCreate methods that correspond to the create method(s) declared in the home interfaces. The container invokes the appropriate ejbCreate method when the client invokes the corresponding create method.

      The return type of all ebjCreate methods is the type of the bean's primary key.

      For a BMP entity bean, provide create methods that allow the client to pass in values that the container will persist to your database. You are responsible for providing an implementation that interacts with your database (usually through straight JDBC calls) to create an instance in the database.

      For more information, see "Implementing an EJB 2.1 BMP ejbCreate Method".

    5. Provide a complete implementation for each of the javax.ejb.EntityBean interface container callback methods (see "Configuring a Lifecycle Callback Method for an EJB 2.1 BMP Entity Bean").

      For a BMP entity bean, you are responsible for providing an implementation for each these methods that interacts with your database (usually through straight JDBC calls) to manage persistence in the database.

    6. Implement a setEntityContext method that takes an instance of EntityContext and unsetEntityContext method (see "Implementing the setEntityContext and unsetEntityContext Methods" ).

    7. Implement the mandatory findByPrimaryKey finder method and, optionally, other finders (see "Configuring an EJB 2.1 BMP Entity Bean Query").

  5. Create the appropriate database schema (tables and columns) for the entity bean.

    For a BMP entity bean, you are responsible for creating this schema in the database (defined in the data-sources.xml file) before your application attempts to create an instance of your BMP entity bean.

  6. Configure your ejb-jar.xml file to match your bean implementation and to reference a data source defined in your data-sources.xml file (see "Using Deployment XML").

  7. Complete the configuration of your entity bean (see "Using EJB 2.1 BMP Entity Bean API").

Using Java

Example 13-5 shows a typical implementation of an EJB 2.1 BMP entity bean. Example 13-7 shows the corresponding home interface and Example 13-6 shows the corresponding remote interface.

Example 13-5 EJB 2.1 BMP Entity Bean Implementation

package bmpapp;
 
import java.util.*;
import java.rmi.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.ejb.*;
 
public class EmployeeBean implements EntityBean
{
 
    public Integer empNo;
 
    public EntityContext ctx;
    private Connection conn = null;
    private PreparedStatement ps = null;
    private EmployeePK pk;
    private static final String dsName = "jdbc/OracleDS";
 
    private static final String insertStatement = 
        "INSERT INTO EMP (EMPNO, ENAME, SAL) VALUES (?, ?, ?)";
    private static final String updateStatement =
        "UPDATE EMP SET ENAME=?, SAL=? WHERE EMPNO=?";
    private static final String deleteStatement =
        "DELETE FROM EMP WHERE EMPNO=?";
    private static final String findAllStatement =
        "SELECT EMPNO, ENAME, SAL FROM EMP";
    private static final String findByPKStatement =
        "SELECT EMPNO, ENAME, SAL FROM EMP WHERE EMPNO = ?";
    private static final String findByNameStatement =
        "SELECT EMPNO, ENAME, SAL FROM EMP WHERE ENAME = ?";
    // or you can define a variable specific to orion to implement finder-method:
    // or use <finder-method/> in orion-ejb-jar.xml
    public static final String findByNameQuery="full: " +
        "SELECT EMPNO, ENAME, SAL FROM EMP WHERE ENAME = $1";
 
    public EmployeeBean()
    {
        // Empty constructor, don't initialize here but in the create().
        // passivate() may destroy these attributes in the case of pooling
    }
 
    public EmployeePK ejbCreate(Integer empNo, String empName, Float salary)
        throws CreateException
    {
        try {
            pk = new EmployeePK(empNo, empName, salary);
            conn = getConnection(dsName);
            ps = conn.prepareStatement(insertStatement);
            ps.setInt(1, empNo.intValue());
            ps.setString(2, empName);
            ps.setFloat(3, salary.floatValue());
            ps.executeUpdate();
            return pk;
        } catch (SQLException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new CreateException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
    }
 
    public void ejbPostCreate(Integer empNo, String empName, Float salary)
        throws CreateException
    {
    }
 
    public EmployeePK ejbFindByPrimaryKey(EmployeePK pk)
        throws FinderException
    {
        if (pk == null || pk.empNo == null) {
            throw new FinderException("Primary key cannot be null");
        }
        try {
            conn = getConnection(dsName);
            ps = conn.prepareStatement(findByPKStatement);
            ps.setInt(1, pk.empNo.intValue());
            ps.executeQuery();
            ResultSet rs = ps.getResultSet();
            if (rs.next()) {
                pk.empNo = new Integer(rs.getInt(1));
                pk.empName = new String(rs.getString(2));
                pk.salary = new Float(rs.getFloat(3));
            } else {
                throw new FinderException("Failed to select this PK");
            }
        } catch (SQLException e) {
            throw new FinderException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
        return pk;
    }
 
    public Collection ejbFindAll() throws FinderException
    {
        //System.out.println("EmployeeBean.ejbFindAll(): begin");
        Vector recs = new Vector();
        try {
            conn = getConnection(dsName);
            ps = conn.prepareStatement(findAllStatement);
            ps.executeQuery();
            ResultSet rs = ps.getResultSet();
            int i = 0;
            while (rs.next()) {
                pk = new EmployeePK();
                pk.empNo = new Integer(rs.getInt(1));
                pk.empName = new String(rs.getString(2));
                pk.salary = new Float(rs.getFloat(3));
                recs.add(pk);
            }
        } catch (SQLException e) {
            throw new FinderException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
        return recs;
    }
 
    public Collection ejbFindByName(String empName)
        throws FinderException
    {
        //System.out.println("EmployeeBean.ejbFindByName(): begin");
        if (empName == null) {
            throw new FinderException("Name cannot be null");
        }
        Vector recs = new Vector();
        try {
            conn = getConnection(dsName);
            ps = conn.prepareStatement(findByNameStatement);
            ps.setString(1, empName);
            ps.executeQuery();
            ResultSet rs = ps.getResultSet();
            int i = 0;
            while (rs.next()) {
                pk = new EmployeePK();
                pk.empNo = new Integer(rs.getInt(1));
                pk.empName = new String(rs.getString(2));
                pk.salary = new Float(rs.getFloat(3));
                recs.add(pk);
            }
        } catch (SQLException e) {
            throw new FinderException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
        return recs;
    }
 
    public void ejbLoad() throws EJBException 
    {
        //Container invokes this method to instruct the instance to
        //synchronize its state by loading it from the underlying database
        //System.out.println("EmployeeBean.ejbLoad(): begin");
        try {
            pk = (EmployeePK) ctx.getPrimaryKey();
            ejbFindByPrimaryKey(pk);
        } catch (FinderException e) {
            throw new EJBException (e.getMessage());
        }
    }
 
    public void ejbStore() throws EJBException
    {
        //Container invokes this method to instruct the instance to
        //synchronize its state by storing it to the underlying database
        //System.out.println("EmployeeBean.ejbStore(): begin");
        try {
            pk = (EmployeePK) ctx.getPrimaryKey();
            conn = getConnection(dsName);
            ps = conn.prepareStatement(updateStatement);
            ps.setString(1, pk.empName);
            ps.setFloat(2, pk.salary.floatValue());
            ps.setInt(3, pk.empNo.intValue());
            if (ps.executeUpdate() != 1) {
                throw new EJBException("Failed to update record");
            }
        } catch (SQLException e) {
            throw new EJBException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
    }
 
    public void ejbRemove() throws RemoveException
    {
        //Container invokes this method befor it removes the EJB object
        //that is currently associated with the instance
        //System.out.println("EmployeeBean.ejbRemove(): begin");
        try {
            pk = (EmployeePK) ctx.getPrimaryKey();
            conn = getConnection(dsName);
            ps = conn.prepareStatement(deleteStatement);
            ps.setInt(1, pk.empNo.intValue());
            if (ps.executeUpdate() != 1) {
                throw new RemoveException("Failed to delete record");
            }
        } catch (SQLException e) {
            throw new RemoveException(e.getMessage());
        } catch (NamingException e) {
            System.out.println("Caught an exception 1 " + e.getMessage() );
            throw new EJBException(e.getMessage());
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new EJBException(e.getMessage());
            }
        }
    }
 
    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
        //System.out.println("EmployeeBean.ejbActivate(): begin");
    }
 
    public void ejbPassivate()
    {
        // Container invokes this method on an instance before the instance
        // becomes disassociated with a specific EJB object
        //System.out.println("EmployeeBean.ejbPassivate(): begin");
    }
 
    public void setEntityContext(EntityContext ctx)
    {
        //Set the associated entity context
        //System.out.println("EmployeeBean.setEntityContext(): begin");
        this.ctx = ctx;
    }
 
    public void unsetEntityContext()
    {
        //Unset the associated entity context
        //System.out.println("EmployeeBean.unsetEntityContext(): begin");
        this.ctx = null;
    }
 
    /**
     * methods inherited from EJBObject
     */
    public Integer getEmpNo()
    {
        pk = (EmployeePK) ctx.getPrimaryKey();
        return pk.empNo;
    }
 
    public String getEmpName()
    {
        pk = (EmployeePK) ctx.getPrimaryKey();
        return pk.empName;
    }
 
    public Float getSalary()
    {
        pk = (EmployeePK) ctx.getPrimaryKey();
        return pk.salary;
    }
 
    public void setEmpNo(Integer empNo)
    {
        pk = (EmployeePK) ctx.getPrimaryKey();
        pk.empNo = empNo;
    }
 
    public void setEmpName(String empName)
    {
        pk = (EmployeePK) ctx.getPrimaryKey();
        pk.empName = empName;
    }
 
    public void setSalary(Float salary) {
        pk = (EmployeePK) ctx.getPrimaryKey();
        pk.salary = salary;
    }
 
    public EJBHome getEJBHome()
    {
        return ctx.getEJBHome();
    }
 
    public Handle getHandle() throws RemoteException
    {
        return ctx.getEJBObject().getHandle();
    }
 
    public Object getPrimaryKey() throws RemoteException
    {
        return ctx.getEJBObject().getPrimaryKey();
    }
 
    public boolean isIdentical(EJBObject remote) throws RemoteException
    {
        return ctx.getEJBObject().isIdentical(remote);
    }
 
    public void remove() throws RemoveException, RemoteException{
        ctx.getEJBObject().remove();
    }
 
    /**
     * Private methods
     */
    private Connection getConnection(String dsName)
             throws SQLException, NamingException
    {
        DataSource ds = getDataSource(dsName);
        return ds.getConnection();
    }
 
    private DataSource getDataSource(String dsName) throws NamingException
    {
        DataSource ds = null;
        Context ic = new InitialContext();
        ds = (DataSource) ic.lookup(dsName);
        return ds;
    }
}

Example 13-6 EJB 2.1 BMP Remote Home Interface

package bmpapp;
 
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
 
public interface EmployeeHome extends EJBHome
{
 
    public Employee create(Integer empNo, String empName, Float salary)
        throws CreateException, RemoteException;
 
    public Employee findByPrimaryKey(EmployeePK pk)
        throws FinderException, RemoteException;
 
    public Collection findByName(String empName)
        throws FinderException, RemoteException;
 
    public Collection findAll()
        throws FinderException, RemoteException;
}

Example 13-7 EJB 2.1 BMP Remote Component Interface

package bmpapp;
 
import java.rmi.*;
import javax.ejb.*;
 
public interface Employee extends EJBObject
{
    // getter remote methods
    public Integer getEmpNo() throws RemoteException;
    public String getEmpName() throws RemoteException;
    public Float getSalary() throws RemoteException;
 
    // setter remote methods
    public void setEmpNo(Integer empNo) throws RemoteException;
    public void setEmpName(String empName) throws RemoteException;
    public void setSalary(Float salary) throws RemoteException;
}

Using Deployment XML

Example 13-8 shows the ejb-jar.xml entity element corresponding to the BMP entity bean shown in Example 13-5.

Example 13-8 ejb-jar.xml For an EJB 2.1 BMP Entity Bean

...
    <enterprise-beans>
        <entity>
            <description>no description</description>
            <display-name>EmployeeBean</display-name>
            <ejb-name>EmployeeBean</ejb-name>
            <home>bmpapp.EmployeeHome</home>
            <remote>bmpapp.Employee</remote>
            <ejb-class>bmpapp.EmployeeBean</ejb-class>
            <persistence-type>Bean</persistence-type>
            <prim-key-class>bmpapp.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>
...

Example 13-9 shows the data-sources.xml file data-source element ejb-location attribute that specifies the res-ref-name (jdbc/OracleDS) used in the ejb-jar.xml file shown in Example 13-8.

Example 13-9 data-sources.xml For an EJB 2.1 BMP Entity Bean Data Source

    <connection-pool name="Example Connection Pool">
        <!-- This is an example of a connection factory that emulates XA behavior. -->
        <connection-factory factory-class="oracle.jdbc.pool.OracleDataSource"
            user="scott"
            password="tiger"
            url="jdbc:oracle:thin:@//localhost:1521/oracle.regress.rdbms.dev.us.oracle.com">
        </connection-factory>
    </connection-pool>
 
    <managed-data-source name="OracleDS"
        connection-pool-name="Example Connection Pool"
        jndi-name="jdbc/OracleDS"/>

Implementing an EJB 2.1 BMP ejbCreate Method

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:

For simplicity, the try blocks within the samples have been removed in this example.

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 "Using Deployment XML".

<enterprise-beans>
   <entity>
      <display-name>EmployeeBean</display-name>
      <ejb-name>EmployeeBean</ejb-name>
      <local-home>employee.EmployeeLocalHome</local-home>
      <local>employee.EmployeeLocal</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;

import java.io.*;
java.io.Serializable;

...

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.EmployeeLocalHome</local-home>
      <local>employee.EmployeeLocal</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 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;
}

Implementing the EJB 2.1 Home Interfaces

The home interfaces are used to specify what methods a client uses to create or retrieve an entity bean instance.

The home interface must contain a create method, which the client invokes to create the bean instance. The entity bean can have zero or more create methods, each with its own defined parameters. For each create method, you define a corresponding ejbCreate method in the bean implementation.

All entity beans must define one or more finder methods in the home interface, where at least one is a findByPrimaryKey method. Optionally, you can define other finder methods, which are named find<name>, including predefined and default finders. For more information, see "Understanding Finder Methods".

In addition to creation and retrieval methods, you can provide home interface business methods within the home interface. The functionality within these methods cannot access data of a particular entity object. Instead, the purpose of these methods is to provide a way to retrieve information that is not related to a single entity bean instance. When the client invokes any home interface business method, an entity bean is removed from the pool to service the request. Thus, this method can be used to perform operations on general information related to the bean.

For example, in an employee application, you might provide the local home interface with a create, findByPrimaryKey, findAll, and calcSalary methods. The calcSalary method is a home interface business method that calculates the sum of all employee salaries. It does not access the information of a particular employee, but performs a SQL query against the database for all employees.

There are two types of home interface:

Implementing the Remote Home Interface

A remote client invokes the EJB through its remote interface. The client invokes the create method that is declared within the remote home interface. The container passes the client call to the ejbCreate method—with the appropriate parameter signature—within the bean implementation. The requirements for developing the remote home interface include:

  • The remote home interface must extend the javax.ejb.EJBHome interface.

  • All create methods may throw the following exceptions:

    • javax.ejb.CreateException

    • javax.ejb.EJBException or another RuntimeException

Example 13-2 shows the remote home interface corresponding to the EJB 2.1 CMP entity bean in Example 13-1 and Example 13-6 shows the remote home interface corresponding to the EJB 2.1 BMP entity bean in Example 13-5.

Implementing the Local Home Interface

An EJB can be called locally from a client that exists in the same container. Thus, a collocated bean, JSP, or servlet invokes the create method that is declared within the local home interface. The container passes the client call to the ejbCreate method—with the appropriate parameter signature—within the bean implementation. The requirements for developing the local home interface include:

  • The local home interface must extend the javax.ejb.EJBLocalHome interface.

  • All create methods may throw the following exceptions:

    • javax.ejb.CreateException

    • javax.ejb.EJBException or another RuntimeException

Implementing the EJB 2.1 Component Interfaces

The component interfaces define the business methods of the bean that a client can invoke.

The entity bean component interface is the interface that the client can invoke its methods with. The component interface defines the business logic methods for the entity bean instance.

There are two types of component interface:

Implementing the Remote Component Interface

The remote interface defines the business methods that a remote client can invoke. The requirements for developing the remote component interface include:

  • The remote component interface of the bean must extend the javax.ejb.EJBObject interface, and its methods must throw the java.rmi.RemoteException exception.

  • You must declare the remote interface and its methods as public for remote clients.

  • The remote component interface, all its method parameters, and return types must be serializable. In general, any object that is passed between the client and the EJB must be serializable, because RMI marshals and unmarshalls the object on both ends.

  • Any exception can be thrown to the client. Runtime exceptions, including EJBException and RemoteException, are transferred back to the client as remote runtime exceptions.

  • A remote component interface can throw user specified application exceptions.

Example 13-3 shows the remote component interface corresponding to the EJB 2.1 CMP entity bean in Example 13-1 and Example 13-7 shows the remote component interface corresponding to the EJB 2.1 BMP entity bean in Example 13-5.

Implementing the Local Component Interface

The local component interface defines the business methods of the bean that a local (collocated) client can invoke. The requirements for developing the local component interface include:

  • The local component interface of the bean must extend the javax.ejb.EJBLocalObject interface.

  • You declare the local component interface and its methods as public.

Implementing the setEntityContext and unsetEntityContext Methods

An entity bean instance uses this method to retain a reference to its context. Entity beans have contexts that the container maintains and makes available to the beans. The bean may use the methods in the entity context to retrieve information about the bean, such as security, and transactional role. Refer to the Enterprise JavaBeans specification from Sun Microsystems for the full range of information that you can retrieve about the bean from the context.

The container invokes the setEntityContext method, after it first instantiates the bean, to enable the bean to retrieve the context. The container will never call this method from within a transaction context. If the bean does not save the context at this point, the bean will never gain access to the context.


Note:

You can also use the setEntityContext and unsetEntityContext methods to allocate and destroy any resources that will exist for the lifetime of the instance.

When the container calls this method, it passes the reference of the EntityContext object to the bean. The bean can then store the reference for later use. The following example shows the bean saving the context in the this.ctx variable.

You use this method to obtain a reference to the context of the bean. Entity beans have entity contexts that the container maintains and makes available to the beans. The bean may use the methods in the entity context to make callback requests to the container.

Example 13-10 shows an entity bean saving the session context in the entityctx variable.

Example 13-10 Implementing the setEntityContext and unsetEntityContext Methods

import javax.ejb.*;

public class MyBean implements EnityBean {
   EntityContext entityctx;

   public void setEntityContext(EntityContext ctx) {
      entityctx = ctx;   // entity context is stored in instance variable
   }

    public void unsetEntityContext() {
        entityctx = null;
    }

   // other methods in the bean
}