Oracle9iAS Containers for J2EE Enterprise JavaBean Developer's Guide and Reference Release 2 (9.0.2) Part Number A95881-01 |
|
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:
As Chapter 3, "CMP Entity Beans" indicates, the steps for creating an entity bean are as follows:
javax.ejb.EJBObject
.
javax.ejb.EJBHome
. It defines the create
and finder methods, including findByPrimaryKey
, for your bean.
java.lang.String
, or defined within its own class.
ejbCreate
, which must create the persistent data, and ejbPostCreate
methods with parameters matching each of the create
methods defined in the home interface.
ejbFindByPrimary
key method, which corresponds to the findByPrimaryKey
method of the home interface, retrieves the primary key and validates that it exists.
javax.ejb.EntityBean
interface. The ejbCreate
, ejbPostCreate
, and ejbFindByPrimaryKey
are already mentioned above. The other methods are as follows:
application.xml
, create an EAR file, and install the EJB in OC4J.
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".
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
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 IntegerejbCreate
(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:
ejbPassivate
and ejbRemove
, and reallocated in ejbActivate
.
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 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; }
You can create other finder methods beyond the single ejbFindByPrimaryKey
.
To create other finder methods, do the following:
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 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 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
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(); }
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 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(); }
In addition to the configuration described in "Creating Entity Beans", you must modify and add the following to your ejb-jar.xml
deployment descriptor:
Bean
" in the <persistence-type>
element.
<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"
/>
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 |
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 )
|
Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|