Entity EJBs with CMP

My previous article discussed creating stateless session beans. I had to write all the SQL code for interfacing to the database but there's another way. In some regards it's more complex yet in others it's simpler. These are entity beans which map directly to underlying database tables. There are two flavours: Container Managed Persistence (CMP) and Bean Managed Persistence (BMP).

The BMP variety is similar in some ways to my implementation of stateless session beans which communicate with the database. The SQL in a BMP is generated by the developer with the container simply providing the cues as to when to load and store the data, when the bean is going to be passivated or activated, etc. BMP beans are useful in those scenarios where you're going to be dealing with a number of different tables and need to implement business logic.

CMP eliminates the need to write SQL statements. All the database access code is managed by the server engine. The double-edged sword here is that while the developer needn't be concerned with the idiosyncracies of a particular RDBMS implementation, the EJBs have to be generated for a specific target. Now some might suggest that this approach leads to greater portability but people running a less popular database (e.g. Progress) might have difficulty finding server support.

While we've certainly come a long way, there are still differences in J2EE server implementations. For example, I picked up a book published by Sun Microsystems Press entitled "Enterprise Java Beans Component Architecture" (ISBN 0-13-035571-2). I attempted to follow their instructions on how to implement entity beans and found that they didn't apply to IBM's WebSphere product. Many of the classes they used weren't found and the required approach was fundamentally different from the one in the book. I ended up looking at some of the samples provided in the WebSphere directories and playing with the assembly tool before I figured it out.

So follow along with me as we develop a really simple entity EJB for the WebSphere platform. As with the session EJBs, we need the home, remote and bean classes. The Sun book talks about Local classes and interfaces but they aren't available on the WebSphere version (Advanced Edition, Single Server for Linux, version 4.0.1) I'm using. Then again, neither is EQL (EJB Query Language) so it's obvious that I'm not using the same enterprise tools.

I'm using a dead simple schema; an identifier called ejb_id, which is also the primary key, and a description, called ejb_desc. Since we don't permit alteration of the primary key after inserting a record in the database, the only mutator method we need to expose is the one which modifies the description. Here's the code for the remote interface:

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

public interface Ejb_test extends EJBObject {
	public String getEjb_desc() throws RemoteException;
	public void setEjb_desc( String ejb_desc ) throws RemoteException;
}
Really simple, right? Very much in-line with what we created for the stateless session beans. The home interface isn't any more complicated:
import	javax.ejb.EJBHome;
import	javax.ejb.CreateException;
import	javax.ejb.FinderException;
import	java.rmi.RemoteException;

public interface Ejb_testHome extends EJBHome {
	public Ejb_test create( Integer ejb_id, String ejb_desc )
	  throws RemoteException, CreateException;
	public Ejb_test findByPrimaryKey( Integer ejb_id )
	  throws RemoteException, FinderException;
}
The create and findByPrimaryKey are standard methods which are required for entity EJBs. We also have the option of creating additional finder methods but I won't get into that level of complexity here; I'm just trying to demonstrate the fundamentals. Here's the bean code:
import	javax.ejb.EntityBean;
import	javax.ejb.CreateException;
import	javax.ejb.EJBException;
import	javax.ejb.EntityContext;

public class Ejb_testBean implements EntityBean {
	EntityContext	context = null;
	public Integer	ejb_id;
	public String	ejb_desc;

	public Integer getEjd_id() {
		return( ejb_id );
	}

	public String getEjb_desc() {
		return( ejb_desc );
	}

	public void setEjb_desc( String ejb_desc ) {
		this.ejb_desc = ejb_desc;
	}

	public Integer ejbCreate( Integer ejb_id, String ejb_desc ) {
		this.ejb_id = ejb_id;
		this.ejb_desc = ejb_desc;
		return( ejb_id );
	}

	public void setEntityContext( EntityContext context ) throws EJBException {
		this.context = context;
	}

	public void unsetEntityContext() throws EJBException {
		context = null;
	}

	public void ejbActivate() throws EJBException {
	}

	public void ejbPassivate() throws EJBException {
	}

	public void ejbLoad() throws EJBException {
	}

	public void ejbStore() throws EJBException {
	}

