This chapter details the operation of the JBoss CMP2 engine. It does not provide an introduction to the EJB 2.0 container managed persistence (CMP2.0) model. To get started with CMP2.0, see the J2EE tutorial ( http://java.sun.com/j2ee/tutorial/index.html ), or Enterprise Java Beans - 3rd edition along with the companion JBoss workbook ( http://www.oreilly.com/catalog/entjbeans3/workbooks/index.html ).
JBossCMP is the default persistence manager for EJB 2.0 applications. Because JBossCMP is a core feature of JBoss 3.x, no action beyond the basic JBoss installation (see the JBoss 3.2 Quick Start Guide) is required to use CMP 2.0, but there are some details to note when creating a new EJB 2.0 application or when upgrading an EJB 1.1 application.
When JBoss deploys an EJB jar file, it uses the DOCTYPE of the ejb-jar.xml deployment descriptor to determine the version of the EJB jar. The correct DOCTYPE for EJB 2.0 is given in See The EJB 2.0 DOCTYPE Declaration.:
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
If the public identifier of the DOCTYPE is "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" JBossCMP will use the "Standard CMP 2.x EntityBean" configuration in the standardjboss.xml file. If you have an application that uses a custom entity bean configuration, and you are upgrading to EJB 2.0, you must change the persistence-manager and add the new interceptors (see the "Standard CMP 2.x EntityBean" configuration in the standardjboss.xml file for details). No further configuration is necessary to deploy and run your EJB 2.0 application successfully.
The full source code for all of the examples presented in this documentation is available in the examples/src/main/org/jboss/cmp2 directory. The code represents a Crime Portal, which models criminal organizations. A diagram of the portions of the Criminal Portal data model used in the example code is shown in See The main CMP2 example classes..
To build the example code, execute ant with the following arguments:
[nr@toki examples]$ ant -Dchap=cmp2 config
[echo] Using jboss.dist=/tmp/jboss-3.2.3
[mkdir] Created dir: /Users/orb/Desktop/jboss/docs323/examples/output/classes
[javac] Compiling 123 source files to /Users/orb/Desktop/jboss/docs323/examples/output/classes
[mkdir] Created dir: /Users/orb/Desktop/jboss/docs323/examples/output/cmp2
[jar] Building jar: /Users/orb/Desktop/jboss/docs323/examples/output/cmp2/cmp2-ex1.jar
[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy
[echo] Waiting for 5 seconds for deploy...
This command builds and deploys the application to the JBoss server. When you start your JBoss server, or if it is already running, you should see the following deployment messages:
13:50:18,316 INFO [MainDeployer] Starting deployment of package: file:/private/tmp/jboss-3.2.3/server/default/deploy/cmp2-ex1.jar
13:50:20,811 INFO [EjbModule] Deploying OrganizationEJB
13:50:20,851 INFO [EjbModule] Deploying GangsterEJB
13:50:20,882 INFO [EjbModule] Deploying JobEJB
13:50:20,918 INFO [EjbModule] Deploying LocationEJB
13:50:20,942 INFO [EjbModule] Deploying EJBTestRunnerEJB
13:50:20,965 INFO [EjbModule] Deploying ReadAheadEJB
13:50:21,590 INFO [EntityInstancePool] Started jboss.j2ee:jndiName=crimeportal/Organization,plugin=pool,service=EJB
13:50:21,594 INFO [EntityContainer] Started jboss.j2ee:jndiName=crimeportal/Organization,service=EJB
13:50:21,647 INFO [EntityInstancePool] Started jboss.j2ee:jndiName=crimeportal/Gangster,plugin=pool,service=EJB
13:50:21,653 INFO [EntityContainer] Started jboss.j2ee:jndiName=crimeportal/Gangster,service=EJB
13:50:21,721 INFO [EntityInstancePool] Started jboss.j2ee:jndiName=crimeportal/Job,plugin=pool,service=EJB
13:50:21,725 INFO [EntityContainer] Started jboss.j2ee:jndiName=crimeportal/Job,service=EJB
13:50:21,843 INFO [OrganizationEJB] Created table 'ORGANIZATION' successfully.
13:50:21,972 INFO [GangsterEJB] Created table 'GANGSTER' successfully.
13:50:21,992 INFO [GangsterEJB] Created table 'GANGSTER_ENEMIES' successfully.
13:50:22,169 INFO [JobEJB] Created table 'JOB' successfully.
13:50:22,196 INFO [JobEJB] Created table 'GANGSTER_JOB' successfully.
13:50:22,271 INFO [LocationEJB] Created table 'LOCATION' successfully.
13:50:22,275 INFO [EntityInstancePool] Started jboss.j2ee:jndiName=crimeportal/Location,plugin=pool,service=EJB
13:50:22,279 INFO [EntityContainer] Started jboss.j2ee:jndiName=crimeportal/Location,service=EJB
13:50:22,417 INFO [StatelessSessionInstancePool] Started jboss.j2ee:jndiName=ejb/EJBTestRunner,plugin=pool,service=EJB
13:50:22,431 INFO [StatelessSessionContainer] Started jboss.j2ee:jndiName=ejb/EJBTestRunner,service=EJB
13:50:22,460 INFO [StatelessSessionInstancePool] Started jboss.j2ee:jndiName=crimeportal/ReadAhead,plugin=pool,service=EJB
13:50:22,463 INFO [StatelessSessionContainer] Started jboss.j2ee:jndiName=crimeportal/ReadAhead,service=EJB
13:50:22,466 INFO [EjbModule] Started jboss.j2ee:module=cmp2-ex1.jar,service=EjbModule
13:50:22,469 INFO [EJBDeployer] Deployed: file:/private/tmp/jboss-3.2.3/server/default/deploy/cmp2-ex1.jar
13:50:22,591 INFO [MainDeployer] Deployed package: file:/private/tmp/jboss-3.2.3/server/default/deploy/cmp2-ex1.jar
13:50:38,358 INFO [OrganizationBean$Proxy] Creating organization Yakuza, Japanese Gangsters
13:50:38,506 INFO [OrganizationBean$Proxy] Creating organization Mafia, Italian Bad Guys
13:50:38,514 INFO [OrganizationBean$Proxy] Creating organization Triads, Kung Fu Movie Extras
13:50:38,572 INFO [GangsterBean$Proxy] Creating Gangster 0 'Bodyguard' Yojimbo
13:50:38,651 INFO [GangsterBean$Proxy] Creating Gangster 1 'Master' Takeshi
13:50:38,665 INFO [GangsterBean$Proxy] Creating Gangster 2 'Four finger' Yuriko
13:50:38,697 INFO [GangsterBean$Proxy] Creating Gangster 3 'Killer' Chow
13:50:38,711 INFO [GangsterBean$Proxy] Creating Gangster 4 'Lightning' Shogi
13:50:38,728 INFO [GangsterBean$Proxy] Creating Gangster 5 'Pizza-Face' Valentino
13:50:38,744 INFO [GangsterBean$Proxy] Creating Gangster 6 'Toohless' Toni
13:50:38,785 INFO [GangsterBean$Proxy] Creating Gangster 7 'Godfather' Corleone
13:50:38,809 INFO [JobBean$Proxy] Creating Job 10th Street Jeweler Heist
13:50:38,821 INFO [JobBean$Proxy] Creating Job The Greate Train Robbery
13:50:38,831 INFO [JobBean$Proxy] Creating Job Cheap Liquor Snatch and Grab
Before the chapter tests can be run, the log level of JBossCMP must be increased. To enable DEBUG logging for the CMP subsystem, add the following category to your log4j.xml file:
<category name="org.jboss.ejb.plugins.cmp">
In addition to this, it is necessary to decrease the threshold on the CONSOLE appender to allow DEBUG level messages to be logged to the console. The following changes also need to be applied to the log4j.xml file.
<!-- ============================== -->
<!-- Append messages to the console -->
<!-- ============================== -->
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Threshold" value="DEBUG"/>
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
To see the full workings of the CMP engine you would need to enable the custom TRACE level priority on the org.jboss.ejb.plugins.cmp category as show here:
<category name="org.jboss.ejb.plugins.cmp">
<priority value="TRACE" class="org.jboss.logging.XLevel"/>
One final note before moving on to look at how to run the chapter examples. Since the beans in the examples are configured to remove their tables on undeployment, anytime you restart the JBoss server you need to rerun the config target to reload the example data. Also, if you make changes to the examples and want to redeploy the example EJB jar, this also should be done using the config target so that the example data is reloaded.
The first test target illustrates a number of the customization features that will be discussed throughout this chapter. To run these tests execute the following ant target:
[nr@toki examples]$ ant -Dchap=cmp2 -Dex=test run-example
14:03:27,920 DEBUG [OrganizationEJB#findByPrimaryKey] Executing SQL: SELECT name FROM ORGANIZATION WHERE name=?
14:03:28,011 DEBUG [OrganizationEJB] Executing SQL: SELECT desc, the_boss FROM ORGANIZATION WHERE (name=?)
14:03:28,020 DEBUG [OrganizationEJB] Executing SQL: SELECT id FROM GANGSTER WHERE (organization=?)
14:03:28,044 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,052 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,070 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,229 DEBUG [GangsterEJB#findBadDudes_ejbql] Executing SQL: SELECT t0_g.id FROM GANGSTER t0_g WHERE (t0_g.badness > ?)
14:03:28,256 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,264 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,270 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,276 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,281 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,395 DEBUG [GangsterEJB#findBadDudes_jbossql] Executing SQL: SELECT t0_g.id, t0_g.badness FROM GANGSTER t0_g WHERE (t0_g.badness > ?) ORDER BY t0_g.badness DESC
14:03:28,417 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,423 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,429 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,439 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,446 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,605 DEBUG [GangsterEJB#findBadDudes_declaredsql] Executing SQL: SELECT id FROM GANGSTER WHERE badness > ? ORDER BY badness DESC
14:03:28,613 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,631 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,641 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,647 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,655 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,783 DEBUG [GangsterEJB#ejbSelectBoss_ejbql] Executing SQL: SELECT DISTINCT t0_underling_organization_theBos.id FROM GANGSTER t1_underling, ORGANIZATION t4_underling_organization, GANGSTER t0_underling_organization_theBos WHERE ((t1_underling.name = ?) OR (t1_underling.nick_name = ?)) AND t1_underling.organization=t4_underling_organization.name AND t4_underling_organization.the_boss=t0_underling_organization_theBos.id
14:03:28,815 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,822 DEBUG [GangsterEJB#ejbSelectBoss_ejbql] Executing SQL: SELECT DISTINCT t0_underling_organization_theBos.id FROM GANGSTER t1_underling, ORGANIZATION t4_underling_organization, GANGSTER t0_underling_organization_theBos WHERE ((t1_underling.name = ?) OR (t1_underling.nick_name = ?)) AND t1_underling.organization=t4_underling_organization.name AND t4_underling_organization.the_boss=t0_underling_organization_theBos.id
14:03:28,829 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:28,835 DEBUG [GangsterEJB#ejbSelectBoss_ejbql] Executing SQL: SELECT DISTINCT t0_underling_organization_theBos.id FROM GANGSTER t1_underling, ORGANIZATION t4_underling_organization, GANGSTER t0_underling_organization_theBos WHERE ((t1_underling.name = ?) OR (t1_underling.nick_name = ?)) AND t1_underling.organization=t4_underling_organization.name AND t4_underling_organization.the_boss=t0_underling_organization_theBos.id
14:03:29,011 DEBUG [GangsterEJB#ejbSelectBoss_declaredsql] Executing SQL: SELECT DISTINCT boss.id FROM GANGSTER boss , gangster g, organization o WHERE (LCASE(g.name) = ? OR LCASE(g.nick_name) = ?) AND
g.organization = o.name AND o.the_boss = boss.id
14:03:29,163 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,172 DEBUG [GangsterEJB#ejbSelectBoss_declaredsql] Executing SQL: SELECT DISTINCT boss.id FROM GANGSTER boss , gangster g, organization o WHERE (LCASE(g.name) = ? OR LCASE(g.nick_name) = ?) AND
g.organization = o.name AND o.the_boss = boss.id
14:03:29,201 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,208 DEBUG [GangsterEJB#ejbSelectBoss_declaredsql] Executing SQL: SELECT DISTINCT boss.id FROM GANGSTER boss , gangster g, organization o WHERE (LCASE(g.name) = ? OR LCASE(g.nick_name) = ?) AND
g.organization = o.name AND o.the_boss = boss.id
14:03:29,378 DEBUG [GangsterEJB#ejbSelectGeneric] DYNAMIC-QL: SELECT OBJECT(g) FROM gangster g WHERE g.hangout.state IN (?1, ?2, ?3, ?4) ORDER BY g.name
14:03:29,390 DEBUG [GangsterEJB#ejbSelectGeneric] SQL: SELECT DISTINCT t0_g.id, t0_g.name FROM GANGSTER t0_g, LOCATION t1_g_hangout WHERE (t1_g_hangout.st IN (?, ?, ?, ?) AND t0_g.hangout=t1_g_hangout.id) ORDER BY t0_g.name ASC
14:03:29,394 DEBUG [GangsterEJB#ejbSelectGeneric] Executing SQL: SELECT DISTINCT t0_g.id, t0_g.name FROM GANGSTER t0_g, LOCATION t1_g_hangout WHERE (t1_g_hangout.st IN (?, ?, ?, ?) AND t0_g.hangout=t1_g_hangout.id) ORDER BY t0_g.name ASC
14:03:29,422 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,428 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,433 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,440 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,449 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,465 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,671 DEBUG [OrganizationEJB#ejbSelectOperatingZipCodes_declaredsql] Executing SQL: SELECT DISTINCT hangout.zip FROM LOCATION hangout , organization o, gangster g WHERE LCASE(o.name) = ? AND o.name = g.organization AND
g.hangout = hangout.id ORDER BY hangout.zip
14:03:29,822 DEBUG [GangsterEJB#ejbSelectGeneric] DYNAMIC-QL: SELECT OBJECT(g) FROM gangster g WHERE g.hangout.state IN (?1, ?2, ?3, ?4) ORDER BY g.name
14:03:29,828 DEBUG [GangsterEJB#ejbSelectGeneric] SQL: SELECT DISTINCT t0_g.id, t0_g.name FROM GANGSTER t0_g, LOCATION t1_g_hangout WHERE (t1_g_hangout.st IN (?, ?, ?, ?) AND t0_g.hangout=t1_g_hangout.id) ORDER BY t0_g.name ASC
14:03:29,832 DEBUG [GangsterEJB#ejbSelectGeneric] Executing SQL: SELECT DISTINCT t0_g.id, t0_g.name FROM GANGSTER t0_g, LOCATION t1_g_hangout WHERE (t1_g_hangout.st IN (?, ?, ?, ?) AND t0_g.hangout=t1_g_hangout.id) ORDER BY t0_g.name ASC
14:03:29,865 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,962 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER WHERE id=?
14:03:29,970 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
14:03:29,980 DEBUG [GangsterEJB] Executing SQL: SELECT cell_area, cell_exch, cell_ext, page_area, page_exch, page_ext, email FROM GANGSTER WHERE (id=?)
14:03:29,987 DEBUG [GangsterEJB] Executing SQL: UPDATE GANGSTER SET cell_area=?, cell_exch=?, cell_ext=?, page_area=?, page_exch=?, page_ext=?, email=? WHERE id=?
14:03:29,995 DEBUG [GangsterEJB] Rows affected = 1
These tests exercise various finders, selectors and object to table mapping issues. We will refer to the tests throughout the chapter.
The other main target runs a set of tests to demonstrate the optimized loading configurations presented in Optimized Loading. Now that the logging is setup correctly, the read-ahead tests will display useful information about the queries performed. Note that you do not have to restart the JBoss server for it to recognize the changes to the log4j.xml file, but it may take a minute or so. The following shows the actual execution of the readahead client:
[starksm@banshee examples]$ ant -Dchap=cmp2 -Dex=readahead run-example
When the readahead client is executed, all of the SQL queries executed during the test are displayed in the JBoss server console. The important items of note when analyzing the output are the number of queries executed, the columns selected, and the number of rows loaded. The following shows the read-ahead none portion of the JBoss server console output from readahead:
########################################################
08:31:15,892 DEBUG [findAll_none] Executing SQL: SELECT t0_g.id, t0_g.id FROM GANGSTER t0_g ORDER BY t0_g.id ASC
08:31:15,902 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,912 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,912 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,912 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,922 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,922 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,932 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,932 DEBUG [GangsterEJB] Executing SQL: SELECT name, nick_name, badness, hangout, organization FROM GANGSTER WHERE (id=?)
08:31:15,942 INFO [ReadAheadTest]
########################################################
We will revisit this example and explore the output when we discuss the settings for optimized loading.
The jbosscmp-jdbc.xml descriptor is used to control the behavior of the JBossCMP engine. This can be done globally through the conf/standardjbosscmp-jdbc.xml descriptor found in the server configuration file set, or per EJB jar deployment via a META-INF/jbosscmp-jdbc.xml descriptor. We will touch on the elements of the as we go through the following sections which describe the capabilities of the JBossCMP engine. The top level elements are shown in See The jbosscmp-jdbc top level content model.. and described below.
The DTD for the jbosscmp-jdbc.xml descriptor can be found in JBOSS_DIST/docs/dtd/jbosscmp-jdbc_3_2.dtd. An annotated html/svg version of the DTD is located in docs/dtd/html-svg/jbosscmp-jdbc_3_2_dtd/index.html. The public doctype for this DTD is:
<!DOCTYPE jbosscmp-jdbc PUBLIC
Although several new features have been added, and there have been major changes to cmp-fields and finders, the basic entity bean structure has not changed much in CMP 2.0. A new feature of EJB 2.0 is the addition of local interfaces. A local interface is composed of two interfaces, the local interface and the local home interface.1 These interfaces are conceptually the same thing as the remote interface and home interface (sometimes referred to as the remote home), except that local interfaces are only accessible within the same Java VM. This allows local interfaces to use pass-by-reference semantics, removing the overhead associated with serializing and deserializing every method parameter.2 Local interfaces are not unique to CMP and are not discussed in this documentation. The simplified code for the Gangster entity follows:
// Gangster Local Home Interface
public interface GangsterHome extends EJBLocalHome
Gangster create(Integer id, String name, String nickName)
Gangster findByPrimaryKey(Integer id) throws FinderException;
public interface Gangster extends EJBLocalObject
// Gangster Implementation Class
public abstract class GangsterBean implements EntityBean
private Category log = Category.getInstance(getClass());
public Integer ejbCreate(Integer id, String name, String nickName)
log.info("Creating Gangster " + id + " '" + nickName + "' "+ name);
public void ejbPostCreate(Integer id, String name,
// CMP field accessors ----------------------------------------------
public abstract Integer getGangsterId();
public abstract void setGangsterId(Integer gangsterId);
public abstract String getName();
public abstract void setName(String name);
public abstract String getNickName();
public abstract void setNickName(String nickName);
public abstract int getBadness();
public abstract void setBadness(int badness);
public abstract ContactInfo getContactInfo();
public abstract void setContactInfo(ContactInfo contactInfo);
// EJB callbacks ----------------------------------------------------
public void setEntityContext(EntityContext context)
public void unsetEntityContext() { ctx = null; }
public void ejbPassivate() { }
public void ejbRemove() { log.info("Removing " + getName()); }
The base declaration of an entity in the ejb-jar.xml file has not changed much in CMP 2.0. The declaration of the GangsterEJB interfaces and cmp fields is shown in See The ejb-jar.xml Entity Declaration..
<?xml version="1.0" encoding="UTF-8"?>
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<display-name>CMP 2.0 Lab Jar</display-name>
<display-name>Gangster Entity Bean</display-name>
<ejb-name>GangsterEJB</ejb-name>
<local-home>org.jboss.cmp2.crimeportal.GangsterHome</local-home>
<local>org.jboss.cmp2.crimeportal.Gangster</local>
<ejb-class>org.jboss.cmp2.crimeportal.GangsterBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>gangster</abstract-schema-name>
<cmp-field><field-name>gangsterId</field-name></cmp-field>
<cmp-field><field-name>name</field-name></cmp-field>
<cmp-field><field-name>nickName</field-name></cmp-field>
<cmp-field><field-name>badness</field-name></cmp-field>
<cmp-field><field-name>contactInfo</field-name></cmp-field>
<primkey-field>gangsterId</primkey-field>
The new local home and local elements are equivalent to the home and remote elements. The cmp-version element is new and can be either 1.x or the default 2.x. This element was added so 1.x and 2.x entities could be mixed in the same application. The abstract-schema-name element is also new and is used to identify this entity type in EJB-QL queries, which are discussed in Queries.
The JBossCMP configuration for the entity is declared with an entity element in the jbosscmp-jdbc.xml file. This file is located in the META-INF directory of the ejb-jar file and contains all of the optional configuration information for JBossCMP. The entity elements are grouped together in the enterprise-beans element under the top level jbosscmp-jdbc element. An example entity configuration is shown in See A sample jbosscmp-jdbc.xml Entity Mapping..
<?xml version="1.0" encoding="UTF-8"?>
"-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN"
"http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd">
<ejb-name>GangsterEJB</ejb-name>
<table-name>gangster</table-name>
<!-- CMP Fields (see CMP-Fields) -->
<!-- Load Groups (see Load Groups)-->
<!-- Queries (see Queries) -->
In this case the DOCTYPE declaration is optional, but will reduce configuration errors. In addition, all of the elements are optional except for ejb-name, which is used to match the configuration to an entity declared in the ejb-jar.xml file. Unless noted otherwise, the default values come from the defaults section of either the jbosscmp-jdbc.xml file, or the conf/standardjbosscmp-jdbc.xml file for the current server configuration file set. The defaults section is discussed in Defaults.
A detailed description of each entity element follows:
Although cmp-fields have not changed in CMP 2.0 with regards to functionality, they are no longer declared using fields in the bean implementation class. In CMP 2.0, cmp-fields are not directly accessible; rather each cmp-field is declared in the bean implementation class of the entity with a set of abstract accessor methods. Abstract accessors are similar to JavaBean property accessors, except no implementation is given. For example, the following listing declares the gangsterId, name, nickName, and badness cmp-field accessors in the gangster entity:
public abstract class GangsterBean implements EntityBean {
public abstract Integer getGangsterId();
public abstract void setGangsterId(Integer gangsterId);
public abstract String getName();
public abstract void setName(String param);
public abstract String getNickName();
public abstract void setNickName(String param);
public abstract int getBadness();
public abstract void setBadness(int param);
Each cmp-field is required to have both a getter and a setter method, and each accessor method must be declared public abstract.
The declaration of a cmp-field in the ejb-jar.xml file has not changed at all in EJB 2.0. For example, to declare the gangsterId, name, nickName and badness fields defined in See Sample cmp-field abstract accessor declaration. you would add the following to the ejb-jar.xml file:
The mapping of an ejb-jar.xml cmp-field is declared in a jbosscmp-jdbc.xml cmp-field element within the entity. The content model of the cmp-field element of the jbosscmp-jdbc.xml is shown in See The jbosscmp-jdbc.xml cmp-field element content model...
An example usage of cmp-field mapping is shown in See The jbosscmp-jdbc.xml cmp-field Mapping.:
<ejb-name>GangsterEJB</ejb-name>
<table-name>gangster</table-name>
<field-name>gangsterId</field-name>
<column-name>name</column-name>
<field-name>nickName</field-name>
<column-name>nick_name</column-name>
<jdbc-type>VARCHAR</jdbc-type>
<sql-type>VARCHAR(64)</sql-type>
<field-name>badness</field-name>
<column-name>badness</column-name>
In the cmp-field element, you can control the name and datatype of the column. A detailed description of each element follows:
Another benefit of abstract accessors for cmp-fields is the ability to have read-only fields. The 1.x CMP engine, JAWS, supported read-only with read-time-out for entities. However, the problem with CMP 1.x was the bean provider could always change the value of a field on a read-only entity, and there was nothing the container could do. With CMP 2.x, the container provides the implementation for the accessor, and therefore can throw an exception when the bean provider attempts to set the value of a read-only bean.
In JBossCMP this feature has been extended to the field level with the addition of the read-only and read-time-out elements to the cmp-field element. These elements work the same way as they do at the entity level. If a field is read-only, it will never be used in an INSERT or UPDATE statement. If a primary key field is read-only, the create method will throw a CreateException. If a set accessor is called for a read-only field, it throws an EJBException. Read-only fields are useful for fields that are filled in by database triggers, such as last update. A read-only cmp-field declaration example follows:
The audit element of the entity section allows one to specify how access to and entity bean is audited. This is only allowed when an entity bean is accessed under a security domain so that this is a caller identity established. The content model of the audit element is given in See The jbosscmp-jdbc.xml audit element content model..
A Dependent Value Class (DVC) is a fancy term used to identity any Java class that is the type of a cmp-field, other than the automatically recognized types. See section 10.3.3 of the Enterprise JavaBeans Specification Version 2.0 Final Release for further requirements. By default, a DVC is serialized, and the serialized form is stored in a single database column. Although not discussed here, there are several known issues with the long-term storage of classes in serialized form. JBossCMP supports the storage of the internal data of a DVC into one or more columns. This is useful for supporting legacy JavaBeans and database structures. It is not uncommon to find a database with a highly flattened structure (e.g., a PURCHASE_ORDER table with the fields SHIP_LINE1, SHIP_LINE2, SHIP_CITY, etc. and an additional set of fields for the billing address). Other common database structures include telephone numbers with separate fields for area code, exchange, and extension, or a person's name spread across several fields. With a DVC, multiple columns can be mapped to one logical JavaBean. It is important to note that DVCs are not the same thing as Dependent Value Objects.3
JBossCMP requires that a DVC to be mapped must follow the JavaBeans naming specification for simple properties, and that each property to be stored in the database must have both a getter and a setter method.4 Furthermore, the bean must be serializable and must have a no argument constructor. A property can be any simple type, an unmapped DVC or a mapped DVC, but cannot be an EJB.5 A DVC mapping is specified within the dependent-value-classes element, and See The jbosscmp-jdbc dependent-value-classes element model.. shows the content model for this element.
An example declaration of a phone number DVC and contact information DVC, and the corresponding classes is shown in See The jbosscmp-jdbc.xml Dependent Value Class Declaration.:
<description>A phone number</description>
<class>org.jboss.cmp2.crimeportal.PhoneNumber</class>
<property-name>areaCode</property-name>
<column-name>area_code</column-name>
<property-name>exchange</property-name>
<column-name>exchange</column-name>
<property-name>extension</property-name>
<column-name>extension</column-name>
<description>General contact info</description>
<class>org.jboss.cmp2.crimeportal.ContactInfo</class>
<property-name>cell</property-name>
<column-name>cell</column-name>
<property-name>pager</property-name>
<column-name>pager</column-name>
<property-name>email</property-name>
<column-name>email</column-name>
<jdbc-type>VARCHAR</jdbc-type>
<sql-type>VARCHAR(128)</sql-type>
public class PhoneNumber implements Serializable {
/** The first three digits of the phone number. */
/** The middle three digits of the phone number. */
/** The last four digits of the phone number. */
public class ContactInfo implements Serializable {
Each DVC is declared with a dependent-value-class element. A DVC is identified by the Java class type declared in the class element. Each property to be persisted is declared with a property element. This specification is based on the cmp-field element, so it should be self-explanatory. This restriction will also be removed in a future release. The current proposal involves storing the primary key fields in the case of a local entity and the entity handle in the case of a remote entity.
The dependent-value-classes section defines the internal structure and default mapping of the classes. When JBossCMP encounters a field that has an unknown type, it searches the list of registered DVCs, and if a DVC is found, it persists this field into a set of columns, otherwise the field is stored in serialized form in a single column. JBossCMP does not support inheritance of DVCs; therefore, this search is only based on the declared type of the field. A DVC can be constructed from other DVCs, so when JBossCMP runs into a DVC, it flattens the DVC tree structure into a set of columns. If JBossCMP finds a DVC circuit during startup, it will throw an EJBException. The default column name of a property is the column name of the base cmp-field followed by an underscore and then the property column name. If the property is a DVC, the process is repeated. For example, a cmp-field of type ContactInfo (seeSee The jbosscmp-jdbc.xml Dependent Value Class Declaration.) and named info will have the following columns:
The automatically generated column names can quickly become excessively long and awkward. The default mappings of columns can be overridden in the entity element as follows:
<ejb-name>GangsterEJB</ejb-name>
<field-name>contactInfo</field-name>
<property-name>cell.areaCode</property-name>
<column-name>cell_area</column-name>
<property-name>cell.exchange</property-name>
<column-name>cell_exch</column-name>
<property-name>cell.extension</property-name>
<column-name>cell_ext</column-name>
<property-name>pager.areaCode</property-name>
<column-name>page_area</column-name>
<property-name>pager.exchange</property-name>
<column-name>page_exch</column-name>
<property-name>pager.extension</property-name>
<column-name>page_ext</column-name>
<property-name>email</property-name>
<column-name>email</column-name>
<jdbc-type>VARCHAR</jdbc-type>
<sql-type>VARCHAR(128)</sql-type>
As shown in See The jbosscmp-jdbc.xml cmp-field Dependent Value Class Property Override. , when overriding property info for the entity, you need to refer to the property from a flat perspective as in cell.areaCode.
Container Managed Relationships (CMRs) are a powerful new feature of CMP 2.0. Programmers have been creating relationships between entity objects since EJB 1.0 was introduced (not to mention since the introduction of databases), but before CMP 2.0 the programmer had to write a lot of code for each relationship in order to extract the primary key of the related entity and store it in a pseudo foreign key field. The simplest relationships were tedious to code, and complex relationships with referential integrity required many hours to code. With CMP 2.0 there is no need to code relationships by hand. The container can manage one-to-one, one-to-many and many-to-many relationships, with referential integrity. One restriction with CMRs is that they are only defined between local interfaces. This means that a relationship cannot be created between two entities in different virtual machines.6
There are two basic steps to create a container managed relationship: create the cmr-field abstract accessors and declare the relationship in the ejb-jar.xml file. The following two sections describe these steps.
CMR-Field abstract accessors have the same signatures as cmp-fields, except that single-valued relationships must return the local interface of the related entity, and multi-valued relationships can only return a java.util.Collection (or java.util.Set ) object. As with cmp-fields, at least one of the two entities in a relationship must have cmr-field abstract accessors. For example, to declare a one-to-many relationship between Organization and Gangster, first add the following to the OrganizationBean class:
public abstract class OrganizationBean implements EntityBean {
public abstract Set getMemberGangsters();
public abstract class GangsterBean implements EntityBean {
public abstract Organization getOrganization();
public abstract void setOrganization(Organization org);
Although in See Collection Valued cmr-field Abstract Accessor Declaration. and See Single Valued cmr-field Abstract Accessor Declaration. each bean declared a cmr-field, only one of the two beans in a relationship must have a set of accessors. As with cmp-fields, a cmr-field is required to have both a getter and a setter method.
The declaration of relationships in the ejb-jar.xml file is complicated and error prone. The XML used to declare relationships is as inconsistent as Visual Basic syntax. The best way to configure a relationship is to use a tool, such as XDoclet, or cut and paste a working relationship. The declaration of the Organization-Gangster relationship follows:
<ejb-relation-name>Organization-Gangster</ejb-relation-name>
<ejb-relationship-role-name>org-has-gangsters
<multiplicity>One</multiplicity>
<ejb-name>OrganizationEJB</ejb-name>
<cmr-field-name>memberGangsters</cmr-field-name>
<cmr-field-type>java.util.Set</cmr-field-type>
<ejb-relationship-role-name>gangster-belongs-to-org
<multiplicity>Many</multiplicity>
<ejb-name>GangsterEJB</ejb-name>
<cmr-field-name>organization</cmr-field-name>
As you can see, each relationship is declared with an ejb-relation element within the top level relationships7 element, and each ejb-relation contains two ejb-relationship-role elements (one for each entity in the relationship). The ejb-relationship-role tags are as follows:
After adding the cmr-field abstract accessors and declaring the relationship, the relationship should be functional. For more information on relationships, see section 10.3 of the EJB 2.0 specification. The next section discusses the database mapping of the relationship.
Relationships can be mapped using either a foreign key or a separate relation-table. One-to-one and one-to-many relationships use the foreign key mapping style by default, and many-to-many relationships use only the relation-table mapping style. The mapping of a relationship is declared in the relationships section of the jbosscmp-jdbc.xml descriptor via ejb-relation elements. Relationships are identified by the ejb-relation-name from the ejb-jar.xml file. The jbosscmp-jdbc.xml ejb-relation element content model is shown in See The jbosscmp-jdbc.xml ejb-relation element content model..
The basic template of the relationship mapping declaration for Organization-Gangster follows:
<ejb-relation-name>Organization-Gangster</ejb-relation-name>
<ejb-relationship-role-name>org-has-gangsters
<column-name>organization</column-name>
<ejb-relationship-role-name>gangster-belongs-to-org
After the ejb-relation-name of the relationship being mapped is declared, the mapping style is declared using a foreign-key-mapping element or a relation-table-mapping element, both of which are discussed in the next two sections. The read-only and read-time-out elements have the same semantics they did in the entity element. The ejb-relationship-role elements are optional, but if one is declared, the other must also be declared. A detailed description of the elements contained in the ejb-relationship-role element follows:
The ejb-relation element must contain either a foreign-key-mapping element or a relation-table-mapping element, which are described in the foreign key mapping and relation-table mapping sections respectively. This element may also contain a pair of ejb-relationship-role elements as described in the following section.
Each of the two ejb-relationship-role elements contains mapping information specific to an entity in the relationship, and the content model of the ejb-relationship-role element is shown in See The jbosscmp-jdbc ejb-relationship-role element content model..
A detailed description of the main elements follows:
As noted above, the key-fields element contains a key-field for each primary key field of the current entity. The key-field element uses the same syntax as the cmp-field element of the entity, except that key-field does not support the not-null option. Key-fields of a relation-table are automatically not null, because they are the primary key of the table. On the other hand, foreign key fields must be nullable by default. This is because the current implementation of JBossCMP inserts a row into the database for a new entity between ejbCreate and ejbPostCreate. Since the EJB specification does not allow a relationship to be modified until ejbPostCreate, a foreign key will be initially set to null. There is a similar problem with removal. As of the 3.2.2 release, one can change this insert behavior using the jboss.xml insert-after-ejb-post-create container configuration flag. An example jboss.xml fragment illustrating the use is given in See Sample jboss.xml fragment illustrating insert-after-ejb-post-create..
<container-configuration extends="Standard CMP 2.x EntityBean">
<container-name>INSERT after ejbPostCreate Container</container-name>
<insert-after-ejb-post-create>true</insert-after-ejb-post-create>
An alternate means of working around the non-null foreign key issue is to map the foreign key elements onto non-null cmp fields. In this case you simply populate the foreign key fields in ejbCreate using the associated cmp field setters.
The content model of the key-fields element is shown in See The jbosscmp-jdbc key-fields element content model..
A detailed description of the elements contained in the key-field element follows:
Foreign key mapping is the most common mapping style for one-to-one and one-to-many relationships, but is not allowed for many-to many relationships. The foreign key mapping element is simply declared by adding an empty foreign key-mapping element to the ejb-relation element.
As noted in the previous section, with a foreign key mapping the key-fields declared in the ejb-relationship-role are added to the table of the related entity. If the key-fields element is empty, a foreign key will not be created for the entity. In a one-to-many relationship, the many side (Gangster in the example) must have an empty key-fields element, and the one side (Organization in the example) must have a key-fields mapping. In one-to-one relationships, one or both roles can have foreign keys.
The foreign key mapping is not dependent on the direction of the relationship. This means that in a one-to-one unidirectional relationship (only one side has an accessor) one or both roles can still have foreign keys. The complete foreign key mapping for the Organization-Gangster relationship is shown in See The jbosscmp-jdbc.xml Foreign Key Mapping. with the foreign key elements highlighted in bold:
Relation table mapping is less common for one-to-one and one-to-many relationships, but is the only mapping style allowed for many-to-many relationships. Relation table mapping is defined using the relation-table-mapping element, the content model of which is shown in See The jbosscmp-jdbc relation-table-mapping element content model..
The relation-table-mapping for the Gangster-Job relationship is shown in with table mapping elements highlighted in bold:
<ejb-relation-name>Gangster-Jobs</ejb-relation-name>
<table-name>gangster_job</table-name>
<ejb-relationship-role-name>gangster-has-jobs
<field-name>gangsterId</field-name>
<column-name>gangster</column-name>
<ejb-relationship-role-name>job-has-gangsters
<column-name>job</column-name>
The relation-table-mapping element contains a subset of the options available in the entity element. A detailed description of these elements is reproduced here for convenience:
Another powerful new feature of CMP 2.0 is the introduction of the EJB Query Language (EJB-QL) and ejbSelect methods. In CMP 1.1, every EJB container had a different way to specify finders, and this was a serious threat to J2EE portability. In CMP 2.0, EJB-QL was created to specify finders and ejbSelect methods in a platform independent way. The ejbSelect method is designed to provide private query statements to an entity implementation. Unlike finders, which are restricted to only return entities of the same type as the home interface on which they are defined, ejbSelect methods can return any entity type or just one field of the entity.
EJB-QL is beyond the scope of this documentation, so only the basic method coding and query declaration will be covered here. For more information, see Chapter 11 of the Enterprise JavaBeans Specification Version 2.0 Final Release or one of the many excellent articles written on CMP 2.0.
The declaration of finders has not changed in CMP 2.0. Finders are still declared in the home interface (local or remote) of the entity. Finders defined on the local home interface do not throw a RemoteException. The following code declares the findBadDudes_ejbql9 finder on the GangsterHome interface:
public interface GangsterHome extends EJBLocalHome {
Collection findBadDudes_ejbql(int badness) throws FinderException;
The ejbSelect methods are declared in the entity implementation class, and must be public abstract just like cmp-field and cmr-field abstract accessors. Select methods must be declared to throw a FinderException, but not a RemoteException. The following code declares an ejbSelect method:
The EJB 2.0 specification requires that every ejbSelect or finder method (except findByPrimaryKey) have an EJB-QL query defined in the ejb-jar.xml file.10 The EJB-QL query is declared in a query element, which is contained in the entity element. The following is the declaration for the queries defined in Listing 5-1 and Listing 5-2:
<ejb-name>GangsterEJB</ejb-name>
<method-name>findBadDudes_ejbql</method-name>
<method-param>int</method-param>
<method-name>ejbSelectBoss_ejbql</method-name>
<method-param>java.lang.String</method-param>
SELECT DISTINCT underling.organization.theBoss
WHERE underling.name = ?1 OR underling.nickName = ?1
EJB-QL is similar to SQL but has some surprising differences. The following are some important things to note about EJB-QL:
'CA' = g.shippingAddress.state NOT Legal
(r.amountPaid * .01) > 300 NOT Legal
r.amountPaid > (300 / .01) Legal
The EJB-QL to SQL mapping can be overridden in the jbosscmp-jdbc.xml file. The finder or ejbSelect is still required to have an EJB-QL declaration in the ejb-jar.xml file, but the ejb-ql element can be left empty. Currently the SQL can be overridden with JBossQL, DynamicQL, DeclaredSQL or a BMP style custom ejbFind method. All EJB-QL overrides are non-standard extensions to the EJB 2.0 specification, so use of these extensions will limit portability of your application. All of the EJB-QL overrides, except for BMP custom finders, are declared using the entity/query element, and the content model is shown in See The jbosscmp-jdbc query element content model..
JBossQL is a superset of EJB-QL that is designed to address some of the inadequacies of EJB-QL. In addition to a more flexible syntax, new functions, key words, and clauses have been added to JBossQL. At the time of this writing, JBossQL includes support for an ORDER BY, OFFSET and LIMIT clauses, parameters in the IN and LIKE operators, the COUNT, MAX, MIN, AVG, SUM, UCASE and LCASE functions, and queries can also include functions in the SELECT clause for ejbSelect methods.
JBossQL is declared in the jbosscmp-jdbc.xml file with a query/jboss-ql element containing the JBossQL query. See The jbosscmp-jdbc.xml JBossQL Override. provides an example JBossQL declaration and See JBossQL SQL Mapping. gives the corresponding generated SQL:
<ejb-name>GangsterEJB</ejb-name>
<method-name>findBadDudes_jbossql</method-name>
Another capability of JBossQL is the ability to retrieve finder results in blocks using the LIMIT and OFFSET functions. For example, to iterate through the large number of jobs performed, the following findManyJobs_jbossql finder may be defined.
<ejb-name>GangsterEJB</ejb-name>
<method-name>findManyJobs_jbossql</method-name>
<method-param>int</method-param>
DynamicQL allows the runtime generation and execution of JBossQL queries. A DynamicQL query method is an abstract method that takes a JBossQL query and the query arguments as parameters. JBossCMP compiles the JBossQL and executes the generated SQL. The following generates a JBossQL query that selects all the gangsters that have a hangout in any state in the states set:
public abstract class GangsterBean implements EntityBean {
public Set ejbHomeSelectInStates(Set states)
StringBuffer jbossQl = new StringBuffer();
jbossQl.append("SELECT OBJECT(g) ");
jbossQl.append("FROM gangster g ");
jbossQl.append("WHERE g.hangout.state IN (");
for(int i = 0; i < states.size(); i++)
jbossQl.append("?").append(i+1);
jbossQl.append(") ORDER BY g.name");
// pack arguments into an Object[]
Object[] args = states.toArray(new Object[states.size()]);
return ejbSelectGeneric(jbossQl.toString(), args);
The DynamicQL ejbSelect method may have any valid ejbSelect method name, but the method must always take a String and Object array as parameters. DynamicQL is declared in the jbosscmp-jdbc.xml file with an empty query/dynamic-ql element. The following is the declaration for the query defined in See DynamicQL Example Code.:
DeclaredSQL is based on the legacy JAWS CMP 1.1 engine finder declaration, but has been updated for CMP 2.0. Commonly this declaration is used to limit a query with a WHERE clause that cannot be represented in EJB-QL or JBossQL. The content model for the declared-sql element is given in See The jbosscmp-jdbc declared-sql element content model...
See The jbosscmp-jdbc.xml DeclaredSQL Override. provides an example DeclaredSQL declaration and See DeclaredSQL SQL Mapping. the corresponding generated SQL:
<ejb-name>GangsterEJB</ejb-name>
<method-name>findBadDudes_declaredsql</method-name>
<method-param>int</method-param>
<where><![CDATA[ badness > {0} ]]></where>
As you can see, JBossCMP generates the SELECT and FROM clauses necessary to select the primary key for this entity. If desired an additional FROM clause can be specified that is appended to the end of the automatically generated FROM clause. See See The jbosscmp-jdbc.xml DeclaredSQL Override With From Clause. for an example DeclaredSQL declaration with an additional FROM clause and See The jbosscmp-jdbc.xml DeclaredSQL With From Clause SQL Mapping. for the generated SQL:
<ejb-name>GangsterEJB</ejb-name>
<method-name>ejbSelectBoss_declaredsql</method-name>
<method-param>java.lang.String</method-param>
<ejb-name>GangsterEJB</ejb-name>
<from><![CDATA[, gangster g, organization o]]></from>
(LCASE(g.name) = {0} OR LCASE(g.nick_name) = {0}) AND
FROM gangster boss, gangster g, organization o
WHERE (LCASE(g.name) = ? OR LCASE(g.nick_name) = ?) AND
g.organization = o.name AND o.the_boss = boss.id
Notice that the FROM clause starts with a comma. This is because the container appends the declared FROM clause to the end of the generated FROM clause. It is also possible for the FROM clause to start with a SQL JOIN statement. Since this is an ejbSelect method, it must have a select element to declare the entity that will be selected. Note that an alias is also declared for the query. If an alias is not declared, the table-name is used as the alias, resulting in a SELECT clause with the table_name.field_name style column declarations. Not all database vendors support the table_name.field_name syntax, so the declaration of an alias is preferred. See The jbosscmp-jdbc.xml DeclaredSQL Override With From Clause. also used the optional empty distinct element, which causes the SELECT clause to use the SELECT DISTINCT declaration. The DeclaredSQL declaration can also be used in ejbSelect methods to select a cmp-field. See The jbosscmp-jdbc.xml DeclaredSQL ejbSelect Override. provides an example which selects all of the zip codes in which an Organization operates, and See The jbosscmp-jdbc.xml DeclaredSQL ejbSelect SQL Mapping. shows the corresponding SQL:
<ejb-name>OrganizationEJB</ejb-name>
<method-name>ejbSelectOperatingZipCodes_declaredsql</method-name>
<method-param>java.lang.String</method-param>
<ejb-name>LocationEJB</ejb-name>
<field-name>zipCode</field-name>
<from><![CDATA[ , organization o, gangster g ]]></from>
LCASE(o.name) = {0} AND o.name = g.organization AND
FROM location hangout, organization o, gangster g
WHERE LCASE(o.name) = ? AND o.name = g.organization AND g.hangout = hangout.id
JBossCMP DeclaredSQL uses a completely new parameter handling system, which supports entity and DVC parameters. Parameters are enclosed in curly brackets and use a base zero index, which is different from the base one EJB-QL parameters. There are three categories of parameters: simple, DVC, and entity:
JBossCMP continues the tradition of JAWS in supporting bean managed persistence custom finders. If a custom finder matches a finder declared in the home or local home interface, JBossCMP will always call the custom finder over any other implementation declared in the ejb-jar.xml or jbosscmp-jdbc.xml files. The following simple example finds the entities by a collection of primary keys:12
The goal of optimized loading is to load the smallest amount of data required to complete a transaction in the least number of queries. The tuning of JBossCMP depends on a detailed knowledge of the loading process. This section describes the internals of the JBossCMP loading process and its configuration. Tuning of the loading process really requires a holistic understanding of the loading system, so this chapter may have to be read more than once.
The easiest way to investigate the loading process is to look at a usage scenario. The most common scenario is to locate a collection of entities and iterate over the results performing some operation. The following example generates an html table containing all of the gangsters:
Assume this code is called within a single transaction and all optimized loading has been disabled. At line 5, JBossCMP will execute the following query:
Then at line 8, in order to load the eight Gangsters in the sample database, JBossCMP executes the following eight queries:
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
SELECT name, nick_name, badness, hangout, organization
There are two problems with this scenario. First, an excessive number of queries are executed because JBossCMP executes one query for findAll and one query for each element found. This is known as the "n+1" problem13 and is addressed with the read-ahead strategies described in the following sections. Second, values of unused fields are loaded because JBossCMP loads the hangout and organization fields,14 which are never accessed. Configuration of eager loading is described in Eager-loading Process. The following table shows the execution of the queries:
The configuration and optimization of the loading system begins with the declaration of named load groups in the entity. A load group contains the names of cmp-fields and cmr-fields with a foreign key (e.g., Gangster in the Organization-Gangster example) that will be loaded in a single operation. An example configuration is shown in See The jbosscmp-jdbc.xml Load Group Declaration.:
<ejb-name>GangsterEJB</ejb-name>
<load-group-name>basic</load-group-name>
<field-name>nickName</field-name>
<field-name>badness</field-name>
<load-group-name>contact info</load-group-name>
<field-name>nickName</field-name>
<field-name>contactInfo</field-name>
<field-name>hangout</field-name>
In See The jbosscmp-jdbc.xml Load Group Declaration., two load groups are declared: basic and contact info. Note that the load groups do not need to be mutually exclusive. For example, both of the load groups contain the nickName field. In addition to the declared load groups, JBossCMP automatically adds a group named "*" (the star group) that contains every cmp-field and cmr-field with a foreign key in the entity.
Optimized loading in JBossCMP is called read-ahead. This term was inherited from JAWS, and refer to the technique of reading the row for an entity being loaded, as well as the next several rows; hence the term read-ahead. JBossCMP implements two main strategies (on-find and on-load) to optimize the loading problem identified in the previous section. The extra data loaded during read-ahead is not immediately associated with an entity object in memory, as entities are not materialized in JBoss until actually accessed. Instead, it is stored in the preload cache where it remains until it is loaded into an entity or the end of the transaction occurs. The following sections describe the read-ahead strategies.
The on-find strategy reads additional columns when the query is invoked. If the query in the scenario detailed in See Loading Scenario Example Code. is on-find optimized, JBossCMP will execute the following query at line 5:
SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness
Then at line 8, all of the required data would be in the preload cache, so no additional queries would be executed. This strategy is effective for queries that return a small amount of data, but becomes very inefficient when trying to load a large result set into memory.15 The following table shows the execution of this query:
The read-ahead strategy and load-group for a query is defined in the query element. If a read-head strategy is not declared in the query element, the strategy declared in the entity element or defaults element is used. The on-find configuration follows:
<ejb-name>GangsterEJB</ejb-name>
<method-name>findAll_onfind</method-name>
<eager-load-group>basic</eager-load-group>
One problem with the on-find strategy is that it must load additional data for every entity selected. Commonly in web applications only a fixed number of results are rendered on a page. Since the preloaded data is only valid for the length of the transaction, and a transaction is limited to a single web http hit, most of the preloaded data is not used. The on-load strategy discussed in the next section does not suffer from this problem.
The on-load strategy block loads additional data for several entities when an entity is loaded, starting with the requested entity and the next several entities in the order they were selected.16 This strategy is based on the theory that the results of a find or ejbSelect will be accessed in forward order. When a query is executed, JBossCMP stores the order of the entities found in the list cache. Later, when one of the entities is loaded, JBossCMP uses this list to determine the block of entities to load. The number of lists stored in the cache is specified with the list-cachemax element of the entity. This strategy is also used when faulting in data not loaded in the on-find strategy. With this strategy, the query executed at line 5 of See Loading Scenario Example Code. remains unchanged.
If, for example, the on-load/page-size is set to four, JBossCMP will execute the following two queries to load the name, nickName and badness fields for the entities:
SELECT id, name, nick_name, badness
WHERE (id=0) OR (id=1) OR (id=2) OR (id=3)
SELECT id, name, nick_name, badness
WHERE (id=4) OR (id=5) OR (id=6) OR (id=7)
The following table shows the execution of these queries:
As with the on-find strategy, on-load is declared in the read-ahead element. The on-load configuration for this example is shown in See The jbosscmp-jdbc.xml on-load Declaration.:
The none strategy is really an anti-strategy. This strategy causes the system to fall back to the default lazy-load code, and specifically does not read-ahead any data or remember the order of the found entities. This results in the queries and performance shown at the beginning of this chapter. The none strategy is declared with a read-ahead element. If the read-ahead element contains a page-size element or eager-load-group, it is ignored. The none strategy is declared in See The jbosscmp-jdbc.xml none Declaration.:
In the previous section several steps use the phrase "when the entity is loaded." This was intentionally left vague because the commit option specified for the entity and the current state of the transaction determine when an entity is loaded. The following section describes the commit options and the loading processes.
Central to the loading process are the commit options, which control when the data for an entity expires. JBoss supports four commit options A, B, C and D. The first three are described in section 10.5.9 of the Enterprise JavaBeans Specification Version 2.0 Final Release and the forth, D, is specific to JBoss. A detailed description of each commit option follows:
The commit option is declared in the jboss.xml file. For a detailed description of this file see Chapter 5. The following example changes the commit option to A for all entity beans in the application:
One of the most important changes in CMP 2.0 is the change from using class fields for cmp-fields to abstract accessor methods. In CMP 1.x, the container could not know which fields were required in a transaction, so the container had to eager-load every field when loading the17 bean. In CMP 2.x, the container creates the implementation for the abstract accessors, so the container can know when the data for a field is required. JBossCMP can be configured to eager-load only some of the fields when loading an entity, and later lazy-load the remaining fields as needed.
When an entity is loaded, JBossCMP must determine the fields that need to be loaded. By default, JBossCMP will use the eager-load-group of the last query that selected this entity. If the entity has not been selected in a query, or the last query used the none read-ahead strategy, JBossCMP uses the default eager-load-group declared for the entity. In the See The jbosscmp-jdbc.xml Eager Load Declaration. configuration, the basic load group is set as the default eager-load-group for the GangsterEJB entity:
<ejb-name>GangsterEJB</ejb-name>
<load-group-name>most</load-group-name>
<field-name>nickName</field-name>
<field-name>badness</field-name>
<field-name>hangout</field-name>
<field-name>organization</field-name>
<eager-load-group>most</eager-load-group>
The eager loading process is initiated the first time a method is called on an entity in a transaction. A detailed description of the load process follows:
Lazy-loading is the other half of eager-loading. If a field is not eager-loaded, it must be lazy-loaded. When the bean accesses an unloaded field, JBossCMP loads the field and any field in a lazy-load-group of which the unloaded field is a member. JBossCMP performs a set join and then removes any field that is already loaded. An example configuration is shown in See The jbosscmp-jdbc.xml Lazy Load Group Declaration.:
<ejb-name>GangsterEJB</ejb-name>
<load-group-name>basic</load-group-name>
<field-name>nickName</field-name>
<field-name>badness</field-name>
<load-group-name>contact info</load-group-name>
<field-name>nickName</field-name>
<field-name>contactInfo</field-name>
<field-name>hangout</field-name>
<load-group-name>basic</load-group-name>
<load-group-name>contact info</load-group-name>
When the bean provider calls getName() with this configuration, JBossCMP loads name, nickName and badness (assuming they are not already loaded). When the bean provider calls getNickName(), the name, nickName, badness, contactInfo, and hangout are loaded. A detailed description of the lazy-loading process follows:
Relationships are a special case in lazy-loading because a cmr-field is both a field and query. As a field it can be on-load block loaded, meaning the value of the currently sought entity and the values of the cmr-field for the next several entities are loaded. As a query, the field values of the related entity can be preloaded using on-find.
Again, the easiest way to investigate the loading is to look at a usage scenario. In this example, an html table is generated containing each gangster and their hangout. The example code follows:
For this example, the configuration of the Gangster findAll_onfind query is unchanged from the on-find section. The configuration of the Location entity and Gangster-Hangout relationship follows:
<ejb-name>LocationEJB</ejb-name>
<load-group-name>quick info</load-group-name>
<field-name>state</field-name>
<field-name>zipCode</field-name>
<ejb-relation-name>Gangster-Hangout</ejb-relation-name>
<ejb-relationship-role-name>gangster-has-a-hangout
<eager-load-group>quick info</eager-load-group>
<ejb-relationship-role-name>hangout-for-a-gangster
<field-name>locationID</field-name>
SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness
Then at line 29, JBossCMP executes the following two queries to load the city, state, and zip fields of the hideout:
SELECT gangster.id, gangster.hangout,
location.city, location.st, location.zip
WHERE (gangster.hangout=location.id) AND
((gangster.id=0) OR (gangster.id=1) OR
(gangster.id=2) OR (gangster.id=3))
SELECT gangster.id, gangster.hangout,
location.city, location.st, location.zip
WHERE (gangster.hangout=location.id) AND
((gangster.id=4) OR (gangster.id=5) OR
(gangster.id=6) OR (gangster.id=7))
The following table shows the execution of the queries:
All of the examples presented in this chapter have been defined to run in a transaction. Transaction granularity is a dominating factor in optimized loading because transactions define the lifetime of preloaded data. If the transaction completes, commits, or rolls back, the data in the preload cache is lost. This can result in a severe negative performance impact.
The performance impact of running without a transaction will be demonstrated with an example similar to See Loading Scenario Example Code.. This example uses an on-find optimized query that selects the first four gangsters (to keep the result set small), and it is executed without a wrapper transaction. The example code follows:
The query executed at line 53 is shown in See No Transaction on-find Optimized findAll Query.:
SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness
Normally this would be the only query executed, but since this code is not running in a transaction, all of the preloaded data is thrown away as soon as findAll returns. Then at line 56 JBossCMP executes the following four queries (one for each loop):18
SELECT id, name, nick_name, badness
WHERE (id=0) OR (id=1) OR (id=2) OR (id=3)
SELECT id, name, nick_name, badness
WHERE (id=1) OR (id=2) OR (id=3)
SELECT id, name, nick_name, badness
SELECT name, nick_name, badness
See No Transaction on-find optimized query execution. shows the execution of the queries:
This performance is much worse than read-ahead none because of the amount of data loaded from the database. The number of rows loaded is determined by the following equation:
This all happens because the transaction in the example is bounded by a single call on the entity. This brings up the important question "How do I run my code in a transaction?" The answer depends on where the code runs. If it runs in an EJB (session, entity, or message driven), the method must be marked with the Required or RequiresNew trans-attribute in the assembly-descriptor. If the code is not running in an EJB, a user transaction is necessary. The following code wraps a call to the method declared in See User Transaction Example Code. with a user transaction:
public String createGangsterHtmlTable_with_tx()
InitialContext ctx = new InitialContext();
tx = (UserTransaction) ctx.lookup("UserTransaction");
String table = createGangsterHtmlTable_no_tx();
if (tx.getStatus() == Status.STATUS_ACTIVE)
if (tx != null) tx.rollback();
catch (SystemException unused)
// eat the exception we are exceptioning out anyway
if (e instanceof FinderException)
New to 3.2 is support for optimistic locking of entity beans. Optimistic locking must be used in conjunction with the "Instance Per Transaction CMP 2.x EntityBean" configuration of the CMP2 container which does not impose pessimistic locking at the application server level. This container configuration is then extended to use the org.jboss.ejb.plugins.lock.JDBCOptimisticLock lock whose behavior is driven by the jbosscmp-jdbc.xml optimistic policy. A sample jboss.xml descriptor which illustrates the prototype configuration is given in See The prototype optimistic locking entity container configuration..
<?xml version="1.0" encoding="UTF-8"?>
"http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">
<ejb-name>EntityGroupLocking</ejb-name>
<local-jndi-name>local/EntityGroupLocking</local-jndi-name>
<configuration-name>Optimistic Container</configuration-name>
extends="Instance Per Transaction CMP 2.x EntityBean">
<container-name>Optimistic Container</container-name>
<!-- overrides: org.jboss.ejb.plugins.lock.NoLock -->
<locking-policy>org.jboss.ejb.plugins.lock.JDBCOptimisticLock
This optimistic locking configuration allows multiple instances of the same entity bean to be active simultaneously. Consistency is enforced based on the optimistic locking policy choice which drives the JDBCOptimisticLock behavior. The optimistic locking policy choice defines the set of fields that are used in the commit time write of modified data to the database. The optimistic consistency check asserts that the values of the chosen set of fields has the same values in the database as existed when the current transaction was started. This is done using a select for UPDATE WHERE... statement that contains the value assertions.
You specify the optimistic locking policy choice using an entity/optimistic-locking element in the jbosscmp-jdbc.xml descriptor. The content model of the optimistic-locking element is shown in See The jbosscmp-jdbc optimistic-locking element content model. and the description of the elements follows.
A sample jbosscmp-jdbc.xml descriptor illustrating all of the optimistic locking strategies is given in See A sample jbosscmp-jdbc.xml descriptor illustrating the optimistic locking strategies..
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN" "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd">
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>Hypersonic SQL</datasource-mapping>
<ejb-name>EntityGroupLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entitygrouplocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<load-group-name>string</load-group-name>
<field-name>stringField</field-name>
<load-group-name>all</load-group-name>
<field-name>stringField</field-name>
<field-name>dateField</field-name>
<group-name>string</group-name>
<ejb-name>EntityModifiedLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entitymodifiedlocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<ejb-name>EntityReadLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entityreadlocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<ejb-name>EntityVersionLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entityversionlocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<field-name>versionField</field-name>
<column-name>ol_version</column-name>
<jdbc-type>INTEGER</jdbc-type>
<sql-type>INTEGER(5)</sql-type>
<ejb-name>EntityTimestampLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entitytimestamplocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<field-name>versionField</field-name>
<column-name>ol_timestamp</column-name>
<jdbc-type>TIMESTAMP</jdbc-type>
<ejb-name>EntityKeyGeneratorLocking</ejb-name>
<create-table>true</create-table>
<remove-table>true</remove-table>
<table-name>entitykeygenlocking</table-name>
<field-name>dateField</field-name>
<field-name>integerField</field-name>
<field-name>stringField</field-name>
<key-generator-factory>UUIDKeyGeneratorFactory
<field-type>java.lang.String</field-type>
<field-name>uuidField</field-name>
<column-name>ol_uuid</column-name>
<jdbc-type>VARCHAR</jdbc-type>
Support for primary key generation outside of the entity bean class has been added to 3.2. This is available through custom implementations of the entity creation command objects used to insert entities into a persistent store. The list of available commands is specified in entity-commands element of the jbosscmp-jdbc.xml descriptor. The default entity-command may be specified in the jbosscmp-jdbc.xml in defaults element. Each entity element can override the entity-command in defaults by specifying its own entity-command. The content model of the entity-commands and child elements is given in See The jbosscmp-jdbc.xml entity-command element model.
The following are the current entity-command definitions found in the standardjbosscmp-jdbc.xml descriptor:
An example configuration using the hsqldb-fetch-key entity-command with the generated key mapped to a known primary key cmp-field is shown in See A sample autogenerated key config for a known pk cmp-field..
<ejb-name>LocationEJB</ejb-name>
<pk-constraint>false</pk-constraint>
<table-name>location</table-name>
<field-name>locationID</field-name>
<entity-command name="hsqldb-fetch-key"/>
An alternate example using an unknown primary key without an explicit cmp-field is shown in See A sample autogenerated key config for an uknown-pk..
<ejb-name>LocationEJB</ejb-name>
<pk-constraint>false</pk-constraint>
<table-name>location</table-name>
<unknown-pk-class>java.lang.Integer</unknown-pk-class>
<field-name>locationID</field-name>
<jdbc-type>INTEGER</jdbc-type>
JBossCMP global defaults are defined in the standardjbosscmp-jdbc.xml file of the server/<server-name>/conf/directory file in the JBoss 3.x distribution. Each application can override the global defaults in the jbosscmp-jdbc.xml file. The default options are contained in a defaults element of the configuration file, and the content model is shown in See The jbosscmp-jdbc/defaults content model..
An example of the defaults section follows:
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>Hypersonic SQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>false</remove-table>
<read-time-out>300000</read-time-out>
<pk-constraint>true</pk-constraint>
<fk-constraint>false</fk-constraint>
<row-locking>false</row-locking>
<preferred-relation-mapping>foreign-key</preferred-relation-mapping>
<eager-load-group>*</eager-load-group>
Each option can apply to entities, relationships, or both, and can be overridden in the specific entity or relationship. A detailed description of each option follows:
JBossCMP includes predefined type-mappings for many databases including: Cloudscape, DB2, DB2/400, Hypersonic SQL, InformixDB, InterBase, MS SQLSERVER, MS SQLSERVER2000, mySQL, Oracle7, Oracle8, Oracle9i, PointBase, PostgreSQL, PostgreSQL 7.2, SapDB, SOLID, and Sybase. If you do not like the supplied mapping, or a mapping is not supplied for your database, you will have to define a new mapping. If you find an error in one of the supplied mappings, or if you create a new mapping for a new database, please consider posting a patch at the JBoss project page on SourceForge.
Customization of a database is done through the type-mapping section of the jbosscmp-jdbc.xml descriptor. The content model for the type-mapping element is given in See The jbosscmp-jdbc type-mapping element content model... The elements are:
If row locking is not supported in select statement this element should be empty. The most common form of row locking is select for update as in: "SELECT ?1 FROM ?2 WHERE ?3 FOR UPDATE".
If a primary key constraint clause is not supported in a create table statement this element should be empty. The most common form of a primary key constraint is: "CONSTRAINT ?1 PRIMARY KEY (?2)"
If the datasource does not support foreign key constraints this element should be empty. The most common form of a foreign key constraint is: "ALTER TABLE ?1 ADD CONSTRAINT ?2 FOREIGN KEY (?3) REFERENCES ?4 (?5)".
A type-mapping is simply a set of mappings between Java class types and database types. A set of type mappings is defined by a set of mapping elements, the content model for which is shown in See The jbosscmp-jdbc mapping element content model...
If JBossCMP cannot find a mapping for a type, it will serialize the object and use the java.lang.Object mapping. The following describes the three child elements of the mapping element:
An example mapping element for a short in Oracle9i is shown in See A sample short mapping for Oracle9i.
User type mappings allow one to map from JDBC column types to custom CMP fields types by specifying an instance of org.jboss.ejb.plugins.cmp.jdbc.Mapper interface, the definition of which is shown in See The org.jboss.ejb.plugins.cmp.jdbc.Mapper interface..
/** This method is called when CMP field is stored.
* @param fieldValue - CMP field value
Object toColumnValue(Object fieldValue);
/** This method is called when CMP field is loaded.
* @param columnValue - loaded column value.
Object toFieldValue(Object columnValue);
A prototypical use case is the mapping of an integer type to its type-safe Java enum instance. The content model of the user-type-mappings element consists of one or more user-type-mapping elements, the content model of which is shown in See The user-type-mapping content model..
1. The term local interface is used to refer to the EJBLocalObject alone, as well as to refer to the EJBLocalObject/ EJBLocalHome combination. Although this is confusing, it is the current usage of the term in the EJB community.
2. Most J2EE servers, including JBoss, can optimize in VM calls over a remote interface by using pass-by-reference semantics.
3. Dependent Value Objects were added in Proposed Final Draft 1 of the EJB 2.0 Specification and subsequently replaced with local interfaces in Proposed Final Draft 2.
4. The requirement that a DVC use the JavaBeans naming convention will be removed in a future release of JBossCMP.
5. This restriction will also be removed in a future release. The current proposal is to allow the value to be retrieved from any no argument method and to be set with any single argument method or with a constructor.
6. The EJB specification does not even allow for relationships between entities in different applications within the same VM
7. This is the first place where the specification is inconsistent. It would be much easier if the specification used the following tags: relationships, relationship, and relationship-name.
8. Note that with foreign key mapping this element can be empty; meaning that there will be not be a foreign key for the current entity. This is required for the many side of a one-to-many relationship, such a Gangster in the Organization-Gangster example.
9. Ignore the "ejbql" suffix; it is not required. Later this query will be implemented using JBossQL and DeclaredSQL, and the suffix is used to separate the different query specifications in the jbosscmp-jdbc.xml file.
10. Currently this is not enforced by JBossCMP, but a future release will enforce this by throwing an exception during deployment.
11. The example "(r.amountPaid * .01) > 300" is presented on page 244 of "Enterprise JavaBeans 3rd Edition" by Richard Monson-Haefel to demonstrate the use of arithmetic operators in a WHERE clause, and is included here to highlight the fact that it is not legal EJB-QL syntax
12. This is a very useful finder because it quickly coverts primary keys into real Entity objects without contacting the database. One drawback is that it can create an Entity object with a primary key that does not exist in the database. If any method is invoked on the bad Entity, a NoSuchEntityException will be thrown. Another drawback is that the resulting entity bean violates the EJB specification in that it implements a finder, and the JBoss EJB verifier will fail the deployment of such an entity unless the StrictVerifier attribute is set to false.
13. The reason for this behavior has to do with the handling of query results inside the JBoss container. Although it appears that the actual entity beans selected are returned when a query is executed, JBoss really only returns the primary keys of the matching entities, and does not load the entity until a method is invoked on it.
14. Normally JBossCMP would also load the contactInfo field, but for the sake of readability, it has been disabled in this example because contact info maps to seven columns. The actual configuration used to disable the default loading of the contactInfo field is presented in Listing 6-12.
15. JBossCMP uses soft references in the read-ahead cache implementation, so data will be cached and then immediately released.
17. In a future version, JBossCMP will be able to keep the current data of a commit option B entity between transactions and validate that the data is still current using last-update optimistic locking. For entities that contain a large amount of data, this will result in a significant performance enhancement.