Oracle® Containers for J2EE Enterprise JavaBeans Developer's Guide 10g Release 3 (10.1.3) B14428-02 |
|
![]() Previous |
![]() Next |
This chapter describes the various options that you must configure in order to use an EJB 2.1 BMP entity bean.
Table 15-1 lists these options and indicates which are basic (applicable to most applications) and which are advanced (applicable to more specialized applications).
For more information, see:
Table 15-1 Configurable Options for an EJB 2.1 BMP Entity Bean
You can configure a BMP entity bean as read-only. By doing so, you enter into a contract with OC4J by which you guarantee not to change the BMP entity bean's state after it is activated. Unlike CMP read-only, no exception will be thrown if you do update a read-only BMP bean.
When you configure a BMP entity bean as read-only, OC4J uses a special case of commit option A (see "Configuring BMP Commit Options") to improve performance by:
Caching the instance
Not calling ejbLoad
after activation
Not updating the instance or calling ejbStore
when the transaction commits
As Figure 15-1 shows, multiple clients accessing the same read-only BMP entity bean by primary key are allocated a single instance. Both Client 1 and Client 2 are satisfied by the same cached read-only BMP entity bean instance. Because the BMP entity bean is read-only, both transactions can proceed in parallel.
Without this optimization, each client is allocated a separate instance and each instance requires the execution of all lifecycle methods.
Figure 15-1 Read-Only BMP Entity Beans and Commit Option A
Example 15-1 shows the orion-ejb-jar.xml
file entity-deployment
element locking-mode
attribute mode
configured to specify a BMP entity bean as read-only.
For a BMP entity bean, you can choose between commit options A and C.
Commit option A offers a performance improvement by postponing a call to ejbLoad
.
If you configure a read-only BMP entity bean to use commit option A (see "Configuring a Read-Only BMP Entity Bean"), you can further improve performance by taking advantage of read-only BMP entity bean caching (see "Commit Options and BMP Applications".
Commit option C is the default.
For more information, see "What are Entity Bean Commit Options?".
Example 15-2 shows the orion-ejb-jar.xml
file entity-deployment
element commit-option
sub-element attribute mode
. Valid settings are A
and C
. The number-of-buckets
attribute is the maximum number of cached instances allowed and is applicable only for commit option A
.
You must implement an ejbFindByPrimaryKey
method for a BMP entity bean (see "Implementing an EJB 2.1 BMP the ejbFindByPrimaryKey Method"). Optionally, you may configure other finders (see "Implementing Other EJB 2.1 BMP Finder Methods").
For more information, see "Using EJB 2.1 Query API".
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 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; }
Optionally, you can create other finder methods in addition to the single ejbFindByPrimaryKey
.
To create other finder methods, do the following:
Add the finder method to the home interface.
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 { ArrayList recs = new ArrayList(); 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; }
In a BMP entity bean, you are responsible for implementing all of the EJB 2.1 BMP entity bean lifecycle callback methods:
The ejbStore
method is called by the container before the object is passivated or whenever a transaction is about to end. Its purpose is to save the persistent data to an outside resource, such as a database
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() 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()); } } }
The ejbLoad
method is called by the container before the object is activated or whenever a transaction has begun, or when an entity bean is instantiated. Its purpose is to restore any persistent data that exists for this particular bean instance
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() 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()); } }
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 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.
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 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 //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()); } } }