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

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.

  1. In Project Builder, choose File > New Project.

  2. Choose Enterprise JavaBean Framework as the project type.

  3. Name the project Person.

  4. 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.

Figure 4-1  The Select Model pane of EOBeanAssistant
The Select Model pane of EOBeanAssistant

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.

Figure 4-2  The Select Entities pane of EOBeanAssistant
The Select Entities pane of EOBeanAssistant

The Configure Bean pane (Figure 4-3) provides several options. Make sure Generate ejbFindAll Method, Generate BMP Class, and Generate CMP Class are selected.

Figure 4-3  The Configure Bean pane of EOBeanAssistant
The Configure Bean pane of EOBeanAssistant

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 and ejbFindAll 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.xmlfile or to add or replace only the entries corresponding to the added entity beans.

Figure 4-4  The Select Generation Path pane of EOBeanAssistant
The Select Generation Path pane of EOBeanAssistant

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:

  1. Create a database named Person using OpenBaseManager:

    1. Launch OpenBaseManager, which is located in /Applications/OpenBase/OpenBaseManager.

    2. Choose Database > New.

    3. 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.

    4. In the OpenBaseManager main window, select the Person database under localhost and click Start Database.

  2. Add the PERSON table to the Person database:

    1. Open models/Person.eomodeld in EOModeler.

    2. Choose Property > Generate SQL.

    3. 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:

  1. Create a project named Person_Client.

  2. In the J2EE Integration pane of the Project Builder Assistant, select “Deploy as Enterprise JavaBean container.”

  3. In the Choose EOAdaptors pane of the Assistant, deselect the JDBC adaptor.

  4. In the Choose Frameworks pane, add Person.framework, which is located in the build 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:

  1. Set the table attribute of the map-to element to "PERSON".

  2. Set the name attribute of the sql element to "PERSON_NAME".

  3. Set the name attribute of the sql 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);
 
}