Bean Managed Persistence (BMP) in Entity Beans
Gopalan Suresh Raj

Note
To work with any of these samples, you will need the following:
.........................................JDK 1.1.6 or higher (I use JDK 1.1.7B)
.........................................
Swing 1.1 or higher for JDK 1.1 (required if you wanna use the HomeBase GUI)
.........................................
The HomeBase (Scunthorpe version 0.5.1) - a free EJB Server from http://ejbhome.iona.com/

Developing a Bank Checking Account Server and Client
The Checking Account Server component has methods for managing a typical Bank Checking Account. Consequently, it has methods to create an account, and credit & debit money from the Account.

The Steps involved in developing and deploying an Entity Bean are

1. Set up your Data Source to the Database
2. Define your Home Interface.
3. Define your Remote Interface.
4. Develop your EntityBean
5. Define a Primary Key class
*** Note : From this point onwards, whatever we do becomes vendor (HomeBase) specific ***
6. Define your Deployment Descriptor
7. Compile your classes and generate the Container code using the tools provided by the vendor
8. Startup your Server

1. Set up your Data Source to the Database

The example program requires a database table named CHECKINGS --the Data Manipulation Language (DML) of which is shown below-- with the following fields:

CREATE TABLE CHECKINGS      
  ACCOUNT_NUMBER Number  
  CUSTOMER_NAME Text  
  BALANCE Currency  
      *primary key (ACCOUNT_NUMBER)

 

2. Define your Home Interface

Bank\CheckingHome.java
package Bank;

import javax.ejb.*;
import java.rmi.*;

public interface CheckingHome extends EJBHome {
 
 Checking create (AccountKey key, String name, double startingBalance)
  throws CreateException, RemoteException;
 
 Checking findByPrimaryKey (AccountKey key)
  throws FinderException, RemoteException;
}

3. Define your Remote Interface

Bank\Checking.java
package Bank;

import javax.ejb.*;
import java.rmi.*;

public interface Checking extends EJBObject {
 
  void credit (double amount) throws RemoteException;
  void debit (double amount) throws RemoteException;
  double getBalance () throws RemoteException;
  public String getCustomerName () throws RemoteException;
 
}

4. Develop your EntityBean

Bank\CheckingBean.java
package Bank;

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

public class CheckingBean implements EntityBean {
 
  protected EntityContext _entityContext;
 
  public    int           account_number;
//ACCOUNT_NUMBER
  public    String        customer_name;  
//CUSTOMER_NAME
  public    double        balance;        
//BALANCE
 
 static final int ALL_FIELDS   = 0;
 static final int NAME_FIELD   = 1;
 static final int BALANCE_FIELD= 2;
 
 ////////////////////////////////////////////////////////////////////
 //          Home Interface methods
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 public CheckingPK ejbCreate (AccountKey key, String name, double startingBalance)
  throws CreateException, RemoteException{
 ////////////////////////////////////////////////////////////////////
  if (startingBalance > 0) {
   this.m_accountNumber = ((CheckingPK)key).m_accountNumber;
   this.m_name = name;
   this.m_balance = startingBalance;
   truePut (CheckingBean.ALL_FIELDS);
  }
  else {
   key = null;
   throw new CreateException ("CheckingBean : ejbCreate exception");
  }
  return ((CheckingPK)key);
 }
 
 ////////////////////////////////////////////////////////////////////
 public void ejbPostCreate (AccountKey key, String name,
                        double startingBalance)
{
 ////////////////////////////////////////////////////////////////////
 }

