Implementing the BMP Entity Bean

This section discusses the implementation of the remote home interface StudentHome, remote interface Student, and the BMP bean class StudentEJB. We'll also discuss how to write the DAO and deployment descriptors in detail.

Defining the Home Interface

The home interface provides methods for creating and removing enterprise beans. In addition, for entity beans, the home interface of an entity bean contains methods to find the instances of a bean based on certain search criteria. The home interface for an entity bean may also contain home business methods. Listing 10.1 shows the StudentHome remote home interface.

Listing 10.1. The Full Text of day10/StudentHome.java
package day10;

import javax.ejb.*;
import java.util.Collection;
import java.rmi.RemoteException;

public interface StudentHome extends EJBHome
{
   /* Create methods */
   Student create(String studentId, String firstName, String lastName,
                  String address) throws CreateException, RemoteException;
   /* Finder methods */
   public Student findByPrimaryKey(String studentId)
      throws FinderException, RemoteException;
   public Collection findByLastName(String lastName)
      throws FinderException, RemoteException;
   /* Home methods */
   public int getTotalNumberOfStudents() throws RemoteException;
}

To create the bean instance, a client calls the create() method of the home interface and passes appropriate values for studentId, firstName, lastName, and address.

The StudentHome home interface provides two finder methods: findByPrimaryKey() and findByLastName(). The findByPrimaryKey() method allows clients to locate a student bean using the primary key. The single-object finder method findByPrimaryKey() returns the Student remote interface. The findByLastName() method allows clients to locate students with a given last name. The multi-object finder method findByLastName() returns java.util.Collection. The container throws a FinderException from the implementation of finder methods to indicate an application-level error.

The remote home interface is a Java Remote Invocation Method (RMI) interface. So, the method arguments and return types of a remote method must be legal types for RMI/IIOP (RMI over Internet Inter-Orb Protocol) and the method must include java.rmi.RemoteException in its throws clause.

Note

Single-object finder methods (such as findByPrimaryKey()) are designed to return one entity object at most. For single-object finders, the result type of the method in the entity bean's remote home interface is the entity bean's remote interface. Similarly, the result type of the method in the entity bean's local home interface is the entity bean's local interface.

Multi-object finder methods (such as findByLastName()) are designed to return multiple entity objects. For multi-object finders, the result type of the method in the entity bean's remote home interface is a collection of objects implementing the entity bean's remote interface. Similarly, the result type of the method in the entity bean's local home interface is a collection of objects implementing the entity bean's local interface. You use the java.util.Collection interface to define the collection type for the result type of a container-managed persistence entity bean.


Predefined Exceptions for Entity Beans

Figure 10.4 shows the application exceptions for entity beans as defined in the Enterprise JavaBeans 2.0 specification.

Figure 10.4. Predefined exceptions for entity beans.


The enterprise bean throws CreateException from the ejbCreate<method>(...) and ejbPostCreate<method>(...) methods to indicate an application-level error from the create or initialization operation.

The enterprise bean throws DuplicateKeyException from the ejbCreate<method>(...) to indicate to the client that an entity object with the same key already exists. DuplicateKeyException is a subclass of CreateException.

The enterprise bean throws FinderException from the ejbFind<method>(...) to indicate to the client that an application-level error occurred in the finder method.

The enterprise bean throws ObjectNotFoundException from the ejbFind<method>(...) to indicate to the client that the requested entity object does not exist.

The enterprise bean throws RemoveException from the ejbRemove() method to indicate to the client that an application-level error occurred in the entity bean removal operation.

Caution

Only single-object finders should throw the ObjectNotFoundException exception. Multi-object finders should return an empty collection as an indication that no matching objects were found.


Defining the Component Interface

The remote interface Student is defined as follows:

Listing 10.2. The Full Text of day10/Student.java
package day10;

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Student extends EJBObject
{
   public String getStudentId() throws RemoteException;
   public String getFirstName() throws RemoteException;
   public void setFirstName(String firstName) throws RemoteException;
   public String getLastName() throws RemoteException;
   public void setLastName(String lastName) throws RemoteException;
   public String getAddress() throws RemoteException;
   public void setAddress(String address) throws RemoteException;
}

The remote interface contains business methods callable by the client. Note that the Student interface does not contain setStudentId() method because we don't allow clients to modify the student ID after a student object is created.

The remote interface is a Java RMI interface. So, method arguments and return types of a remote method must be legal types for RMI/IIOP and the method must include java.rmi.RemoteException in its throws clause.

Implementing the Enterprise Bean Class

