It’s
finally time to write some code. If you
are new to EJB, you should pick up the aforementioned
Enterprise
JavaBeans
and skim
through Chapter 4 before continuing on. That should give you enough
of a basic foundation to understand this chapter, as
I’m going to move through the basic entity bean code
pretty quickly.
I’ll start the entity bean work with the office data
structure. Remember the OFFICES
table from last
chapter? The table structure is shown again in Figure 4-1 for reference.
I’ll use this structure for the first entity bean,
as it has a very simple structure. It also does not depend on any
other objects (i.e., it has no foreign keys), a subject I will
address in the next chapter. Dependent objects introduce some
additional considerations, but I will look at those when we start to
code beans for tables with foreign keys, like the
USERS
table. For the time being,
it’s enough to know that this office structure is as
simple as it gets. You need to store the ID (an
int
), the city (a String
), and
the state (another String
).
As this bean is simple in nature, it is a perfect candidate for container-managed persistence (CMP). There are no special JDBC calls that need to be made, no multiple-table queries, and no explicit data caching that needs to be performed. In the absence of these special cases, CMP is almost always a better choice than bean-managed persistence (BMP). While CMP beans are not truly portable yet, as each container generates specific implementation classes for that product, the basic classes I’ll look at in a CMP bean are portable. In other words, you won’t be able to take a complete JAR file (with implementation classes) from BEA Weblogic and deploy it on Lutris Enhydra, but you will be able to take the user-coded classes from this chapter and use them to generate classes for both BEA Weblogic and Lutris Enhydra. The resultant JARs can then be deployed on the respective application servers. This adds a bit of work, but is as close to complete portability as is possible in today’s EJB containers. It does allow us to write vendor-neutral code and not have to focus on a specific application server in this chapter.
All these reasons add up to a good case for using CMP instead of BMP. Additionally, the EJB 2.0 specification makes CMP an even more attractive solution, offering almost complete portability in all aspects of CMP entity beans.
Note
While the process of creating the “skeleton” classes for a CMP entity bean (the home and remote interfaces, the primary key, and the implementation class) is vendor-neutral, the means of generating container classes and deploying into a specific application server varies widely from product to product. In this book, I focus on BEA Weblogic, the most prevalent application server in use today, as it would be impossible to cover the ever-growing number of application servers. However, it should be fairly trivial to take the instructions for the Weblogic server and relate them to your own application server.
I’ve moved any steps specific to Weblogic to the appendixes, leaving only vendor-neutral code in the chapters’ content. This should make it easier to see what is general code, and what is platform-specific. Appendix D, then, covers installation and setup of the Weblogic application server for this book.
In preparing to code CMP entity beans, it
turns out that most of the EJB callbacks that must be implemented are
empty in almost all cases; the container will generate the code for
these callbacks at deployment time. Instead of coding these methods
into each entity bean implementation, wasting time and space in these
beans and generally adding a lot of clutter, you can create
an adapter class to handle this
task for you. This concept, initially presented in Richard
Monson-Haefel’s book, is
used here for the clarity it provides in later code. Example 4-1 is a slightly modified version of
Richard’s EntityAdapter
class,
which provides default, empty implementations of the required entity
bean callbacks. The entity bean implementation classes in this
chapter will extend this class, rather than implementing the
javax.ejb.EntityBean
interface directly.
Example 4-1. The EntityAdapter Helper Class
package com.forethought.ejb.util; import javax.ejb.EntityBean; import javax.ejb.EntityContext; public class EntityAdapter implements EntityBean { protected EntityContext entityContext; public void ejbActivate( ) { } public void ejbPassivate( ) { } public void ejbLoad( ) { } public void ejbStore( ) { } public void ejbRemove( ) { } public void setEntityContext(EntityContext entityContext) { this.entityContext = entityContext; } public void unsetEntityContext( ) { entityContext = null; } public EntityContext getEntityContext( ) { return entityContext; } }
If you have any implementation classes that need to provide special
behavior for a callback, you can override the adapter class;
overriding still allows you to leave out any callbacks that are not
implemented, keeping code clean. In addition, I’ve
introduced the com.forethought.ejb
package in this
class. All the entity beans created for the example can be put into
this base package, in a subpackage using the bean’s
name (for example, office
or
user
), with the EntityAdapter
class in the util
subpackage of the same
structure. So entity and session beans end up prefaced with
com.forethought.ejb.office
or
com.forethought.ejb.shoppingCart
. This is the same
naming scheme you’ll see in Sun’s
PetStore and most other enterprise applications.
In addition to package naming, I follow standard practices for the
actual bean class names. This entails using the actual name of the
data object as the name of an entity bean’s remote
interface. In the case of an office, the interface name simply
becomes Office
. The home interface has
“Home” appended to it, resulting in
OfficeHome
, and the bean implementation itself
gets the word “Bean” added to it,
resulting in OfficeBean
. And with this last detail
covered, you’re ready to move to the bean code for
the office classes.
Once the naming has been determined, it is simple to code the
remote interface, which is always a
good starting point in EJB coding. In this case, coding the remote
interface is a matter of simply providing a few accessors and
mutators[19] for the data fields in the office
structure. Additionally, you should notice that no methods are
provided to modify the ID of an office. That data is intrinsic to the
database and is used in indexing, but has no business meaning; as a
result, it should never be modified by an application. The only time
this information is ever fed to an entity bean is in the finder
methods, where an office is located by its primary key
(findByPrimaryKey( )
in the home interface), and in the
creation of an office, where it is required for row creation (the
create( )
method in the remote interface).
I’ll look at this in Chapter 5
and discuss how you can avoid even these situations of directly
dealing with a database-specific value.
Additionally, you will notice that the ID of the office is returned
as an Integer
, instead of the Java primitive
int
type. An Integer
is
returned for two important reasons. First, CMP 2.0 introduces
container-managed relationships (sometimes called
CMR,
or CMP relationships). This is a way of letting an EJB container
manage relationships between entity beans (like the Office bean here,
and the User bean in Appendix D). When these
relationships are used, the container is
responsible for generating additional classes to handle them, similar
to a container generating implementation classes for your CMP beans.
When these classes are generated, though, most containers make
several assumptions; the first is that the primary key value on an
entity bean is stored as a Java object
(java.lang.Integer
), and not as a primitive type
(int
). While this is not true in all EJB
containers, it is in most. For this reason alone, it is better to use
Integer
instead of int
when
dealing with primary key types.
Using an Integer
with primary keys also has a nice side effect.
Because Java programmers are almost always more accustomed to working
with the int
data type, using
Integer
makes the primary key value stand out. The
result is that developers think a little bit more about working with
the value, resulting in primary keys being handled with care, as they
should be. Therefore, you will note that the getId( )
method in the remote interface of the Office bean returns
an Integer
, not an int
, and the
create( )
method in the bean’s
home interface requires an Integer
as well.
Something else to note is the apparent naming discrepancy between the
database columns and the entity bean. You can see from Figure 4-1 that the primary key column in the database is
OFFICE_ID
, and the field name, as well as related
methods, in the Java class is simply ID
(or
id
as a method variable). This discrepancy may
seem a little odd, but turns out to be perfectly natural. In the
database layer, simply using ID
as the
column
name can result in some very unclear SQL statements. For example,
consider this SQL selecting all users and their offices:
SELECT FIRST_NAME, LAST_NAME, CITY, STATE FROM USERS u, OFFICES o WHERE u.OFFICE_ID = o.OFFICE_ID
There is no ambiguity; the join occurs between the
OFFICE_ID
columns of each
table. However, consider the following
SQL, which would produce the equivalent results when the
OFFICES
table’s primary key
column was named ID
:
SELECT FIRST_NAME, LAST_NAME, CITY, STATE FROM USERS u, OFFICES o WHERE u.OFFICE_ID = o.ID
This is certainly not as clear; add to this statement joins with five, ten, or even more additional tables (something quite common even in medium-size systems), and the joins between columns in different tables can become a nightmare. In the example’s naming system, columns in one table are always joined to columns in other tables with the same name; there is no room left for mistakes.
However, using this same naming in Java results in some odd code.
Consider that it is common to use the lowercased name of a class as
the name of an object class. For example, an instance of the class
Office
is often called
office
. If the ID method variable is named
officeId
, this practice can result in the rather
strange code fragment shown here:
// Get an instance of the Office class Integer keyValue = office.getOfficeId( );
It seems a bit redundant to call getOfficeID( )
on
an office
; while this might be a meaningful method
on an instance of the User
class, it
doesn’t make a lot of sense on the
Office
class. Here, this is only a minor
annoyance, but it could occur hundreds of times in hundreds of
classes in a complete application, becoming quite a nuisance. There
are enough annoyances in programming without adding to the list, so
you should stick to using database conventions in the database, and
Java conventions in the application. It takes a little extra
concentration during implementation, but is well worth it in the long
run.
So, with no further talk, Example 4-2 is the remote interface for the Office bean.
Example 4-2. The Remote Interface for the Office Bean
package com.forethought.ejb.office; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface Office extends EJBObject { public Integer getId( ) throws RemoteException; public String getCity( ) throws RemoteException; public void setCity(String city) throws RemoteException; public String getState( ) throws RemoteException; public void setState(String state) throws RemoteException; }
Warning
Lest you fall into an ugly trap, be sure not to use a capital
“D” in the getId( )
method (calling it getID( )
is
incorrect). This rule holds true when looking at the bean
implementation class, as well. While you may prefer this style (as I
do), it will cause problems in your container’s CMP
process. The container converts the first letter of the variable (the
“I” in
“Id”) to lowercase, takes the
resultant name (“id”), and matches
that to a member variable. If you use getID( )
,
you’ll then be forced to use a member variable
called “iD”, which is obviously not
what you want. So stick with the uppercase-lowercase convention, and
save yourself some trouble.
There’s also a growing trend to name remote
interfaces <Bean-Name>Remote
, so that the
remote interface for our office entity bean would be called
OfficeRemote
. This convention is a response to the
local interfaces introduced in EJB 2.0 (which I’ll
discuss in the next chapter). However, I’m not a big
fan of this, for a couple of reasons. First and foremost, I like to
make the most common case the simplest; since beans most commonly
have a remote interface, I make the naming of the remote interface
the simplest for a client to work with. Why type
“OfficeRemote” when 99 out of 100
cases, you can just type “Office”?
Then, if a local interface is needed, the name of that class can be
OfficeLocal
. The one time this name is used
instead of the remote interface, the name change is a clear
indication of the use of a local interface. So stick with the bean
name for your remote interfaces; programmers writing bean clients
will thank you for the simplicity later.
At this point, you need to stop a minute and think about how your bean is going to be used. It’s clear that any application clients that need to work with offices will require the remote interface you just coded. However, because offices are related to users (refer back to Figure 3-9 if you’re unsure of why this is so), you will also have some entity bean-to-entity bean communication. In this case, the overhead of RMI communication becomes unnecessary, and a local interface can improve performance drastically. It’s important to understand that there is nothing to prevent a bean from providing both local interfaces (for inter-bean communication) and remote interfaces (for client-to-bean communication).
It’s also trivial to code the local interface of a bean once you have the remote interface. Example 4-3 shows this interface, and it’s remarkably similar to the remote interface from the previous section. You’ll use this local interface later, in the User bean, which will have a persistence relationship with the Office bean.
Example 4-3. The Office Bean Local Interface
package com.forethought.ejb.office; import javax.ejb.EJBException; import javax.ejb.EJBLocalObject; public interface OfficeLocal extends EJBLocalObject { public Integer getId( ) throws EJBException; public String getCity( ) throws EJBException; public void setCity(String city) throws EJBException; public String getState( ) throws EJBException; public void setState(String state) throws EJBException; }
Primary keys in
beans where only one value is used are a piece of cake. In the case
of the Office bean, the primary key is the
OFFICE_ID
column, named simply
id
in the Java code you’ve seen
so far. All you need to do is identify the field used for the primary
key in the ejb-jar.xml
deployment descriptor
(I’ll detail this more fully in a moment). Your
entry will look something like this:
<ejb-name>OfficeBean</ejb-name> <home>com.forethought.ejb.office.OfficeHome</home> <remote>com.forethought.ejb.office.Office</remote> <local-home>com.forethought.ejb.office.OfficeLocalHome</local-home> <local>com.forethought.ejb.office.OfficeLocal</local> <ejb-class>com.forethought.ejb.office.OfficeBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <reentrant>False</reentrant> <abstract-schema-name>OFFICES</abstract-schema-name> <cmp-field><field-name>id</field-name></cmp-field> <cmp-field><field-name>city</field-name></cmp-field> <cmp-field><field-name>state</field-name></cmp-field> <primkey-field>id</primkey-field>
If you do come across a case where more than one value is used for a
primary key, you can code an actual Java class. However, this
situation is fairly rare, so I won’t cover it here.
The majority of cases require you to simply add to your deployment
descriptor for handling primary keys. You’ll also
notice (again) that the java.lang.Integer
type is used; as already
discussed, EJB containers generally must work in Java object types,
rather than in primitives.
The home
interface is also simple to code. For now, the ID of the office to
create is passed directly to the create( )
method. Later,
you’ll remove that dependency, and the ID will be
determined independently of the application client. You also can add
the basic finder,
findByPrimaryKey( )
, which takes in the
Integer primary key type. Example 4-4 shows this
code listing.
Example 4-4. The Home Interface for the Office Bean
package com.forethought.ejb.office; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; import javax.ejb.FinderException; public interface OfficeHome extends EJBHome { public Office create(Integer id, String city, String state) throws CreateException, RemoteException; public Office findByPrimaryKey(Integer officeID) throws FinderException, RemoteException; }
Like the remote interface, many folks have taken to calling the
remote home interface <Bean-Name>HomeRemote
(in this case OfficeHomeRemote
), again in
deference to local interfaces. And in the same vein, I recommend
against it for the same reasons as the remote interface.
It’s best to leave the remote home interface as-is,
and use OfficeLocalHome
as needed.
Just as you coded up a local interface for persistence relationships and bean-to-bean communication, you should create a corresponding local home interface. This is extremely similar to the remote home interface, and bears little discussion. Example 4-5 is the Office bean’s local home interface.
Example 4-5. The Local Home Interface for the Office Bean
package com.forethought.ejb.office; import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.EJBLocalHome; import javax.ejb.FinderException; public interface OfficeLocalHome extends EJBLocalHome { public OfficeLocal create(Integer id, String city, String state) throws CreateException, EJBException; public OfficeLocal findByPrimaryKey(Integer officeID) throws FinderException, EJBException; }
Last, but not
least, Example 4-6 is the bean implementation class.
Notice that it extends the
EntityAdapter
class instead of directly implementing
EntityBean
, like other examples you may find.
Because the bean’s persistence is container-managed,
the accessor and mutator methods are declared abstract. The container
will handle the method implementations that make these updates affect
the underlying data store.
Example 4-6. The Implementation for the Office Bean
package com.forethought.ejb.office; // EJB imports import javax.ejb.CreateException; import com.forethought.ejb.util.EntityAdapter; public abstract class OfficeBean extends EntityAdapter { public Integer ejbCreate(Integer id, String city, String state) throws CreateException { setId(id); setCity(city); setState(state); return null; } public void ejbPostCreate(int id, String city, String state) throws CreateException { // Empty implementation } public abstract Integer getId( ); public abstract void setId(Integer id); public abstract String getCity( ); public abstract void setCity(String city); public abstract String getState( ); public abstract void setState(String state); }
Take special note of the throws
CreateException
clause on the ejbCreate( )
and ejbPostCreate( )
methods. I have several books on EJB 2.0
on my desk right now that omit this clause; however, leaving it out
causes several application servers, including the J2EE reference
implementation, to fail on deployment. Therefore, be sure to have
your bean creation methods throw this exception. It also makes sense
in that the subclasses of the Office
class that
the container creates need to be able to report errors during bean
creation, and a CreateException
gives them that
ability. Since a subclass can’t add new exceptions
to the method declaration, the throws
clause must
exist in your bean class.
Also, be sure that your creation methods use the other methods in the
class for assignment. A common mistake is to code the
ejbCreate( )
method like this:
public Integer ejbCreate(Integer id, String city, String state) throws CreateException { this.id = id; this.city = city; this.state = state; return null; }
This was common in EJB 1.1, but doesn’t work so well
in EJB 2.0. You want to be sure that you invoke the
container-generated methods, which will handle database access.
Invoking the container-generated methods also means you
don’t have to explicitly define member variables for
the class, so that’s one less detail to worry about.
Also note that the creation method invokes setId( )
,
which I earlier said wouldn’t be made available to
clients. That remains true, because even though it’s
in the bean’s implementation class, the remote
interface does not expose the method, keeping it hidden from the
client.
One final note before moving on: you should notice in this
book’s source code (downloadable from http://www.newInstance.com) that the methods
in the bean implementation are not commented, as they are in the remote
interface. This is a fairly standard practice; methods are commented
(and therefore available in Javadoc) in interfaces, but these
comments are not duplicated in the implementation, which generally
makes implementation classes simpler to move through. If there are
details specific to the implementation that need to be documented,
they are suitable for commenting; however, such comments usually are
made in the code and are preceded with the
double-slash
(//
), rather than being Javadoc-style comments.
Such practices are followed in all the EJBs in this chapter and the
rest of the book.
[19] Throughout this book, the terms
accessor
and mutator
are
used; you may be more familiar with the terms
getter
and setter
. However,
as I’m a dog person, and my wife is a veterinary
technician, we both realize that a setter
is an
animal, not a Java term. So an accessor provides access to a variable
(the getXXX( )
style methods), and a mutator
modifies a variable (the setXXX( )
style
methods).
Get Building Java Enterprise Applications now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.