Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Developing Entity Beans
This chapter guides you through the development of an entity-bean framework based on a data model and its use in an application. It also shows you the entity-bean source files that the Project Builder Assistant can generate for you, which include source files for CMP (container-managed persistence) entity beans, BMP (bean-managed persistence) entity beans, and DAO (Data Access Object) source files used to customize data-store access.
The chapter contains the following sections:
Developing an Entity Bean From a Data Model shows you how to create an enterprise-bean framework using EOBeanBuilder.
Using an Entity-Bean Framework explains how an entity-bean framework can be used in a Web application.
Advanced Entity-Bean Development shows the source files that EOBeanBuilder generates for bean-managed persistence beans and DAO-based data-store access.
Developing an Entity Bean From a Data Model
This section shows how to use EOBeanAssistant to create an enterprise-bean framework based on entities defined in a model file created using EOModeler.
This document’s companion files include a simple model file named Person.eomodeld
, located in the models
directory.
Creating an Empty Bean Framework
Before generating the source files for an entity bean, there needs to be a project to which you add the files. You can create an empty bean-framework project or you may add the generated files to an existing bean-framework project. This section walks you through creating an empty bean framework.
In Project Builder, choose File > New Project.
Choose Enterprise JavaBean Framework as the project type.
Name the project
Person
.Make sure “Empty Enterprise JavaBean framework” is selected in the Create New Enterprise JavaBean pane of the Project Builder Assistant.
Generating Enterprise-Bean Source Files From a Model File Using EOBeanAssistant
EOBeanAssistant is an application that creates the Java source files and the ejb-jar.xml
file you need to implement an entity bean. It’s located in /Developer/Applications
. Figure 4-1 shows the Select Model pane of EOBeanAssistant. Enter the path to your model in the text field or click Select Model and navigate to it.
In the Select Entities pane (Figure 4-2), select the entity or entities you want to generate enterprise-bean source files for and click the right-pointing arrow so that they move from the box on the left to the one on the right.
The Configure Bean pane (Figure 4-3) provides several options. Make sure Generate ejbFindAll Method, Generate BMP Class, and Generate CMP Class are selected.
The following list explains the text fields and options in the Configure Bean pane:
- Package
The package name to use in the Java source files and the deployment descriptor.
- Home Interface
The name of the home-interface class of the entity bean.
- Remote Interface
The name of the remote-interface class of the entity bean.
- Primary Key Class Name
The data type you want to use for the primary key of the entity bean.
- Generate ejbFindAll Method
When selected, EOBeanAssistant generates
findAll
andejbFindAll
methods.- Generate BMP Class
When selected, EOBeanAssistant generates a BMP source file for the entity bean.
- BMP Class Name
The name of the BMP class.
- JNDI Data-Source Name
The name of the JNDI (Java Naming and Directory Service) data source that identifies the data store.
- BMP Class Extends CMP Class
When selected, the BMP source file generated by EOBeanAssistant extends the CMP class.
- Generate DAO Class
When selected, EOBeanAssistant generates DAO class files that implement database-specific logic.
- DAO Interface Name
The name of the DAO interface.
- JDBC Class Name
The name of the JDBC class used to communicate with the data store.
- Generate CMP Class
When selected, EOBeanAssistant generates a CMP source file for the entity bean.
- CMP Class Name
The name of the CMP class.
The Common Bean Configuration pane allows you to enter header information that you want all the source files generated to contain.
The Select Generation Path pane (Figure 4-4) allows you to enter or choose the path of the bean-framework project folder you want to add the generated source files to. You can choose to overwrite the existing ejb-jar.xml
file or to add or replace only the entries corresponding to the added entity beans.
Listing 4-1 shows the files EOBeanAssistant adds to the Person
project directory.
Listing 4-1 Enterprise bean files added by EOBeanAssistant to the Person
project directory
Person/ |
EOBeanBuilder.xml |
Person.java |
PersonBMP.java |
PersonCMP.java |
PersonHome.java |
META-INF/ |
ejb-jar.xml |
Using an Entity-Bean Framework
In this section you create an application that uses the entity-bean framework developed in Developing an Entity Bean From a Data Model.
Before you can successfully run the application, you must create the Person database. Use OpenBaseManager to import the database in the databases
directory, which is part of the companion resources of this document. Alternatively, you can do the following:
Create a database named
Person
using OpenBaseManager:Launch OpenBaseManager, which is located in
/Applications/OpenBase/OpenBaseManager
.Choose Database > New.
In the Configure Database dialog, enter
Person
in the Database Name text input field, select Start Database at Boot, choose ASCII from the Internal Encoding pop-up menu, and click Set.In the OpenBaseManager main window, select the Person database under localhost and click Start Database.
Add the PERSON table to the Person database:
Open
models/Person.eomodeld
in EOModeler.Choose Property > Generate SQL.
Make sure only the Create Tables option is selected in the SQL Generation dialog and click Execute SQL.
Developing the Application Project
Follow these steps to develop the client-application project:
Create a project named
Person_Client
.In the J2EE Integration pane of the Project Builder Assistant, select “Deploy as Enterprise JavaBean container.”
In the Choose EOAdaptors pane of the Assistant, deselect the JDBC adaptor.
In the Choose Frameworks pane, add
Person.framework
, which is located in thebuild
directory of the Person project directory.
Defining Data Sources
In Project Builder, select GlobalTransactionConfiguration.xml
under the Resources group. You should see the file shown in Listing 4-2.
Listing 4-2 Person_Client project—GlobalTransactionConfiguration.xml
file
<!DOCTYPE databases PUBLIC "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN" |
"http://www.apple.com/webobjects/5.2/DTDs/jdo-conf.dtd"> |
<database name="Global_TX_Database" engine="oracle">// 1 |
<jndi name="java:comp/env/jdbc/DefaultCMPDatasource" /> |
<mapping href="Contents/Resources/CMPConfiguration.xml" /> |
</database> |
In the line numbered 1, change the value of the engine
attribute to "generic"
.
For more information on data-source definition, see Defining Data Sources.
Mapping Enterprise Beans to Data-Store Tables
Select CMPConfiguration.xml
under the Resources group of the Files list. You should see the file shown in Listing 4-3.
Listing 4-3 Person_Client project—CMPConfiguration.xml
file
<?xml version="1.0" encoding="UTF-8"?> |
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" |
"http://www.apple.com/webobjects/5.2/DTDs/mapping.dtd"> |
<mapping> |
<class identity="person_ID" name="my.ejb.PersonCMP"> |
<map-to table="*** MARKER table name ***"/>// 1 |
<field direct="true" name="personName" type="java.lang.String"> |
<sql name="personName" type="varchar"/>// 2 |
</field> |
<field direct="true" name="person_ID" type="java.lang.Integer"> |
<sql name="person_ID" type="integer"/>// 3 |
</field> |
</class> |
</mapping> |
Change the numbered lines according to these instructions:
Set the
table
attribute of themap-to
element to"PERSON"
.Set the
name
attribute of thesql
element to"PERSON_NAME"
.Set the
name
attribute of thesql
element to"PERSON_ID"
.
For more information on mapping enterprise beans to tables, see Mapping Enterprise Beans to Database Tables.
Configuring the Transaction Manager
Select TransactionManagerConfiguration.xml
and edit it so that it looks like Listing 4-4.
Listing 4-4 Person_Client project—TransactionManagerConfiguration.xml
file
<domain> |
<name>default</name> |
<resources> |
<dataSource> |
<name>DefaultDatabase</name> |
<class>tyrex.resource.jdbc.xa.EnabledDataSource</class> |
<jar>/Library/Java/Extensions/OpenBaseJDBC.jar</jar> |
<config> |
<driverName>jdbc:openbase://localhost/Person</driverName> |
<driverClassName>com.openbase.jdbc.ObDriver</driverClassName> |
<transactionTimeout>60</transactionTimeout> |
<isolationLevel>Serializable</isolationLevel> |
</config> |
<limits> |
<maximum>100</maximum> |
<minimum>10</minimum> |
<initial>10</initial> |
<maxRetain>300</maxRetain> |
<timeout>50</timeout> |
</limits> |
</dataSource> |
</resources> |
</domain> |
For more information on transaction manager configuration, see Transaction Manager Configuration .
Creating, Retrieving, and Removing Person Beans
Now, add the application’s business logic. Select Application.java
under Classes in the Files list and modify it to match Listing 4-5.
Listing 4-5 Person_Client project—Application.java
file
import my.ejb.Person; |
import my.ejb.PersonHome; |
import java.rmi.RemoteException; |
import javax.ejb.CreateException; |
import javax.ejb.FinderException; |
import javax.ejb.RemoveException; |
import javax.naming.Context; |
import javax.naming.InitialContext; |
import javax.naming.NamingException; |
import javax.rmi.PortableRemoteObject; |
import com.webobjects.foundation.*; |
import com.webobjects.appserver.*; |
public class Application extends WOApplication { |
private PersonHome _personHome = null; |
public static void main(String argv[]) { |
WOApplication.main(argv, Application.class); |
} |
public Application() { |
super(); |
System.out.println("Welcome to " + this.name() + "!"); |
// Create Person records. |
createPeople(); |
// Show Person records. |
showPeople(); |
// Remove Person records. |
removePeople(); |
} |
/** |
* Creates Person records. |
*/ |
public void createPeople() { |
System.out.println(); |
System.out.println("Creating Person records."); |
String[] names = {"Susana", "Charles", "Maria", "August"}; |
NSArray personNames = new NSArray(names); |
for (int id = 1; id <= personNames.count(); id++) { |
addPerson(id, (String)personNames.objectAtIndex(id - 1)); |
} |
System.out.println(); |
} |
/** |
* Displays Person records. |
*/ |
public void showPeople() { |
System.out.println("Showing Person records:"); |
for (int id = 1; ; id++) { |
try { |
Integer personID = new Integer(id); |
Person person = personHome().findByPrimaryKey(personID); |
System.out.println("Name: " + person.getPersonName()); |
} |
catch (FinderException fe) { |
break; |
} |
catch (RemoteException re) { |
re.printStackTrace(); |
} |
} |
System.out.println(); |
} |
/** |
* Removes Person records. |
*/ |
public void removePeople() { |
int id = 1; |
System.out.println("Removing Person records:"); |
while (removePerson(id++)) |
; |
System.out.println(); |
} |
/** |
* Adds a Person record. |
*/ |
public void addPerson(int id, String name) { |
try { |
Integer personID = new Integer(id); |
if (personIDIsAvailable(personID)) { |
Person person = personHome().create(personID, name); |
System.out.println("Added " + name + "."); |
} |
else { |
System.out.println(name + " not added because ID " + personID + " is in use."); |
} |
} |
catch (RemoteException re) { |
re.printStackTrace(); |
} |
catch (CreateException ce) { |
// Unable to create record: Do nothing. |
} |
} |
/** |
* Removes a Person record. |
* @return <code>true</code> when successful, <code>false</code> otherwise. |
*/ |
public boolean removePerson(int id) { |
boolean removed = false; |
try { |
Integer personID = new Integer(id); |
// FinderException is thrown when the record doesn't exist. |
Person person = personHome().findByPrimaryKey(personID); |
System.out.println("Deleting " + person.getPersonName() + "."); |
personHome().remove(personID); |
removed = true; |
} |
catch (FinderException fe) { |
// Record not found: Do nothing. |
} |
catch (RemoteException re) { |
re.printStackTrace(); |
} |
catch (RemoveException re) { |
re.printStackTrace(); |
} |
return removed; |
} |
/** |
* Determines whether a personID has been used. |
* @param personID the value to check; |
* @return <code>true</code> when personID is available, |
* <code>false</code> otherwise. |
*/ |
public boolean personIDIsAvailable(Integer personID) { |
boolean personID_available = true; |
try { |
personHome().findByPrimaryKey(personID); |
personID_available = false; |
} |
catch (FinderException fe) { |
// personID is available: Do nothing. |
} |
catch (RemoteException re) { |
re.printStackTrace(); |
personID_available = false; |
} |
return personID_available; |
} |
/** |
* Obtains Person's home interface. |
* @return Person's home interface. |
*/ |
public PersonHome personHome() { |
if (_personHome == null) { |
try { |
Context jndiContext = new InitialContext(); |
_personHome = (PersonHome)PortableRemoteObject.narrow(jndiContext.lookup("PersonCMP"), PersonHome.class); |
} |
catch (NamingException ne) { |
ne.printStackTrace(); |
} |
} |
return _personHome; |
} |
} |
Build and run the application. You should see the following output in the console:
Welcome to Person_Client! |
Creating Person records. |
Added Susana. |
Added Charles. |
Added Maria. |
Added August. |
Showing Person records: |
Name: Susana |
Name: Charles |
Name: Maria |
Name: August |
Removing Person records: |
Deleting Susana. |
Deleting Charles. |
Deleting Maria. |
Deleting August. |
Advanced Entity-Bean Development
This section addresses support for advanced entity-bean development, mainly bean-managed persistence (BMP) and Data Access Objects (DAO).
Bean-Managed Persistence
CMP beans are easy to use because the bean container performs the data-store operations; you don’t need to worry about executing SQL statements. However, you can use BMP beans when you need to customize the way your beans store and retrieve data from a data store.
If you select Generate BMP Class in the Configure Bean pane of EOBeanAssistant when you develop an entity bean from a data model, you get a file similar to the one shown in Listing 4-6.
Listing 4-6 PersonBMP.java
file
package my.ejb; |
import javax.sql.DataSource; |
import javax.naming.InitialContext; |
import javax.ejb.*; |
import java.sql.*; |
public class PersonBMP implements EntityBean { |
protected DataSource _datasource; |
protected EntityContext _entityContext; |
protected java.lang.String personName; |
protected java.lang.Integer person_ID; |
/** |
* Empty constructor as required by the EJB spcification. |
*/ |
public PersonBMP() { |
} |
/** |
* This method creates a new entity from all required (non-NULL) |
* attributes. |
* @returns the primary key for this bean instance. |
* @throws javax.ejb.CreateException |
*/ |
public java.lang.Integer ejbCreate(java.lang.Integer person_ID) throws CreateException{ |
this.person_ID = person_ID; |
Connection connection = null; |
PreparedStatement statement = null; |
try { |
String sqlString = "INSERT INTO PERSON (PERSON_ID) VALUES (?)"; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setInt(1, person_ID.intValue()); |
int ret=statement.executeUpdate(); |
if ( ret != 1 ) { |
throw new CreateException(); |
} |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
return person_ID; |
} |
/** |
*/ |
public void ejbPostCreate(java.lang.Integer person_ID) { |
} |
/** |
* This method creates a new entity from all attributes. |
* @returns the primary key for this bean instance. |
* @throws javax.ejb.CreateException |
*/ |
public java.lang.Integer ejbCreate(java.lang.Integer person_ID, java.lang.String personName) throws CreateException { |
this.personName = personName; |
this.person_ID = person_ID; |
Connection connection = null; |
PreparedStatement statement = null; |
try { |
String sqlString = "INSERT INTO PERSON (PERSON_NAME, PERSON_ID) VALUES (?,?)"; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setString(1, personName); |
statement.setInt(2, person_ID.intValue()); |
int ret=statement.executeUpdate(); |
if (ret != 1 ) { |
throw new CreateException(); |
} |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
return person_ID; |
} |
/** |
*/ |
public void ejbPostCreate(java.lang.Integer person_ID, java.lang.String personName) { |
} |
/** |
* This method returns all entities in this table. |
* @returns all entities in the table in a java.util.Collection. |
* @throws javax.ejb.FinderException if there are problems connecting |
* to the database or executing the query. |
*/ |
public java.util.Collection ejbFindAll() throws FinderException { |
Connection connection = null; |
PreparedStatement statement = null; |
try { |
String sqlString = "SELECT PERSON_ID FROM PERSON"; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
ResultSet resultSet = statement.executeQuery(); |
java.util.Collection primaryKeys = new java.util.ArrayList(); |
while(resultSet.next()) { |
java.lang.Integer primaryKey = new java.lang.Integer(resultSet.getInt(1)); |
primaryKeys.add(primaryKey); |
} |
resultSet.close(); |
return primaryKeys; |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
} |
/** |
* This method returns the bean associated with the primaryKey argument, |
* or throws a javax.ejb.ObjectNotFoundException. |
* @returns the bean associated with the primaryKey argument. |
* @throws javax.ejb.ObjectNotFoundException if an entity with the given |
* primary key doesn't exist. |
*/ |
public java.lang.Integer ejbFindByPrimaryKey(java.lang.Integer primaryKey) throws FinderException { |
Connection connection=null; |
PreparedStatement statement=null; |
try { |
String sqlString = "SELECT PERSON_ID FROM PERSON WHERE PERSON_ID = ? "; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setInt(1, primaryKey.intValue()); |
ResultSet resultSet = statement.executeQuery(); |
boolean found = resultSet.next(); |
resultSet.close(); |
if (found) { |
return primaryKey; |
} |
else { |
throw new ObjectNotFoundException("Could not find: " + primaryKey); |
} |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
} |
/** |
*/ |
public java.lang.String getPersonName(){ |
return personName; |
} |
/** |
*/ |
public void setPersonName(java.lang.String personName){ |
this.personName = personName; |
} |
/** |
*/ |
public java.lang.Integer getPerson_ID() { |
return person_ID; |
} |
public void ejbRemove() throws RemoveException { |
Connection connection = null; |
PreparedStatement statement = null; |
try { |
String sqlString = "DELETE FROM PERSON WHERE PERSON_ID = ? "; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setInt(1, person_ID.intValue()); |
if (statement.executeUpdate() != 1) { |
throw new RemoveException(); |
} |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
} |
public void ejbActivate() { |
java.lang.Integer person_ID = (java.lang.Integer) _entityContext.getPrimaryKey(); |
this.person_ID = person_ID; |
} |
public void ejbPassivate() { |
} |
public void ejbLoad() { |
Connection connection = null; |
PreparedStatement statement = null; |
try { |
String sqlString = "SELECT PERSON_NAME FROM PERSON WHERE PERSON_ID = ? "; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setInt(1, person_ID.intValue()); |
ResultSet resultSet = statement.executeQuery(); |
if(!resultSet.next()) { |
resultSet.close(); |
throw new NoSuchEntityException("ejbLoad failed. Primary key not found: "+_entityContext.getPrimaryKey()); |
} |
personName = resultSet.getString(1); |
resultSet.close(); |
} |
catch(SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
} |
public void ejbStore() { |
Connection connection=null; |
PreparedStatement statement=null; |
try { |
String sqlString = "UPDATE PERSON SET PERSON_NAME = ? WHERE PERSON_ID = ? "; |
connection = _datasource.getConnection(); |
statement = connection.prepareStatement(sqlString); |
statement.setString(1, personName); |
statement.setInt(2, person_ID.intValue()); |
if(statement.executeUpdate() != 1) { |
throw new NoSuchEntityException("ejbStore failed. Primary key not found: "+_entityContext.getPrimaryKey()); |
} |
} |
catch (SQLException e) { |
throw new EJBException(e); |
} |
finally { |
cleanup(connection, statement); |
} |
} |
public void setEntityContext(EntityContext entityContext) { |
_entityContext = entityContext; |
try { |
InitialContext context = new InitialContext(); |
_datasource = (DataSource) context.lookup("java:comp/env/jdbc/DataSource"); |
} |
catch(Exception ne) { |
throw new EJBException(ne); |
} |
} |
public void unsetEntityContext() { |
_entityContext = null; |
} |
private void cleanup(Connection connection, PreparedStatement statement) { |
if(statement != null) { |
try{ |
statement.close(); |
} |
catch(SQLException e) { |
// Do nothing. |
} |
} |
if(connection != null) { |
try { |
connection.close(); |
} |
catch(SQLException e) { |
// Do nothing. |
} |
} |
} |
} |
Data Access Objects
When you select Generate DAO Class in the Configure Bean pane of EOBeanAssistant, you get a file similar to the one listed in Listing 4-7.
Listing 4-7 PersonDAO.java
file
package my.ejb; |
public interface PersonDAO { |
/** |
* This method creates a new entity from its required attributes. |
* @throws javax.ejb.CreateException; |
* @returns the primary key for this bean instance. |
*/ |
public java.lang.Integer ejbCreate(java.lang.Integer person_ID) throws javax.ejb.CreateException; |
/** |
* This method creates a new entity from all attributes. |
* @throws javax.ejb.CreateException; |
* @returns the primary key for this bean instance. |
*/ |
public java.lang.Integer ejbCreate(java.lang.Integer person_ID, java.lang.String personName) throws javax.ejb.CreateException; |
/** |
* This method returns the bean associated with the primaryKey argument, |
* or throws a javax.ejb.ObjectNotFoundException. |
* @throws javax.ejb.ObjectNotFoundException if an entity with the given |
* primary key doesn't exist; |
* @returns the bean associated with the primaryKey argument. |
*/ |
public java.lang.Integer ejbFindByPrimaryKey(java.lang.Integer primaryKey) throws javax.ejb.FinderException; |
public void ejbRemove() throws javax.ejb.RemoveException; |
public void ejbLoad(); |
public void ejbStore(); |
public void setBeanInstance(PersonBMP bean, javax.sql.DataSource datasource); |
} |
Copyright © 2001, 2004 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2004-10-05