Using bean-managed persistence, the entity bean provider writes database access calls (for example, using JDBC) directly in the entity bean component. The data access calls can be coded directly into the entity bean class, or they can be encapsulated in a data access component that is part of the entity bean.

Caution

Directly coding data access calls in the entity bean class might make it more difficult to adapt the entity bean to work with a database that has a different schema or a different type of database.


Listing 10.3 shows the StudentEJB entity bean class.

Listing 10.3. The Full Text of day10/StudentEJB.java
package day10;

import java.sql.*;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;

public class StudentEJB implements EntityBean {
   protected EntityContext ctx;
   /* persistent state */
   private String studentId;
   private String firstName;
   private String lastName;
   private String address;
   /* Accessor methods for entity bean fields */
   public  String getStudentId() {
      return studentId;
   }
   public  void setStudentId(String id) {
      studentId = id;
   }
   public  String getFirstName() {
      return firstName;
   }
   public  void setFirstName(String firstName) {
      this.firstName = firstName;
   }
   public  String getLastName() {
      return lastName;
   }
   public  void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public  String getAddress() {
      return address;
   }
   public  void setAddress(String address) {
      this.address = address;
   }
   /* Callback methods */
   public void setEntityContext(EntityContext ctx) {
      System.out.println("Student.setEntityContext called");
      this.ctx = ctx;
   }
   public void unsetEntityContext() {
      System.out.println("Student.unsetEntityContext called");
      this.ctx = null;
   }
   public void ejbActivate() {
      System.out.println("Student.ejbActivate() called.");
      studentId = (String) ctx.getPrimaryKey();
   }
   public void ejbPassivate() {
      System.out.println("Student.ejbPassivate () called.");
      studentId = null;
   }
   public void ejbLoad() {
     try {
      System.out.println("Student.ejbLoad() called.");
      StudentDAO dao = getDAO();
      StudentDetails student = dao.load(studentId);
      firstName = student.getFirstName();
      lastName = student.getLastName();
      address = student.getAddress();
     } catch(Exception ex) {
       throw new EJBException("ejbLoad: Unable to load Student "
           + studentId + " from database", ex);
     }
   }
   public void ejbStore() {
     try {
      System.out.println("Student.ejbStore() called.");
      StudentDAO dao = getDAO();
      dao.store(studentId,firstName, lastName,address);
     } catch(Exception ex) {
                       throw new EJBException("Student " + studentId +
                     " failed to save to database", ex);
     }
   }
   public String ejbCreate(String studentId, String firstName,
     String lastName, String address)
      throws CreateException {
     try {
      System.out.println("Student.ejbCreate() called.");
      StudentDAO dao = getDAO();
      dao.create(studentId, firstName, lastName, address);
      setStudentId(studentId);
      setFirstName(firstName);
      setLastName(lastName);
      setAddress(address);
      return studentId;
     } catch(Exception ex) {
        throw new CreateException("Failed to create student "
           + studentId + ex.getMessage());
     }
   }
   public void ejbPostCreate(String studentId, String firstName,
     String lastName, String address) throws CreateException {
      System.out.println("Student.ejbPostCreate() called.");
   }
   public void ejbRemove() throws RemoveException {
     try {
      System.out.println("Student.ejbRemove() called.");
      StudentDAO dao = getDAO();
      dao.remove(studentId);
     } catch(Exception ex) {
        throw new RemoveException("Failed to remove student "
           + studentId + ex.getMessage());
     }
   }
   /* Finder methods */
   public String ejbFindByPrimaryKey(String studentId)
      throws FinderException  {
      try {
         StudentDAO dao = getDAO();
         boolean result = dao.findByPrimaryKey(studentId);
         if(!result) throw new ObjectNotFoundException
           ("Student id "+ studentId+ " not found");
         return studentId;
     } catch(Exception ex) {
        throw new ObjectNotFoundException
           ("Student id "+ studentId+ " not found");
     }
   }
   public Collection ejbFindByLastName(String lastName)
      throws FinderException  {
      try {
         StudentDAO dao = getDAO();
         return dao.findByLastName(lastName);
     } catch(Exception ex) {
        throw new FinderException
           ("FinderException:"+ lastName+ ex.getMessage());
     }
   }
   /* Home methods */
   public int ejbHomeGetTotalNumberOfStudents() {
      try {
         StudentDAO dao = getDAO();
         return dao.findTotalNumberOfStudents();
      } catch(Exception ex) {
         throw new EJBException
            ("ejbHomeGetTotalNumberOfStudents:" + ex.getMessage());
      }
   }
   /* Helper methods */
   private StudentDAO getDAO()  {
      try{
         StudentDAO dao = null;
         Context context = new InitialContext();
         dao = (StudentDAO) Class.forName
          ((String)context.lookup("java:comp/env/param/StudentDAOClass"))
            .newInstance();
         return dao;
      } catch(Exception ex) {
         throw new EJBException ("getDAO:" + ex.getMessage());
      }
   }
}