	public void ejbRemove() throws EJBException {
	}
}
Here's where we encounter a major departure from what the book describes. Rather than determining the table field names and types from the accessor/mutator methods, WebSphere requires that the CMP managed fields be defined as public variables in the class. The EntityContext variable is protected and so ignored by the assembly tool which leaves us with just the ejb_id and ejb_desc instance variables. Also note that the accessor and mutator methods merely get and set these instance variables.

Another major difference is that these are concrete classes and methods. The book states that everything has to be declared abstract and the server will fill in the blanks. Not in the IBM world it doesn't. There are some other differences, such as ensuring that the home and remote methods are declared as throwing RemoteException and the ejb... methods in the bean class (except for ejbCreate) throw EJBException. If you don't have these declared properly then the assembly tool will throw cryptic errors.

Compile these files, jar them up and then fire up the assembly tool. Create a new EJB jar with your beans and specify that they are entity beans with CMP. Make sure that you specify the CMP fields and the primary key field and class in the General tab. I'm using java.lang.Integer for the ejb_id in this example. Be sure to visit the Bindings table and define a JNDI name along with the JNDI name of your datasource. Save the file and select the "Generate code for deployment" menu item (under "File".)

If you've done everything right then you can pull up the admin tool from the browser and install the new EJBs. Again, assuming you've previously defined the datasource then you should be just about ready to test. But wait! I named all my methods and classes so that I could use an existing table named EJB_TEST in my Oracle instance. The obvious mistake was using an underscore in the table name. It's non-portable and has caused interoperability problems in the past, kind of like using dashes in field names (i.e. DON'T.)

What you can do is create a temporary directory and unjar the deployment archive into it. In the META-INF subdirectory you will find a file called Table.ddl. Examination will show that it contains the SQL to create the table used by your beans. If you go to Google you can find some unsupported mechanisms for changing the mapping from the generated table name to existing ones. Rather than go to that trouble, I just ran the Oracle sqlplus tool and used the script to create a new table.

Now you can use the usual client programming techniques to access your entity beans. I bound mine to a JNDI name of EJBTEST so here's some code which creates a new row in the underlying database, retrieves a reference by primary key, modifies the description and then deletes the record:

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

public class EJBtest3 {

	public static void main( String args[] ) {
		Properties	props = new Properties();
		Context		context = null;
		Ejb_testHome	home = null;
		Ejb_test	bean = null;

		try {

			/*
			 * obtain the home reference
			 */

			props.put( Context.PROVIDER_URL, "iiop://localhost" );
			props.put( Context.INITIAL_CONTEXT_FACTORY,
			  "com.ibm.websphere.naming.WsnInitialContextFactory" );
			context = new InitialContext( props );
			Object	obj = context.lookup( "EJBTEST" );
			home = (Ejb_testHome)
			  javax.rmi.PortableRemoteObject.narrow( obj,
			  Ejb_testHome.class );

			/*
			 * create a new table entry
			 */

			bean = home.create( new Integer( 1 ), "original" );
System.out.println( "before change, description = '" +
  bean.getEjb_desc() + "'" );

			/*
			 * locate by primary key and modify
			 */

			bean = home.findByPrimaryKey( new Integer( 1 ) );
			bean.setEjb_desc( "modified value" );
System.out.println( "after change, description = '" +
  bean.getEjb_desc() + "'" );

			/*
			 * delete the entry
			 */

			bean.remove();
		}
		catch( Exception e ) {
			e.printStackTrace();
			System.exit( 12 );
		}
	}
}
No suprises here and everything behaves as expected. Apart from learning the semantics of obtaining references from the naming system and the content of the various interface files, EJB programming is no more complicated than any other Java application. The server tools can be a bit convoluted but they have to be all things to all people. While I've presented simple examples, enterprise applications will be understandably more complex.

My best advice would be to play around with a J2EE server at home. You can download trial versions of both WebLogic and WebSphere from the respective vendors for the Linux platform. You can also obtain either free or trial versions of powerful RDBMSs such as Oracle and DB/2. Develop some applications of your own in order to gain familiarity with the platform. While many in the industry are sitting on the fence right now, once companies start to deploy this technology there will be a huge demand for experienced individuals, far outstripping the supply. Now is the time to gain the experience you'll need!

Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 by Phil Selby