Oracle9iAS Containers for J2EE Enterprise JavaBean Developer's Guide and Reference Release 2 (9.0.2) Part Number A95881-01 |
|
An entity bean manages persistent data in one of two manners: container-managed persistence and bean-managed persistence. The primary difference between the two is as follows:
ejbStore
method and reloaded from your storage in the ejbLoad
method. The container invokes these methods when necessary.
This chapter demonstrates simple CMP EJB development with a basic configuration and deployment. Download the CMP entity bean example (cmpapp.jar
) from the OC4J sample code page on the OTN site.
This chapter demonstrates the following:
See Chapter 4, "BMP Entity Beans", for an example of how to create a simple bean-managed persistent entity bean.
To create an entity bean, perform the following steps:
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.
application.xml
file, create an EAR file, and install the EJB in OC4J.
The following sections demonstrate a simple CMP entity bean. This example continues the use of the employee example, as in other chapters--without adding complexity.
The home interface must contain a create
method, which the client invokes to create the bean instance. Each create
method can have a different signature. For an entity bean, you must develop a findByPrimaryKey
method. Optionally, you can develop other finder methods for the bean, which are named find<
name
>
.
To demonstrate an entity bean, this example creates a bean that manages a purchase order. The entity bean contains a list of items that were ordered by the customer.
The home interface extends javax.ejb.EJBHome
and defines the create
and findByPrimaryKey
methods.
package employee;
import javax.ejb.*;
import java.rmi.*;
public interface EmployeeHome extends EJBHome
{
public Employee create(Integer empNo)
throws CreateException, RemoteException;
// Find an existing employee
public Employee findByPrimaryKey
(Integer empNo)
throws FinderException, RemoteException;
//Find all employees
public Collection findAll()
throws FinderException, RemoteException;
}
The entity bean remote interface is the interface that the customer sees and invokes methods upon. It extends javax.ejb.EJBObject
and defines the business logic methods. For our employee entity bean, the remote interface contains methods for adding and removing employees, and retrieving and setting employee information.
package employee; import javax.ejb.*; import java.rmi.*; import java.util.*; 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 setEmpName(String newEmpName) throws RemoteException; public void setSalary(Float newSalary) throws RemoteException; }
The entity bean class must implement the following methods:
ejbCreate
method and any finder methods, including ejbFindByPrimaryKey
EntityBean
interface
However, with container-managed persistence, the container manages most of the target methods and the data objects. This leaves little for you to implement.
package employee; import javax.ejb.*; import java.rmi.*; public class EmployeeBean extends Object implements EntityBean { public Integer empNo; public String empName; public Float salary; public EntityContext entityContext; public EmployeeBean() { // Constructor. Do not initialize anything in this method. // All initialization should be performed in the ejbCreate method. } public Integer getEmpNo() { return empNo; } public String getEmpName() { return empName; } public Float getSalary() { return salary; } public void setEmpName(String empName) { this.empName = empName; } public void setSalary(Float salary) { this.salary = salary; } public Integer ejbCreate(Integer empNo) throws CreateException, RemoteException { this.empNo = empNo; return empNo; } public void ejbPostCreate(Integer empNo) throws CreateException, RemoteException { // Called just after bean created; container takes care of implementation } public void ejbStore() { // Called when bean persisted; container takes care of implementation } public void ejbLoad() { // Called when bean loaded; container takes care of implementation } public void ejbRemove() { // Called when bean removed; container takes care of implementation } public void ejbActivate() { // Called when bean activated; container takes care of implementation. // If you need resources, retrieve them here. } public void ejbPassivate() { // Called when bean deactivated; container takes care of implementation. // if you set resources in ejbActivate, remove them here. } public void setEntityContext(EntityContext entityContext) { this.entityContext = entityContext; } public void unsetEntityContext() { this.entityContext = null; } }
In CMP entity beans, you define the persistent data both in the bean instance and in the deployment descriptor. The declaration of the data fields in the bean instance creates the resources for the fields. The deployment descriptor defines these fields as persistent.
In our employee example, the data fields are defined in the bean instance, as follows:
public Integer empNo; public String empName; public Float salary;
These fields are defined as persistent fields in the ejb-jar.xml
deployment descriptor within the <cmp-field><field-name>
element, as follows:
<enterprise-beans> <entity> <display-name>Employee</display-name> <ejb-name>EmployeeBean</ejb-name> <home>employee.EmployeeHome</home> <remote>employee.Employee</remote> <ejb-class>employee.EmployeeBean</ejb-class> <persistence-type>Container</persistence-type><prim-key-class>java.lang.Integer</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>
<primkey-field>empNo</primkey-field>
</entity> ... </enterprise-beans>
In most cases, you map the persistent data fields to columns in a table that exists in a designated database. However, you can accept the defaults for these fields--thus, avoiding more deployment descriptor configuration.
OC4J contains some defaults for mapping these fields to a database and its table.
data-sources.xml
file. The JNDI name that is used for a pooled database is defined in the <ejb-location>
element.
Upon installation, the default database is a locally installed Oracle database that must be listening on port 5521 with a SID of ORACLE
.
To customize the default database, change the first configured database (including its <ejb-location>
) within the data-sources.xml
to point to your database.
<ejb-name>
), with columns having the same name as the <cmp-field>
elements in the designated database. The data types for the database, translating Java data types to database data types, are defined in the specific database XML file, such as oracle.xml
.
If you want to designate another database or generate a table that has a different naming convention, see "Object-Relational Mapping of Persistent Fields" for a description of how to customize your database, table, and column names.
Each entity bean instance has a primary key that uniquely identifies it from other instances. You must declare the primary key (or the fields contained within a complex primary key) as a container-managed persistent field in the deployment descriptor. All fields within the primary key are restricted to either primitive, serializable, or types that can be mapped to SQL types. You can define your primary key in one of two ways:
<prim-key-class>
in the deployment descriptor. The data field that is identified as the persistent primary key is identified in the <primkey-field>
element in the deployment descriptor. The primary key variable that is declared within the bean class must be declared as public
.
name
>PK
class that is serializable. This class is declared in the <prim-key-class>
element in the deployment descriptor. This is an advanced method for defining a primary key, so it is discussed in "Defining the Primary Key in a Class".
For a simple CMP, you can define your primary key to be a well-known type by defining the data type of the primary key within the deployment descriptor.
The employee example defines its primary key as a java.lang.Integer
and uses the employee number (empNo
) as its primary key.
<enterprise-beans> <entity> <display-name>Employee</display-name> <ejb-name>EmployeeBean</ejb-name> <home>employee.EmployeeHome</home> <remote>employee.Employee</remote> <ejb-class>employee.EmployeeBean</ejb-class> <persistence-type>Container</persistence-type><prim-key-class>java.lang.Integer</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>
<primkey-field>empNo</primkey-field>
</entity> ... </enterprise-beans>
If your primary key is more complex than a simple data type, your primary key must be a class that is serializable of the name <
name
>PK
. You define the primary key class within the <prim-key-class>
element in the deployment descriptor.
The primary key variables must adhere to the following:
<cmp-field><field-name>
element in the deployment descriptor. This enables the container to manage the primary key fields.
public
and restricted to be either primitive, serializable, or types that can be mapped to SQL types.
Within the primary key class, you implement a constructor for creating a primary key instance. Once defined in this manner, the container manages the primary key, as well as storing the persistent data.
The following example is a complex primary key made up of employee number and country code. Our company is so large that it reuses employee numbers in different countries. Thus, the combination of both the employee number and the country code uniquely identifies each employee.
package employee; public class EmpPK implements java.io.Serializable { public Integer empNo; public String countryCode; //constructor public EmpPK ( ) { } }
The primary key class is declared within the <prim-key-class>
element and its variables, each within a <cmp-field><field-name>
element in the XML deployment descriptor, as follows:
<enterprise-beans> <entity> <display-name>Employee</display-name> <ejb-name>EmployeeBean</ejb-name> <home>employee.EmployeeHome</home> <remote>employee.Employee</remote> <ejb-class>employee.EmployeeBean</ejb-class> <persistence-type>Container</persistence-type><prim-key-class>employee.EmpPK</prim-key-class>
<reentrant>False</reentrant><cmp-field><field-name>empNo</field-name></cmp-field>
<cmp-field><field-name>countryCode</field-name></cmp-field>
</entity> ... </enterprise-beans>
Archive your EJB into a JAR file. You deploy the entity bean in the same way as the session bean, which "Prepare the EJB Application for Assembly" and "Deploy the Enterprise Application to OC4J" explain in detail.
This section discusses how to implement your bean beyond the simple CMP entity bean. It includes the following sections:
Specifying the findByPrimaryKey
method is easy to do in OC4J. All the fields for defining a simple or complex primary key are specified within the ejb-jar.xml
deployment descriptor. However, if you want to define other finder methods in a CMP entity bean, you must do the following:
orion-ejb-jar.xml
file.
You must first add the finder method to the home interface. For example, with the employee entity bean, if we wanted to retrieve all employees, the findAll
method would be defined within the home interface, as follows:
public Collection findAll() throws FinderException, RemoteException;
After specifying the finder method in the home interface, modify the orion-ejb-jar.xml
file with the finder method specifics. The container identifies the correct query necessary for retrieving the required fields.
The <finder-method>
element defines all finder methods--excluding the findByPrimaryKey
method. The simplest finder method to define is the findByAll
method. The query
attribute in the <finder-method>
element specifies the WHERE
clause for the query. If you want all rows retrieved, then an empty query (query=""
) returns all records.
The following example retrieves all records from the EmployeeBean
. The method name is findAll
, and it requires no parameters because it returns a Collection
of all employees.
<finder-method query=""> <method> <ejb-name>EmployeeBean</ejb-name> <method-name>findAll</method-name> <method-params></method-params> </method> </finder-method>
After deploying the application with this bean, OC4J adds the following statement of what query it invokes as a comment in the finder method definition:
<finder-method query=""> <!-- Generated SQL: "select EmployeeBean.empNo, EmployeeBean.empName, EmployeeBean.salary from EmployeeBean" --> <method> <ejb-name>EmployeeBean</ejb-name> <method-name>findAll</method-name> <method-params></method-params> </method> </finder-method>
Verify that it is the type of query that you expect.
To be more specific, modify the query
attribute with the appropriate WHERE
clause. This clause refers to passed in parameters using the '$' symbol: the first parameter is denoted by $1, the second by $2. All <cmp-field>
elements that are used within the WHERE
clause are denoted by $<cmp-field>
name.
The following example specifies a findByName
method (which should be defined in the home interface) where the name of the employee is given as in the method parameter, which is substituted for the $1. It is matched to the CMP name, "empName
". Thus, our query
attribute is modified to contain the following for the WHERE
clause: "$empname
=$1
".
<finder-method query="$empname = $1"> <method> <ejb-name>EmployeeBean</ejb-name> <method-name>findByName</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </finder-method>
If you have more than one method parameter, each parameter type is defined in successive <method-param>
elements and referred to in the query statement by successive $n, where n represents the number.
If you wanted to specify a full query and not just the section after the WHERE clause, specify the partial
attribute to FALSE and then define the full query in the query
attribute. The default value for partial
is true, which is why it is not specified on the previous finder-method example.
<finder-method partial="false"
query="select * from EMP where $empName = $1">
<!-- Generated SQL: "select * from EMP where EMP.ENAME = ?" -->
<method>
<ejb-name>EmployeeBean</ejb-name>
<method-name>findByName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
</finder-method>
Specifying the full SQL query is useful for complex SQL statements.
As "Persistent Data" discusses, your persistent data can be automatically mapped to a database table by the container. However, if the data represented by your bean is more complex or you do not want to accept the defaults that OC4J provides for you, then map the CMP designated fields to an existing database table and its applicable rows within the orion-ejb-jar.xml
file. Once mapped, the container provides the persistence storage of the CMP data to the indicated table and rows.
Before configuring the object-relational mapping, add the DataSource
used for the destination within the <resource-ref>
element in the ejb-jar.xml
file.
Configure the following within the orion-ejb-jar.xml
file:
<entity-deployment>
element for every entity bean that contains CMP fields that will be mapped within it.
<cmp-field-mapping>
element for every field within the bean that is mapped. Each <cmp-field-mapping>
element must contain the name of the field to be persisted.
<primkey-mapping>
element contained within its own <cmp-field-mapping>
element.
<cmp-field-mapping>
element. The name and database field are fully defined within the element attributes.
<cmp-field-mapping>
element. These can be one of the following:
<fields>
or <properties>
element.
<entity-ref>
element.
List
, Collection
, Set
, or Map
of fields, then define these fields within the <list-mapping>
, <collection-mapping>
, <set-mapping>
, <map-mapping>
elements.
Examples for simple and complex O-R mappings are shown in the following sections:
The following example demonstrates how to map persistent data fields in your bean instance to database tables and columns by mapping the employee persistence data fields to the Oracle database table EMP
.
<entity-deployment>
name
attribute. The JNDI name for this bean is defined in the location
attribute.
table
attribute. And the database is specified in the data-source
attribute, which should be identical to the <ejb-location>
name of a DataSource
defined in the data-sources.xml
file.
empNo
, is mapped to the database table column, EMPNO
, within the <primkey-mapping>
element.
empName
and salary
, are mapped to the database table columns ENAME
and SAL
within the <cmp-field-mapping>
element.
<entity-deploymentname="EmpBean" location="emp/EmpBean"
wrapper="EmpHome_EntityHomeWrapper2" max-tx-retries="3"
table="emp" data-source="jdbc/OracleDS"
><primkey-mapping>
<cmp-field-mapping name="empNo" persistence-name="empno" />
</primkey-mapping>
<cmp-field-mapping name="empName" persistence-name="ename" />
<cmp-field-mapping name="salary" persistence-name="sal" />
... </entity-deployment>
After deployment, OC4J maps this to the following:
Bean | Database |
---|---|
|
EMP table, located at |
|
|
|
|
|
|
If you have two beans that access two tables for their data, you must map the persistent data from both beans to the respective tables.
We added a department number to our employee example. Each employee belongs to a department; each department has multiple employees. The container will handle this object-relational mapping; however, you must specify how the data is stored.
The employee data maps to the employee database table; the department data is mapped to the database department table. The employee database table also contains a foreign key of the department number to link this information together.
The XML configuration for the employee bean, EmpBean
, is as follows:
deptno
, which relates to the department number defined in the DeptBean
. Thus, the DeptBean
, its deptno
field, and its mapping to the database column, deptno
, is configured within a <cmp-field-mapping><entity-ref>
element, as follows:
<entity-deployment name="EmpBean" location="emp/EmpBean"wrapper="EmpHome_EntityHomeWrapper2" max-tx-retries="3" table="emp" data-source="jdbc/OracleDS"><primkey-mapping> <cmp-field-mapping name="empNo" persistence-name="empno" /> </primkey-mapping> <cmp-field-mapping name="empName" persistence-name="ename" /> <cmp-field-mapping name="salary" persistence-name="sal" /><cmp-field-mapping name="dept">
<entity-ref home="dept/DeptBean">
<cmp-field-mapping name="dept" persistence-name="deptno" />
</entity-ref>
</cmp-field-mapping> <finder-method query=""> <!-- Generated SQL: "select EMP.empno, EMP.ename, EMP.sal, EMP.deptno from EMP" --> <method> <ejb-name>EmpBean</ejb-name> <method-name>findAll</method-name> <method-params></method-params> </method> </finder-method> </entity-deployment>
The XML configuration for the department bean, DeptBean
, is as follows:
<entity-deployment>
name
attribute. The JNDI name for this bean is defined in the location
attribute.
table
attribute. And the database is specified in the data-source
attribute, which should be identical to the <ejb-location>
name of a DataSource
defined in the data-sources.xml
file.
deptNo
, is mapped to the dept
database table in its DEPTNO
column within the <primkey-mapping>
element.
deptName
, is mapped to the DEPT
database table in its DNAME
column within a <cmp-field-mapping>
element.
employees
, is actually a bean--the employee bean. Thus, the example uses the <collection-mapping>
element to specify all fields within the employee bean. A Collection
containing the employee information is returned. See the bold text in the example below.
EmpBean
entity bean. Its home interface reference is defined in the home
attribute of the <entity-ref>
element.
deptNo
within the <primkey-mapping>
element and is mapped to the database column DEPTNO
.
<cmp-field-mapping>
elements. The bean instance fields within the EmpBean
that are of interest are empNo
, empName
, and salary
. Their respective database columns are also specified: EMPNO
, ENAME
, and SAL.
The database table itself is defined in the EmpBean
<entity-deployment>
definition.
<entity-deployment name="DeptBean" location="dept/DeptBean"
wrapper="DeptHome_EntityHomeWrapper2" max-tx-retries="3" table="dept" data-source="jdbc/OracleDS">
<primkey-mapping>
<cmp-field-mapping name="deptNo" persistence-name="deptno" />
</primkey-mapping>
<cmp-field-mapping name="deptName" persistence-name="dname" />
<cmp-field-mapping name="employees">
<collection-mapping table="emp">
<primkey-mapping>
<cmp-field-mapping name="deptNo" persistence-name="deptno" />
</primkey-mapping>
<value-mapping type="emp.Emp">
<cmp-field-mapping>
<entity-ref home="emp/EmpBean">
<cmp-field-mapping name="empNo" persistence-name="empno"/>
<cmp-field-mapping name="empName" persistence-name="ename"/>
<cmp-field-mapping name="salary" persistence-name="sal"/>
</entity-ref>
</cmp-field-mapping>
</value-mapping>
</collection-mapping>
</cmp-field-mapping>
... </entity-deployment>
|
Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|