The StudentEJB entity bean implements the javax.ejb.EntityBean interface and is a concrete class. The instance variables studentId, firstName, lastName, and address constitute the bean's persistent state. The bean class uses StudentDAO class to access the database. It implements the methods setEntityContext(), unsetEntityContext(), ejbActivate(), ejbPassivate(), ejbLoad(), ejbStore(), and ejbRemove() as defined in the javax.ejb.EntityBean interface. In the ejbLoad() method, you refresh the instance variables by reading from the database. The ejbStore() method updates the instance variables to the database. The ejbCreate() method inserts the entity state into the database, initializes the instance variables, and returns the primary key. The bean implements the ejbPostCreate method that corresponds to the ejbCreate method. The bean class implements the finder methods ejbFindByPrimaryKey() and ejbFindByLastName(). The single-object finder method ejbFindByPrimaryKey() verifies that the object exists in the database and returns the primary key. The multi-object finder method ejbFindByLastName() returns a collection of primary keys that match the search criteria.

In addition, the StudentEJB bean class implements the ejbHomeGetTotalAmountOfAllOrders() home business method. This home business method is independent of any particular student instance. It returns the total number of students in the system.

Note

The entity bean instance can discover its primary key by calling the getPrimaryKey() method on its entity context object. This method can be called when the entity object is associated with an identity in operations such as ejbPostCreate, ejbRemove, ejbActivate, ejbPassivate, ejbLoad, ejbStore, and business methods from the component interface. The following code snippet illustrates the getPrimaryKey method call:

EntityContext  ctx;
String studentId;
...
public void ejbActivate() {
  studentId = (String) ctx.getPrimaryKey();
  }

The client can retrieve the primary key for an entity object by invoking the getPrimaryKey() method on the EJBObject as follows:

Student student = ...  ;
String studentId = (String)student.getPrimaryKey();


Note

The data access calls are performed in ejbCreate<method>(...), ejbRemove(), ejbFind<method>(...), ejbLoad(), and ejbStore() methods, and/or in the business methods. Table 10.1 shows the summary of SQL statements for the StudentEJB bean class.


Table 10.1. Summary of the SQL Statements in Entity Bean Methods
Entity Bean MethodSQL Statement
ejbCreate()insert
ejbLoad()select
ejbStore()update
ejbFindByPrimaryKey()select
ejbFindByLastName()select
ejbHomeGetTotalNumberOfStudents()select
ejbRemove()delete

Listing 10.4 shows the StudentDAO interface. The StudentEJB class uses this interface to access the database.

Listing 10.4. The Full Text of day10/StudentDAO.java
package day10;
import java.util.Collection;

public interface StudentDAO
{
   public void create(String id, String firstName,
                      String lastName, String address)
      throws StudentDAOSysException;
   public void remove(String studentId)
      throws StudentDAOSysException;
   public boolean findByPrimaryKey(String studentId)
      throws StudentDAOSysException;
   public Collection findByLastName(String lastName)
      throws StudentDAOSysException;
   public int findTotalNumberOfStudents()
      throws StudentDAOSysException;
   public void store(String id, String firstName,
                     String lastName, String address)
      throws StudentDAOSysException;
   public StudentDetails load(String studentId)
      throws StudentDAOSysException;
}

Listing 10.5 shows the implementation of the StudentDAOPB class. This class implements the StudentDAO interface and is used to access a PointBase database. Methods in the StudentDAOPB class implement the actual logic of inserting, fetching, or updating data in the database. Note that, in each data access method, we acquire a connection, perform data access, and release the connection. When the client virtually releases the connection with close(), behind the scenes, the container returns the connection to the pool and makes it available for use by other clients. The class throws StudentDAOSysException to indicate a data access error.

Listing 10.5. The Full Text of day10/StudentDAOPB.java
package day10;

import java.util.*;
import javax.naming.*;
import java.sql.*;
import javax.sql.*;

