Oracle Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide 10g (9.0.4) Part Number B10324-01 |
|
If you have EJB 1.1 CMP entity beans from last release, this appendix informs you of how OC4J maps your EJB 1.1 CMP deployment descriptor elements to OC4J-specific mappings. Oracle encourages you to migrate to using the EJB 2.0 method for CMP entity beans; however, Oracle supports both specifications.
This chapter demonstrates simple CMP EJB 1.1 development with a basic configuration and deployment. Download the CMP entity bean example from the OC4J sample code page on the OTN Web site.
This chapter demonstrates the following:
See Chapter 3, "CMP Entity Beans" for details on CMP EJB 2.0 entity beans.
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.
<location>
element for emulated data sources and <ejb-location>
element for non-emulated data sources.
Upon installation, the default database is a locally installed Oracle database that must be listening on port 1521 with a SID of ORCL
. To customize the default database, change the first configured database (including its <ejb-location>
) 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 "EJB 1.1 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 EJB 1.1 finder method specifics. The container identifies the correct query necessary for retrieving the required fields.
The EJB 1.1 <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.
For entity bean finder methods, lazy loading can cause the select method to be invoked more than once. To turn on lazy loading and enforce only a single execution of this finder method, set the lazy-loading
property to true.
<finder-method partial="false"
query="select * from EMP where $empName = $1"
lazy-loading=true>
<!-- 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>
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.
Collection
or Set
of fields, then define these fields within the <collection-mapping>
, <set-mapping>
elements.
|
Copyright © 2002, 2003 Oracle Corporation. All Rights Reserved. |
|