 ////////////////////////////////////////////////////////////////////
 public CheckingPK ejbFindByPrimaryKey (AccountKey key) {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String query =
  "SELECT CUSTOMER_NAME, BALANCE FROM Checkings WHERE ACCOUNT_NUMBER = ?";
 
  try {
  
//STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   stmt.setInt (1, ((CheckingPK)key).m_accountNumber);
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
   if(rset.next ()) {
    this.m_accountNumber = ((CheckingPK)key).m_accountNumber;
    this.m_name = rset.getString (1);
    this.m_balance = rset.getDouble (2);
   }
   else {
    key = null;
    throw new FinderException ("CheckingBean : FindPrimaryKey");
   }
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
   // Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
  return ((CheckingPK)key);
 }

 ////////////////////////////////////////////////////////////////////
 //          Remote Interface methods
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 public void credit (double amount) {
 ////////////////////////////////////////////////////////////////////
  System.out.println("changing Checking::balance from " + this.m_balance);

  try {
   if( amount > 0 ) {
    this.m_balance += amount;
    truePut (CheckingBean.BALANCE_FIELD);
   }
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  System.out.println(" to " + this.m_balance);
 }
 
 ////////////////////////////////////////////////////////////////////
 public void debit (double amount) {
 ////////////////////////////////////////////////////////////////////
  System.out.println("changing CheckingBean::balance from " + this.m_balance);
 
  try {
   if ( (amount > 0) && (this.m_balance >= amount) ) {
    this.m_balance -= amount;
    truePut (CheckingBean.BALANCE_FIELD);
   }
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  System.out.println(" to " + this.m_balance);
 }
 
 ////////////////////////////////////////////////////////////////////
 public double getBalance () {
 ////////////////////////////////////////////////////////////////////
  System.out.println("CheckingBean::balance is " + this.m_balance);
  return this.m_balance;
 }
 
 ////////////////////////////////////////////////////////////////////
 public String getCustomerName () {
 ////////////////////////////////////////////////////////////////////
  System.out.println ("Checking::customer_name is " + this.m_name);
  return this.m_name;
 }

 ////////////////////////////////////////////////////////////////////
 //          Other Internal methods
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 public Connection getConnection () throws Exception {
 ////////////////////////////////////////////////////////////////////
  return DriverManager.getConnection ("jdbc:odbc:Bank");
 }
 
 ////////////////////////////////////////////////////////////////////
 String trueGet (int type, CheckingPK key) {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String     result = null;
  String     query = null;
  String     fieldName = null;
 
  switch (type) {
   case CheckingBean.NAME_FIELD:
    query = "SELECT CUSTOMER_NAME FROM Checkings WHERE ACCOUNT_NUMBER = ?";
    fieldName = "CUSTOMER_NAME";
    break;

   case CheckingBean.BALANCE_FIELD:
    query = "SELECT BALANCE FROM Checkings WHERE ACCOUNT_NUMBER = ?";
    fieldName = "BALANCE";
    break;
  }
 
  try {
  
//STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   stmt.setInt (1, key.m_accountNumber);
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
 
  
//STEP 3:Get the appropriate fields
   if (!rset.wasNull ()) {
    rset.next ();
    result = rset.getString (fieldName);
   }
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
  
// Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
  return result;
 }

 ////////////////////////////////////////////////////////////////////
 String truePut (int type) {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String     result = null;
  String     query = null;
 
  switch (type) {
   case CheckingBean.BALANCE_FIELD:
    query = "UPDATE Checkings SET BALANCE = ? WHERE ACCOUNT_NUMBER = ?";
    break;

   case CheckingBean.ALL_FIELDS:
    query = "INSERT INTO Checkings VALUES ( ?, ?, ? )";
    break;
  }
 
  try {
  
//STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   if (type == CheckingBean.BALANCE_FIELD) {
    stmt.setDouble (1, this.m_balance);
    stmt.setInt (2, this.m_accountNumber);
   }
   else {
    stmt.setInt (1, this.m_accountNumber);
    stmt.setString (2, this.m_name);
    stmt.setDouble (3, this.m_balance);
   }
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
   rset.next ();
 
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
   // Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
 public CheckingBean () {
 ////////////////////////////////////////////////////////////////////
 }

 ////////////////////////////////////////////////////////////////////
 //          Mandatory callback methods required by the EJB Spec
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 public void ejbActivate () {
 ////////////////////////////////////////////////////////////////////
 }

 ////////////////////////////////////////////////////////////////////
 public void ejbPassivate () {
 ////////////////////////////////////////////////////////////////////
 }

 ////////////////////////////////////////////////////////////////////
 public void ejbLoad () {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String query =
  "SELECT CUSTOMER_NAME, BALANCE FROM Checkings WHERE ACCOUNT_NUMBER = ?";
 
  try {
   CheckingPK key = (CheckingPK)_entityContext.getPrimaryKey ();

   //STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   stmt.setInt (1, key.m_accountNumber);
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
   if(rset.next ()) {
    this.m_accountNumber = key.m_accountNumber;
    this.m_name = rset.getString (1);
    this.m_balance = rset.getDouble (2);
   }
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
   // Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
 }

 ////////////////////////////////////////////////////////////////////
 public void ejbStore () {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String query =
  "UPDATE Checkings SET CUSTOMER_NAME = ?, BALANCE = ? WHERE ACCOUNT_NUMBER = ?";
 
  try {

   //STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   stmt.setString (1, this.m_name);
   stmt.setDouble (2, this.m_balance);
   stmt.setInt (3, this.m_accountNumber);
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
   rset.next ();
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
   // Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
 }
 
 ////////////////////////////////////////////////////////////////////
 public void ejbRemove () {
 ////////////////////////////////////////////////////////////////////
  Connection connection = null;
  PreparedStatement stmt= null;
  ResultSet  rset = null;
  String query = "DELETE FROM Checkings WHERE ACCOUNT_NUMBER = ?";
 
  try {
   CheckingPK key = (CheckingPK)_entityContext.getPrimaryKey ();

   //STEP1 : open the connection
   connection = this.getConnection ();
   stmt = connection.prepareStatement (query);
   stmt.setInt (1, this.m_accountNumber);
 
  
//STEP 2: Obtain the desired resultset with a query
   rset = stmt.executeQuery ();
   rset.next ();
 
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
  finally {
   // Cleanup that needs to occur whether we leave via a return or exception
   try {
    if (stmt != null) {
     stmt.close ();
     stmt = null;
    }
    if (connection != null) {
     connection.close ();
     connection = null;
    }
   }
   catch (Exception e) {
    e.printStackTrace ();
   }
  }
 }

 ////////////////////////////////////////////////////////////////////
 public void setEntityContext (EntityContext context)
  throws RemoteException {
 ////////////////////////////////////////////////////////////////////
  this._entityContext = context;
 }

 ////////////////////////////////////////////////////////////////////
 public void unsetEntityContext () throws RemoteException {
 ////////////////////////////////////////////////////////////////////
  this._entityContext = null;
 }
}

5. Define a Primary Key class

Bank\AccountKey.java
package Bank;

public interface AccountKey {
 void setKey (int key) throws Exception;
}

 

Bank\CheckingPK.java
package Bank;

import java.util.*;

public class CheckingPK implements AccountKey, java.io.Serializable {
 
 public static final int CHECKING_BASE = 11;
 public static final int CHECKING_HIGH = 10000;

 public int m_accountNumber;

 public void setKey (int key) throws Exception {
  System.out.println("Invoking CheckingsPK::setKey");
  if ( (key < CHECKING_BASE) ||
       (key > CHECKING_HIGH) ) {
   throw new Exception ("Invalid Checking Primary Key");
  }

  m_accountNumber = key;
 }
}

 

*** Note : From this point onwards, whatever we do becomes vendor (HomeBase) specific ***

 

6. Write the Deployment Descriptor for your Server

I had to manually edit the ejbml file to replace container-managed with bean-managed (not that it really mattered one way or the other. This is probably a bug in HomeBase).

conf\Bank.ejbml
<ejbml>
  <entity-bean
    name="checking"
    descriptor="Bank/CheckingDeployment"
    package="Bank"
    home="Bank.CheckingHome"
    remote="Bank.Checking"
    bean="Bank.CheckingBean"
    primary-key="Bank.CheckingPK"
    tx-attribute="TX_SUPPORTS"
  >
    <bean-managed
      storage-helper="com.ejbhome.generator.helpers.RelationalPersistenceCodeHelper"
      table="checking"
      data-source=""
      user=""
      password=""
    >
    </bean-managed>
  </entity-bean>
</ejbml>

 

7. Compile your classes and generate the Container code using the tools provided by the vendor

MS-DOS Command Prompt

H:\com\gopalan\AccountEJB>cd Bank

H:\com\gopalan\AccountEJB\Bank>javac *.java

H:\com\gopalan\AccountEJB\Bank>cd ..

H:\com\gopalan\AccountEJB>java com.ejbhome.Deployer .\conf\Bank.ejbml
EJBHome EJB Deployer version 0.5.1 (Scunthorpe)
(c) Copyright IONA Technologies PLC 1999. All Rights Reserved.
Windows NT x86 4.0
A nonfatal internal JIT (3.00.072b(x)) error 'regvarHI' has occurred in :
'com/ejbhome/Deployer.<init> (Ljava/util/Properties;Ljava/util/Vector;)V': Interpreting method.
Please report this error in detail to http://java.sun.com/cgi-bin/bugreport.cgi

Deploying: checking...
Generating: IonaCheckingHome...done.
Generating: IonaRemoteChecking...done.
Generating: IonaCheckingBean...done.
Generating: IonaCheckingContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaCheckingHome.java...done.
Compiling: IonaRemoteChecking.java...done.
Compiling: IonaCheckingBean.java...done.
Compiling: IonaCheckingContext.java...done.
Compiling: IonaCheckingHome_Stub.java...done.
Compiling: IonaCheckingHome_Skel.java...done.
Compiling: IonaRemoteChecking_Stub.java...done.
Compiling: IonaRemoteChecking_Skel.java...done.

H:\com\gopalan\AccountEJB>

8. Startup your Server

MS-DOS Command Prompt

H:\com\gopalan\AccountEJB>
H:\com\gopalan\AccountEJB>java com.ejbhome.Server
com.ejbhome.server.EnterpriseServer, initializing.
com.ejbhome.server.EnterpriseServer.fireContainerAdded() at (Compiled Code)
com.ejbhome.VendorConfiguration.<init>() at (VendorConfiguration.java:57)
com.ejbhome.VendorConfiguration.getSystemCodeHelper() at (VendorConfiguration.java:174)
com.ejbhome.VendorConfiguration.getTransactionCodeHelper() at (VendorConfiguration.java:138)
com.ejbhome.VendorConfiguration.getCommsCodeHelper() at (VendorConfiguration.java:163)
com.ejbhome.VendorConfiguration.getTracingCodeHelper() at (VendorConfiguration.java:152)
com.ejbhome.VendorConfiguration.getPersistenceCodeHelper() at (VendorConfiguration.java:125)
com.ejbhome.VendorConfiguration.getSessionPassivationCodeHelper() at (VendorConfiguration.java:185)
com.ejbhome.VendorConfiguration.getVendorPrefix() at (VendorConfiguration.java:100)
starting RMI registry on port 1099...
RMI registry on port 1099, has started.
com.ejbhome.server.EnterpriseServer.bindDataSources() at (Compiled Code)
com.ejbhome.naming.spi.rmi.RMICtx.<init>() at (RMICtx.java:20)
registering XA data sources
com.ejbhome.sql.XADataSourceImpl.<init>(jdbc:odbc:Bank,{password=mouse, user=user}) at (XADataSourceImpl.java:75)
com.ejbhome.naming.spi.rmi.RMICtx.bind() at (RMICtx.java:168)
com.ejbhome.server.EnterpriseServer.fireDataSourceAdded() at (Compiled Code)
registered: Bank -> jdbc:odbc:Bank, as an XA pooled data source [main]
com.ejbhome.naming.spi.rmi.RMICtx.<init>() at (RMICtx.java:20)
loading beans from: Bank.ejbml
com.ejbhome.server.EnterpriseServer$2.startElement(ejbml) at (EnterpriseServer.java:228)
com.ejbhome.server.EnterpriseServer$2.startElement(entity-bean) at (EnterpriseServer.java:228)
com.ejbhome.VendorConfiguration.getHomeContainerClassName() at (VendorConfiguration.java:226)
com.ejbhome.VendorConfiguration.getVendorPrefix() at (VendorConfiguration.java:100)
com.ejbhome.container.AbstractEJBHome.<init>() at (AbstractEJBHome.java:136)
com.ejbhome.naming.spi.rmi.RMICtx.bind() at (RMICtx.java:168)
registered Bank.IonaCheckingHome, as: checking.
com.ejbhome.server.EnterpriseServer$2.startElement(bean-managed) at (EnterpriseServer.java:228)
finished loading beans from: Bank.ejbml

The Server is now up and ready and is waiting for connections from the client.

Develop the Client Code

CheckingClient.java
import java.rmi.*;
import java.util.*;
import Bank.*;

public class CheckingClient {
 
 static final int INITIAL_RECORD = 11;
 
 int    accountNo;
 String name;
 double balance;
 int    count;
 Vector accountList = null;
 
 public CheckingHome home = null;
 public Checking server = null;

 ////////////////////////////////////////////////////////////////////
 public void fillAccountList() throws Exception {
 ////////////////////////////////////////////////////////////////////
  count = 0;
  accountList = new Vector ();
  int numRecords = 4;
  do {
   try {
   CheckingPK key = new CheckingPK ();
   key.setKey (count+INITIAL_RECORD);

    server = home.findByPrimaryKey (key);
    if (server == null)
     break;

    name = server.getCustomerName ();
    if(name == null)
     break;
    balance = server.getBalance ();
 
    System.out.println ("Account = " + (count+INITIAL_RECORD)+
                        " Customer = " + name +
                        " Balance = " + balance);
    accountList.addElement (new Integer (count+INITIAL_RECORD));
    key = null;
   }
   catch (Exception ex) {
    ex.printStackTrace ();
   }
   ++count;
  } while (count < numRecords);
 }

 public static void main(String[] args) {
 
  try {
  CheckingClient client = new CheckingClient ();
  client.home = (CheckingHome)Naming.lookup("checking");
  System.out.println( "Naming.lookup successful..." );
  if( client.home == null ) {
   System.out.println( "null TellerHome returned..." );
  }
  client.fillAccountList ();
  }
  catch (Exception e) {
   e.printStackTrace ();
  }
 
 }
}

Compile and Run the Client

MS-DOS Command Prompt

H:\com\gopalan\AccountEJB>javac CheckingClient.java

H:\com\gopalan\AccountEJB>java CheckingClient
Naming.lookup successful...
Invoking CheckingsPK::setKey
Account = 11 Customer = Athul Raj Balance = 100032.32
Invoking CheckingsPK::setKey
Account = 12 Customer = Adithya Ramesh Balance = 50000.15
Invoking CheckingsPK::setKey
Account = 13 Customer = Somebody Balance = 132.12
Invoking CheckingsPK::setKey
Account = 14 Customer = Somebody Balance = 45.32

H:\com\gopalan\AccountEJB>

Download the entire source code as a zip file.

click here to go to
My EJB HomePage...

click here to go to
My Advanced Java Tutorial Page...

About the Author...
Gopalan Suresh Raj is a Software Architect, Developer and an active Author. He is contributing author to a couple of books "Enterprise Java Computing-Applications and Architecture" and "The Awesome Power of JavaBeans". His expertise spans enterprise component architectures and distributed object computing. Visit him at his Web Cornucopia© site (http://www.execpc.com/~gopalan) or mail him at gopalan@execpc.com.

Go to the Component Engineering Cornucopia page

This site was developed and is maintained by Gopalan Suresh Raj

This page has been visited times since September 21,1998.

Last Updated : Mar 27, '99

If you have any questions, comments, or problems regarding this site, please write to me I would love to hear from you.


Copyright (c) 1997-99, Gopalan Suresh Raj - All rights reserved. Terms of use.

All products and companies mentioned at this site are trademarks of their respective owners.