public class StudentDAOPB implements StudentDAO {
   private Connection con;
   public StudentDAOPB()  {}
   public void create(String id, String firstName,
     String lastName, String address) throws StudentDAOSysException {
      PreparedStatement stmt = null;
      try {
         getDBConnection();
         stmt = con.prepareStatement
            ("insert into students(student_id, first_name, last_name, "+
             " address) values (?, ?, ?, ?)");
         stmt.setString(1, id);
         stmt.setString(2, firstName);
         stmt.setString(3, lastName);
         stmt.setString(4, address);
         stmt.executeUpdate();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
   }
   public void remove(String studentId)
      throws StudentDAOSysException {
      PreparedStatement stmt = null;
      try {
         getDBConnection();
         stmt = con.prepareStatement
            ("delete from students where student_id = ?");
         stmt.setString(1,studentId);
         stmt.executeUpdate();
         stmt.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
   }
   public boolean findByPrimaryKey(String studentId)
      throws StudentDAOSysException {
      boolean result = false;
      PreparedStatement stmt = null;
      try {
         getDBConnection();
         stmt  = con.prepareStatement
            ("select student_id from students where student_id = ?");
         stmt.setString(1, studentId);
         ResultSet rs = stmt.executeQuery();
         result = rs.next();
         rs.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException: "+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
      return result;
   }
   public Collection findByLastName(String lastName)
      throws StudentDAOSysException {
      Collection students = new ArrayList();
      PreparedStatement stmt = null;
      try {
         getDBConnection();
         stmt = con.prepareStatement
            ("select student_id from students where last_name = ?");
         stmt.setString(1, lastName);
         ResultSet rs = stmt.executeQuery();
         while(rs.next()){
            String studentId = rs.getString(1);
            students.add(studentId);
         }
         rs.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException: "+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
      return students;
   }
   public int findTotalNumberOfStudents()
      throws StudentDAOSysException {
      int total = 0;
      PreparedStatement stmt = null;
      try {
         getDBConnection();
         stmt = con.prepareStatement
            ("select count(student_id) from students");
         ResultSet rs = stmt.executeQuery();
         rs.next();
         total = rs.getInt(1);
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
      return total;
   }
   public StudentDetails load(String studentId)
      throws StudentDAOSysException {
      StudentDetails student = null;
      PreparedStatement stmt = null;
      try{
         getDBConnection();
         stmt = con.prepareStatement ("select first_name, last_name, " +
           " address from students where student_id=?");
         stmt.setString(1, studentId);
         ResultSet rs = stmt.executeQuery();
         rs.next();
         student = new StudentDetails(studentId,
                                      rs.getString(1),
                                      rs.getString(2),
                                      rs.getString(3));
         rs.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
      return student;
   }
   public void store(String id, String firstName, String lastName,
     String address)  throws StudentDAOSysException {
      PreparedStatement stmt = null;
      try{
         getDBConnection();
         stmt = con.prepareStatement ("update  students set "+
           "first_name=?,last_name = ?,address = ? where student_id=?");
         stmt.setString(1, firstName);
         stmt.setString(2,lastName);
         stmt.setString(3, address);
         stmt.setString(4, id);
         stmt.executeUpdate();
         stmt.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      finally {
         closeStatement(stmt);
         closeDBConnection();
      }
   }
   private void getDBConnection()
      throws StudentDAOSysException {
      try {
         Context context = new InitialContext();
         DataSource ds = (DataSource) context.lookup("jdbc/styejbDB");
         con = ds.getConnection();
         if(con==null)
            System.err.println("Database Connection is null");
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
      catch(NamingException ex) {
         throw new StudentDAOSysException("NamingException:"+ex.getMessage());
      }
   }
   private void closeDBConnection()
      throws StudentDAOSysException {
      try {
         con.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
   }
   private void closeStatement(PreparedStatement stmt)
      throws StudentDAOSysException {
      try {
         stmt.close();
      }
      catch(SQLException ex) {
         throw new StudentDAOSysException("SQLException:"+ ex.getMessage());
      }
   }
}

Listing 10.6 shows the StudentDetails value object.

Listing 10.6. The Full Text of day10/StudentDetails.java
package day10;

public class StudentDetails
{
   String id;
   String firstName;
   String lastName;
   String address;
   StudentDetails(String id, String firstName,
                  String lastName, String address)
      {
         this.id = id;
         this.firstName = firstName;
         this.lastName = lastName;
         this.address = address;
      }
   public String getFirstName() {return firstName; }
   public String getLastName() {return lastName;}
   public String getAddress() {return  address; }
}

Listing 10.7 shows the StudentDAOSysException class. The StudentDAO interface throws this exception to indicate a data access error.

Listing 10.7. The Full Text of day10/StudentDAOSysException.java
package day10;

public class StudentDAOSysException extends RuntimeException {
   public StudentDAOSysException (String str) {
      super(str);
   }
   public StudentDAOSysException () {
      super();
   }
}

Declaring the Deployment Descriptor

The deployment descriptor describes a component's deployment settings. Listing 10.8 shows the ejb-jar.xml deployment descriptor for the Student enterprise bean. ejb-jar.xml describes the enterprise bean's deployment properties, such as its bean type and structure. The file also provides the EJB container with information about where it can find and then load the home interface, remote interface, and bean class. In addition, the entity bean's deployment descriptor declares the persistent type and its primary key.

Listing 10.8. The Full Text of day10/ejb-jar.xml
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
  <enterprise-beans>
    <entity>
      <ejb-name>StudentEJB</ejb-name>
      <home>day10.StudentHome</home>
      <remote>day10.Student</remote>
      <ejb-class>day10.StudentEJB</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>java.lang.String</prim-key-class>
      <reentrant>False</reentrant>
      <env-entry>
        <env-entry-name>param/StudentDAOClass</env-entry-name>
        <env-entry-type>java.lang.String</env-entry-type>
        <env-entry-value>day10.StudentDAOPB</env-entry-value>
      </env-entry>
       <resource-env-ref>
         <resource-env-ref-name>jdbc/styejbDB</resource-env-ref-name>
      <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
       </resource-env-ref>
    </entity>
   </enterprise-beans>
   <assembly-descriptor>
      <container-transaction>
        <method>
        <ejb-name>StudentEJB</ejb-name>
        <method-name>*</method-name>
        </method>
        <trans-attribute>Required</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>

The ejb-jar.xml file declares StudentEJB as the name of the entity bean and specifies the home interface, remote interface, and bean class. The persistence-type element declares this as a bean-managed entity bean (as opposed to a container-managed persistence bean). The primary-key-class element specifies the type of our primary key. The reentrant element specifies whether an entity bean is reentrant. If an instance of a nonreentrant entity bean executes a client request in a given transaction context, and another request with the same transaction context arrives for the same entity object, the container will throw an exception to the second request. This rule allows the bean provider to program the entity bean as single-threaded, nonreentrant code. The Student enterprise bean uses environment entries to find the implementation class for student data access object.

Resource environment references are declared by the resource-env-ref elements. The resource-env-ref-name element specifies the JNDI name of the reference relative to java:comp/env. The resource-env-ref-type element specifies the fully qualified class name of the referenced object. For example, we specified the class name of jdbc/styejbDB data source as javax.sql.DataSource. In the vendor-specific deployment descriptor such as weblogic-ejb-jar.xml or jboss.xml we bind the resource environment references to the actual administered object location. The assembly-descriptor element contains application assembly information, and also includes specifying the transaction attributes. The method-name element is assigned the value * (asterisk) to indicate that all methods have the transaction attribute Required. The Required transaction attribute specifies the bean will always be part of a transaction. On Day 16, we'll learn more about transactions.

Listing 10.9 shows the weblogic-ejb-jar.xml deployment descriptor that is specific to WebLogic Server. The jndi-name element declares the JNDI name of the enterprise bean. So, the JNDI name of the StudentEJB is day10/Student.

Listing 10.9. The Full Text of day10/weblogic-ejb-jar.xml
<?xml version="1.0"?>

<!DOCTYPE weblogic-ejb-jar PUBLIC
'-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN'
'http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd'>

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>StudentEJB</ejb-name>
    <reference-descriptor>
        <resource-env-description>
          <res-env-ref-name>jdbc/styejbDB</res-env-ref-name>
          <jndi-name>jdbc.styejbDB</jndi-name>
     </resource-env-description>
      </reference-descriptor>
    <jndi-name>day10/Student</jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

Listing 10.10 shows the jboss.xml deployment descriptor that's specific to the JBoss server. Also, the jndi-name element declares the JNDI name of the StudentEJB as day10/Student.

Listing 10.10. The Full Text of day10/jboss.xml
<?xml version="1.0" encoding="UTF-8"?>

<jboss>
  <enterprise-beans>
    <entity>
      <ejb-name>StudentEJB</ejb-name>
      <jndi-name>day10/Student</jndi-name>
       <resource-env-ref>
         <resource-env-ref-name>jdbc/styejbDB</resource-env-ref-name>
         <jndi-name>java:/DefaultDS</jndi-name>
       </resource-env-ref>
    </entity>
  </enterprise-beans>
</jboss>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.62