CHAPTER 11 The CMP Engine

Dain Sundstrom, Scott Stark

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 ).

Getting Started

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.:

The EJB 2.0 DOCTYPE Declaration

<!DOCTYPE ejb-jar PUBLIC

"-//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.

Example Code

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..

 

FIGURE 11-1. The main CMP2 example classes

To build the example code, execute ant with the following arguments:

[nr@toki examples]$ ant -Dchap=cmp2 config

Buildfile: build.xml

...

 

init:

[echo] Using jboss.dist=/tmp/jboss-3.2.3

 

compile:

[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

 

config:

 

prepare:

[mkdir] Created dir: /Users/orb/Desktop/jboss/docs323/examples/output/cmp2

 

build-ejb.jar:

[jar] Building jar: /Users/orb/Desktop/jboss/docs323/examples/output/cmp2/cmp2-ex1.jar

 

config:

[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy

[echo] Waiting for 5 seconds for deploy...

[junit] .

[junit] Time: 4.535

 

[junit] OK (1 test)

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">

<priority value="DEBUG"/>

</category>

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"/>

</layout>

</appender>

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"/>

</category>

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.

Tests

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.

Read-ahead

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

Buildfile: build.xml

...

run-example:

 

run-examplereadahead:

[junit] .

[junit] Time: 0.561

 

[junit] OK (1 test)

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:

########################################################

### read-ahead none

###

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 Structure

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.

 

FIGURE 11-2. The jbosscmp-jdbc top level content model.
  • defaults: The defaults section allows for the specification of default behavior/settings for behavior that controls entity beans. Use of this section simplifies the amount of information needed for the common behaviors found in the entity beans section. See Defaults for the details of the defaults content.
  • enterprise-beans: The enterprise-beans section allows for customization of entity beans defined in the ejb-jar.xml enterprise-beans descriptor. This is described in detail in Entity Beans.
  • relationships: The relationships element allows for the customization of tables and the loading behavior of entity relationships. This is described in detail in Container Managed Relationships.
  • dependent-value-classes: The dependent-value-classes element allows for the customization of the mapping of dependent value classes to tables. This is described in detail in Dependent Value Classes (DVCs).
  • type-mappings: The type-mappings element defines the Java to SQL type mappings for a database, along with SQL templates, and function mappings. This is described in detail in Datasource Customization.
  • entity-commands: The entity-commands element allows for the definition of the entity creation command instances that know how to create an entity instance in a persistent store. This is described in detail in Entity Commands and Primary Key Generation.
  • user-type-mappings: The user-type-mappings elements defines a mapping of a user types to a column using a mapper class. A mapper is like a mediator: when storing, it takes an instance of the user type and translates it to a column value; when loading, it takes a column value and translates it to an instance of the user type. Details of the user type mappings are described in User Type Mappings.
  • reserved-words: The reserved-words element defines one or more reserved words that should be escaped when generating tables. Each reserved word is specified as the content of a word element.

DTD for jbosscmp-jdbc.xml

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

"-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN"

"http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd">

Entity Beans

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:

Entity Local Home Interface

// Gangster Local Home Interface

public interface GangsterHome extends EJBLocalHome

{

Gangster create(Integer id, String name, String nickName)

throws CreateException;

Gangster findByPrimaryKey(Integer id) throws FinderException;

}

Entity Local Interface

// Gangster Local Interface

public interface Gangster extends EJBLocalObject

{

Integer getGangsterId();

String getName();

String getNickName();

void setNickName(String nickName);

}

Entity Implementation Class

// Gangster Implementation Class

public abstract class GangsterBean implements EntityBean

{

private EntityContext ctx;

private Category log = Category.getInstance(getClass());

 

public Integer ejbCreate(Integer id, String name, String nickName)

throws CreateException

{

log.info("Creating Gangster " + id + " '" + nickName + "' "+ name);

setGangsterId(id);

setName(name);

setNickName(nickName);

return null;

}

public void ejbPostCreate(Integer id, String name,

String nickName)

{}

// 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)

{ ctx = context; }

public void unsetEntityContext() { ctx = null; }

public void ejbActivate() { }

public void ejbPassivate() { }

public void ejbRemove() { log.info("Removing " + getName()); }

public void ejbStore() { }

public void ejbLoad() {}

}

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..

The ejb-jar.xml Entity Declaration

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ejb-jar PUBLIC

"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"

"http://java.sun.com/dtd/ejb-jar_2_0.dtd">

 

<ejb-jar>

<display-name>CMP 2.0 Lab Jar</display-name>

 

<enterprise-beans>

 

<entity>

<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>

<reentrant>False</reentrant>

<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>

...

</entity>

</enterprise-beans>

</ejb-jar>

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.

Entity Mapping

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..

A sample jbosscmp-jdbc.xml Entity Mapping

<?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">

 

<jbosscmp-jdbc>

...

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<table-name>gangster</table-name>

 

<!-- CMP Fields (see CMP-Fields) -->

<!-- Load Groups (see Load Groups)-->

<!-- Queries (see Queries) -->

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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.

 

FIGURE 11-3. The entity element content model

A detailed description of each entity element follows:

  • ejb-name: This required element is the name of the EJB to which this configuration applies. This element must match an ejb-name of an entity in the ejb-jar.xml file.
  • datasource: This optional element is the jndi-name used to look up the datasource. All database connections used by an entity or relation-table are obtained from the datasource. Having different datasources for entities is not recommended, as it vastly constrains the domain over which finders and ejbSelects can query. The default is java:/DefaultDS unless overridden in the defaults section.
  • datasource-mapping: This optional element specifies the name of the type-mapping, which determines how Java types are mapped to SQL types, and how EJB-QL functions are mapped to database specific functions. Type mappings are discussed in Type Mapping. The default is "Hypersonic SQL" unless overridden in the defaults section.
  • create-table: This optional element when true, specifies that JBossCMP should attempt to create a table for the entity. When the application is deployed, JBossCMP checks if a table already exists before creating the table. If a table is found, it is logged, and the table is not created. This option is very useful during the early stages of development when the table structure changes often. The default is false unless overridden in the defaults section.
  • remove-table: This optional element when true, JBossCMP will attempt to drop the table for each entity and each relation table mapped relationship. When the application is undeployed, JBossCMP will attempt to drop the table. This option is very useful during the early stages of development when the table structure changes often. The default is false unless overridden in the defaults section.
  • post-table-create: This optional element specifies an arbitrary SQL statement that should be executed immediately after the database table is created. This command is only executed if create-table is true and the table did not previously exist.
  • read-only: This optional element when true specifies that the bean provider will not be allowed to change the value of any fields. A field that is read-only will not be stored in, or inserted into, the database. If a primary key field is read-only, the create method will throw a CreateException. If a set accessor is called on 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. The read-only option can be overridden on a per cmp-field basis, and is discussed in Read-only Fields. The default is false unless overridden in the defaults section.
  • read-time-out: This optional element is the amount of time in milliseconds that a read on a read-only field is valid. A value of 0 means that the value is always reloaded at the start of a transaction, and a value of -1 means that the value never times out. This option can also be overridden on a per cmp-field basis. If read-only is false, this value is ignored. The default is -1 unless overridden in the defaults section.
  • row-locking: This optional element if true specifies that JBossCMP will lock all rows loaded in a transaction. Most databases implement this by using the SELECT FOR UPDATE syntax when loading the entity, but the actual syntax is determined by the row-locking-template in the datasource-mapping used by this entity. The default is false unless overridden in the defaults section.
  • pk-constraint: This optional element if true specifies that JBossCMP will add a primary key constraint when creating tables. The default is true unless overridden in the defaults section.
  • read-ahead: This optional element controls caching of query results and cmr-fields for the entity. This option is discussed in Read-ahead.
  • list-cache-max: This optional element specifies the number of read-lists that can be tracked by this entity. This option is discussed in on-load. The default is 1000 unless overridden in the defaults section.
  • fetch-size: This optional element specifies the number of entities to read in one round-trip to the underlying datastore. The default is 0 unless overridden in the defaults section.
  • table-name: This optional element is the name of the table that will hold data for this entity. Each entity instance will be stored in one row of this table. The default is the ejb-name.
  • cmp-field: The optional element allows one to define how the ejb-jar.xml cmp-field is mapped onto the persistence store. This is discussed in CMP-Fields.
  • load-groups: This optional element specifies one or more groupings of cmp-fields to declare load groupings of fields. This is discussed in Load Groups.
  • eager-load-groups: This optional element defines one or more load grouping as eager load groups. This is discussed in Eager-loading Process.
  • lazy-load-groups: This optional element defines one or more load grouping as lazy load groups. This is discussed in Lazy-loading Process.
  • query: This optional element specifies the definition of finders and selectors. This is discussed in Queries.
  • unknown-pk: This optional element allows one to define how an unknown primary key type of java.lang.Object maps to the persistent store.
  • entity-command: This optional element allows one to define the entity creation command instance. Typically this is used to define a custom command instance to allow for primary key generation. This is described in detail in Entity Commands and Primary Key Generation.
  • optimistic-locking: This optional element defines the strategy to use for optimistic locking. This is described in detail in Optimistic Locking.
  • audit: This optional element defines the cmp fields that will be audited. This is described in detail in Auditng Entity Access.

CMP-Fields

CMP-Field Abstract Accessors

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:

Sample cmp-field abstract accessor declaration

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.

CMP-Field Declaration

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 ejb-jar.xml cmp-field Declaration

<ejb-jar>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-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>

</entity>

</enterprise-beans>

</ejb-jar>

CMP-Field Column Mapping

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...

 

FIGURE 11-4. 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.:

The jbosscmp-jdbc.xml cmp-field Mapping

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<table-name>gangster</table-name>

 

<cmp-field>

<field-name>gangsterId</field-name>

<column-name>id</column-name>

</cmp-field>

<cmp-field>

<field-name>name</field-name>

<column-name>name</column-name>

<not-null/>

</cmp-field>

<cmp-field>

<field-name>nickName</field-name>

<column-name>nick_name</column-name>

<jdbc-type>VARCHAR</jdbc-type>

<sql-type>VARCHAR(64)</sql-type>

</cmp-field>

<cmp-field>

<field-name>badness</field-name>

<column-name>badness</column-name>

</cmp-field>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

In the cmp-field element, you can control the name and datatype of the column. A detailed description of each element follows:

  • field-name This required element is the name of the cmp-field that is being configured. It must match the field-name element of a cmp-field declared for this entity in the ejb-jar.xml file.
  • column-name This optional element is the name of the column to which the cmp-field is mapped. The default is to use the field-name value.
  • not-null This optional element indicates that JBossCMP should add a NOT NULL to the end of the column declaration when automatically creating the table for this entity. The default for primary key fields and primitives not null.
  • jdbc-type This is the JDBC type that is used when setting parameters in a JDBC PreparedStatement or loading data from a JDBC ResultSet. The valid types are defined in java.sql.Types. Only required if sql-type is specified, default is based on datasourcemapping
  • sql-type This is the SQL type that is used in create table statements for this field. Valid sql-types are only limited by your database vendor. Only required if jdbc-type is specified, default is based on datasourcemapping
  • property This optional element allows one to define how the properties of a dependent value class cmp-field should be mapped to the persistent store. This is discussed further in Dependent Value Classes (DVCs).
  • auto-increment The presence of this optional field indicates that it is auto-incremented by the database layer. This is used to map a field to a generated column as well as an externally manipulated column.
  • dbindex: The presence of this optional field indicates that the server should create an index on the corresponding column in the database, and the index name will be fieldname_index.

Read-only Fields

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:

A sample jbosscmp-jdbc.xml cmp-field read-only declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<cmp-field>

<field-name>lastUpdated</field-name>

<read-only>true</read-only>

<read-time-out>1000</read-time-out>

</cmp-field>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

Auditng Entity Access

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..

 

FIGURE 11-5. The jbosscmp-jdbc.xml audit element content model
  • created-by This optional element indicates that the caller who created the entity should be saved to either the indicated column-name or cmp field-name.
  • created-time This optional element indicates that the time of entity creation should be saved to either the indicated column-name or cmp field-name.
  • updated-by This optional element indicates that the caller who last modified the entity should be saved to either the indicated column-name or cmp field-name.
  • updated-time This optional element indicates that the last time of entity modification should be saved to either the indicated column-name or cmp field-name.
  • */field-name This element indicates that the corresponding audit information should be stored in the indicated cmp-field of the accessed entity bean. Note that there does not have to be an actual cmp field match in the entity. In case there are matching field names, you will be able to access audit fields in the application using the corresponding cmp field abstract getters and setters. Otherwise, audit fields will be created and added to entity internally, and you will be able to access audit information in EJB-QL queries using the audit field names, but not directly through the entity accessors.
  • */column-name This element indicates that the corresponding audit information should be stored in the indicated column of the entity table. If JBossCMP is creating the table the jdbc-type and sql-type element can be used to define the storage type.
A sample audit element declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>AuditChangedNamesEJB</ejb-name>

<table-name>cmp2_audit_changednames</table-name>

<audit>

<created-by>

<column-name>createdby</column-name>

</created-by>

<created-time>

<column-name>createdtime</column-name>

</created-time>

<updated-by>

<column-name>updatedby</column-name></updated-by>

<updated-time>

<column-name>updatedtime</column-name>

</updated-time>

</audit>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

Dependent Value Classes (DVCs)

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.

 

FIGURE 11-6. The jbosscmp-jdbc dependent-value-classes element model.

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.:

The jbosscmp-jdbc.xml Dependent Value Class Declaration

<jbosscmp-jdbc>

<dependent-value-classes>

<dependent-value-class>

<description>A phone number</description>

<class>org.jboss.cmp2.crimeportal.PhoneNumber</class>

<property>

<property-name>areaCode</property-name>

<column-name>area_code</column-name>

</property>

<property>

<property-name>exchange</property-name>

<column-name>exchange</column-name>

</property>

<property>

<property-name>extension</property-name>

<column-name>extension</column-name>

</property>

</dependent-value-class>

 

<dependent-value-class>

<description>General contact info</description>

<class>org.jboss.cmp2.crimeportal.ContactInfo</class>

<property>

<property-name>cell</property-name>

<column-name>cell</column-name>

</property>

<property>

<property-name>pager</property-name>

<column-name>pager</column-name>

</property>

<property>

<property-name>email</property-name>

<column-name>email</column-name>

<jdbc-type>VARCHAR</jdbc-type>

<sql-type>VARCHAR(128)</sql-type>

</property>

</dependent-value-class>

</dependent-value-classes>

</jbosscmp-jdbc>

 

public class PhoneNumber implements Serializable {

/** The first three digits of the phone number. */

private short areaCode;

 

/** The middle three digits of the phone number. */

private short exchange;

 

/** The last four digits of the phone number. */

private short extension;

...

}

 

public class ContactInfo implements Serializable {

/** The cell phone number. */

private PhoneNumber cell;

 

/** The pager number. */

private PhoneNumber pager;

 

/** The email address */

private String email;

...

}

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:

Generated Column Names for ContactInfo Dependent Value Class

info_cell_area_code

info_cell_exchange

info_cell_extension

info_pager_area_code

info_pager_exchange

info_pager_extension

info_email

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:

The jbosscmp-jdbc.xml cmp-field Dependent Value Class Property Override

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<cmp-field>

<field-name>contactInfo</field-name>

<property>

<property-name>cell.areaCode</property-name>

<column-name>cell_area</column-name>

</property>

<property>

<property-name>cell.exchange</property-name>

<column-name>cell_exch</column-name>

</property>

<property>

<property-name>cell.extension</property-name>

<column-name>cell_ext</column-name>

</property>

<property>

<property-name>pager.areaCode</property-name>

<column-name>page_area</column-name>

</property>

<property>

<property-name>pager.exchange</property-name>

<column-name>page_exch</column-name>

</property>

<property>

<property-name>pager.extension</property-name>

<column-name>page_ext</column-name>

</property>

 

<property>

<property-name>email</property-name>

<column-name>email</column-name>

<jdbc-type>VARCHAR</jdbc-type>

<sql-type>VARCHAR(128)</sql-type>

</property>

</cmp-field>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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

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

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:

Collection Valued cmr-field Abstract Accessor Declaration

public abstract class OrganizationBean implements EntityBean {

public abstract Set getMemberGangsters();

public abstract void setMemberGangsters(Set gangsters);

}

Second, add the following to the GangsterBean class:

Single Valued cmr-field Abstract Accessor Declaration

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.

Relationship Declaration

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:

The ejb-jar.xml Relationship Declaration

<ejb-jar>

<relationships>

<ejb-relation>

<ejb-relation-name>Organization-Gangster</ejb-relation-name>

 

<ejb-relationship-role>

<ejb-relationship-role-name>org-has-gangsters

</ejb-relationship-role-name>

 

<multiplicity>One</multiplicity>

 

<relationship-role-source>

<ejb-name>OrganizationEJB</ejb-name>

</relationship-role-source>

 

<cmr-field>

<cmr-field-name>memberGangsters</cmr-field-name>

<cmr-field-type>java.util.Set</cmr-field-type>

</cmr-field>

</ejb-relationship-role>

 

<ejb-relationship-role>

<ejb-relationship-role-name>gangster-belongs-to-org

</ejb-relationship-role-name>

 

<multiplicity>Many</multiplicity>

<cascade-delete/>

 

<relationship-role-source>

<ejb-name>GangsterEJB</ejb-name>

</relationship-role-source>

 

<cmr-field>

<cmr-field-name>organization</cmr-field-name>

</cmr-field>

</ejb-relationship-role>

</ejb-relation>

</relationships>

</ejb-jar>

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:

  • ejb-relationshiprole-name This optional element is used to identify the role and match the database mapping the jbosscmp-jdbc.xml file. The name cannot be the same as the related role.
  • multiplicity This required element must be "One" or "Many". Note, as with all XML elements, this element is case-sensitive.
  • cascade-delete When this optional element is present, JBossCMP will delete the child entity when the parent entity is deleted. Cascade deletion is only allowed for a role where the other side of the relationship has a multiplicity of one. The default is to not cascade delete.
  • relationship-role-source/ejb-name This required element gives the name of the entity that has the role.
  • cmr-field/cmr-field-name This is the name of the cmr-field of the entity has one, if entity has a cmrfield abstract accessor.
  • cmr-field/cmr-field-type This is the type of the cmr-field. Must be java.util.Collection or java.util.Set . Only required if cmr-field abstract accessor is collection valued

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.

Relationship Mapping

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..

 

FIGURE 11-7. The jbosscmp-jdbc.xml ejb-relation element content model

The basic template of the relationship mapping declaration for Organization-Gangster follows:

The jbosscmp-jdbc.xml Relationship Mapping Template

<jbosscmp-jdbc>

<relationships>

<ejb-relation>

<ejb-relation-name>Organization-Gangster</ejb-relation-name>

<foreign-key-mapping/>

 

<ejb-relationship-role>

<ejb-relationship-role-name>org-has-gangsters

</ejb-relationship-role-name>

<key-fields>

<key-field>

<field-name>name</field-name>

<column-name>organization</column-name>

</key-field>

</key-fields>

</ejb-relationship-role>

 

<ejb-relationship-role>

<ejb-relationship-role-name>gangster-belongs-to-org

</ejb-relationship-role-name>

<key-fields/>

</ejb-relationship-role>

</ejb-relation>

</relationships>

</jbosscmp-jdbc>

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:

  • ejb-relation This required element gives the name of the relationship that is being configured. This element must match the name of a relationship declared in the ejb-jar.xml file.
  • read-only This optional element if true indicates that the bean provider will not be allowed to change the value of this relationship. A relationship that is read-only will not be stored in, or inserted into, the database. If a set accessor is called on a read-only relationship, it throws an EJBException.
  • read-time-out This optional element gives the amount of time in milliseconds that a read on a read-only relationship is valid. A value of 0 means that the value is always reloaded at the start of a transaction, and a value of -1 means that the value never times out. If read-only is false, this value is ignored.

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.

Relationship Role Mapping

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..

 

FIGURE 11-8. The jbosscmp-jdbc ejb-relationship-role element content model

A detailed description of the main elements follows:

  • ejb-relationship-role-name This required element gives the name of the role to which this configuration applies. This element must match the name of one of the roles declared for this query in the ejb-jar.xml file.
  • fk-constraint This optional element if true indicates that JBossCMP should add a foreign key constraint to the tables. JBossCMP will only add the constraint if both the primary table and the related table were created by JBossCMP during deployment.
  • key-fields This optional element specifies the mapping of the primary key fields of the current entity. This element is only necessary if exact field mapping is desired. Otherwise, the key-fields element must8 contain a key-field element for each primary key field of the current entity. The details of this element are described below.
  • read-ahead: This optional element controls the caching of this relationship. This option is discussed in Relationships.

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..

Sample jboss.xml fragment illustrating insert-after-ejb-post-create

<jboss>

...

<container-configurations>

<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>

</container-configuration>

</container-configurations>

 

</jboss>

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..

 

FIGURE 11-9. The jbosscmp-jdbc key-fields element content model

A detailed description of the elements contained in the key-field element follows:

  • field-name This required element identifies the field to which this mapping applies. This name must match a primary key field of the current entity.
  • column-name Use this element to specify the column name in which this primary key field will be stored. If this is relationship uses foreign-key-mapping, this column will be added to the table for the related entity. If this relationship uses relation-table-mapping, this column is added to the relation-table. This element is not allowed for mapped dependent value class; instead use the property element.
  • jdbc-type This is the JDBC type that is used when setting parameters in a JDBC PreparedStatement or loading data from a JDBC ResultSet. The valid types are defined in java.sql.Types.
  • sql-type This is the SQL type that is used in create table statements for this field. Valid sql-types are only limited by your database vendor.
  • property Use this element for to specify the mapping of a primary key field which is a dependent value class.
  • dbindex: The presence of this optional field indicates that the server should create an index on the corresponding column in the database, and the index name will be fieldname_index.

Foreign Key Mapping

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:

The jbosscmp-jdbc.xml Foreign Key Mapping

<jbosscmp-jdbc>

<relationships>

<ejb-relation>

<ejb-relation-name>Organization-Gangster</ejb-relation-name>

<foreign-key-mapping/>

 

<ejb-relationship-role>

<ejb-relationship-role-name>org-has-gangsters

</ejb-relationship-role-name>

<key-fields>

<key-field>

<field-name>name</field-name>

<column-name>organization</column-name>

</key-field>

</key-fields>

</ejb-relationship-role>

 

<ejb-relationship-role>

<ejb-relationship-role-name>gangster-belongs-to-org

</ejb-relationship-role-name>

<key-fields/>

</ejb-relationship-role>

</ejb-relation>

</relationships>

</jbosscmp-jdbc>

Relation-table Mapping

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..

 

FIGURE 11-10. 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:

The jbosscmp-jdbc.xml Relation-table Mapping

<jbosscmp-jdbc>

<relationships>

<ejb-relation>

<ejb-relation-name>Gangster-Jobs</ejb-relation-name>

<relation-table-mapping>

<table-name>gangster_job</table-name>

</relation-table-mapping>

 

<ejb-relationship-role>

<ejb-relationship-role-name>gangster-has-jobs

</ejb-relationship-role-name>

<key-fields>

<key-field>

<field-name>gangsterId</field-name>

<column-name>gangster</column-name>

</key-field>

</key-fields>

</ejb-relationship-role>

 

<ejb-relationship-role>

<ejb-relationship-role-name>job-has-gangsters

</ejb-relationship-role-name>

<key-fields>

<key-field>

<field-name>name</field-name>

<column-name>job</column-name>

</key-field>

</key-fields>

</ejb-relationship-role>

</ejb-relation>

</relationships>

</jbosscmp-jdbc>

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:

  • table-name This optional element gives the name of the table that will hold data for this relationship. The default is based on the entity and cmr-field names.
  • datasource This optional element gives the jndi-name used to look up the datasource. All database connections are obtained from the datasource. Having different datasources for entities is not recommended, as it vastly constrains the domain over which finders and ejbSelects can query.
  • datasourcemapping This optional element allows one to specify the name of the type-mapping to use.
  • create-table This optional element if true indicates JBossCMP should attempt to create a table for the relationship. When the application is deployed, JBossCMP checks if a table already exists before creating the table. If a table is found, it is logged, and the table is not created. This option is very useful during the early stages of development when the table structure changes often.
  • post-table-create: This optional element specifies an arbitrary SQL statement that should be executed immediately after the database table is created. This command is only executed if create-table is true and the table did not previously exist.
  • remove-table This optional element if true indicates JBossCMP should attempt to drop the relation-table when the application is undeployed. This option is very useful during the early stages of development when the table structure changes often.
  • row-locking This optional element if true indicates JBossCMP should lock all rows loaded in a transaction. Most databases implement this by using the SELECT FOR UPDATE syntax when loading the entity, but the actual syntax is determined by the row-locking-template in the datasource-mapping used by this entity.
  • pk-constraint This optional element if true indicates JBossCMP should add a primary key constraint when creating tables.

Queries

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.

Finder and ejbSelect Declaration

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:

Finder Declaration

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:

ejbSelect Declaration

public abstract class GangsterBean implements EntityBean {

public abstract Set ejbSelectBoss_ejbql(String name)

throws FinderException;

}

EJB-QL Declaration

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:

A sample ejb-jar.xml Query Declaration

<ejb-jar>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<query>

<query-method>

<method-name>findBadDudes_ejbql</method-name>

<method-params>

<method-param>int</method-param>

</method-params>

</query-method>

<ejb-ql><![CDATA[

SELECT OBJECT(g)

FROM gangster g

WHERE g.badness > ?1

]]></ejb-ql>

</query>

<query>

<query-method>

<method-name>ejbSelectBoss_ejbql</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</query-method>

<ejb-ql><![CDATA[

SELECT DISTINCT underling.organization.theBoss

FROM gangster underling

WHERE underling.name = ?1 OR underling.nickName = ?1

]]></ejb-ql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

EJB-QL is similar to SQL but has some surprising differences. The following are some important things to note about EJB-QL:

  • EJB-QL is a typed language, meaning that it only allows comparison of like types (i.e., strings can only be compared with strings).
  • In an equals comparison a variable (single valued path) must be on the left hand side. Some examples follow:11

g.hangout.state = 'CA' Legal

'CA' = g.shippingAddress.state NOT Legal

'CA' = 'CA' NOT Legal

(r.amountPaid * .01) > 300 NOT Legal

r.amountPaid > (300 / .01) Legal

  • Parameters use a base 1 index like java.sql.PreparedStatement.
  • Parameters are only allowed on the right hand side of a comparison. For example:

gangster.hangout.state = ?1 Legal

?1 = gangster.hangout.state NOT Legal

Overriding the EJB-QL to SQL Mapping

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..

 

FIGURE 11-11. The jbosscmp-jdbc query element content model
  • description An optional description for the query.
  • query-method This required element specifies the query method that being configured. This must match a query-method declared for this entity in the ejb-jar.xml file.
  • jboss-ql, dynamic-ql, declared-sql These elements are alternate ways to specify the query method and each is discussed in its own section.
  • read-ahead This optional element allows one to optimize the loading of additional fields for use with the entities referenced by the query. This is discussed in detail in Optimized Loading.

JBossQL

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:

The jbosscmp-jdbc.xml JBossQL Override

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<query>

<query-method>

<method-name>findBadDudes_jbossql</method-name>

<method-params>

<method-param>int</method-param>

</method-params>

</query-method>

<jboss-ql><![CDATA[

SELECT OBJECT(g)

FROM gangster g

WHERE g.badness > ?1

ORDER BY g.badness DESC

]]></jboss-ql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

JBossQL SQL Mapping

SELECT t0_g.id

FROM gangster t0_g

WHERE t0_g.badness > ?

ORDER BY t0_g.badness DESC

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.

A sample jboss-ql finder using LIMIT and OFFSET

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<query>

<query-method>

<method-name>findManyJobs_jbossql</method-name>

<method-params>

<method-param>int</method-param>

</method-params>

<method-params>

<method-param>int</method-param>

</method-params>

</query-method>

<jboss-ql><![CDATA[

SELECT OBJECT(j)

FROM jobs j

OFFSET ?1 LIMIT ?2

]]></jboss-ql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

DynamicQL

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:

DynamicQL Example Code

public abstract class GangsterBean implements EntityBean {

public Set ejbHomeSelectInStates(Set states)

throws FinderException

{

// generate JBossQL query

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++)

{

if(i > 0)

{

jbossQl.append(", ");

}

jbossQl.append("?").append(i+1);

}

jbossQl.append(") ORDER BY g.name");

 

// pack arguments into an Object[]

Object[] args = states.toArray(new Object[states.size()]);

 

// call dynamic-ql query

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.:

A sample jbosscmp-jdbc.xml DynamicQL Override

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<query>

<query-method>

<method-name>ejbSelectGeneric</method-name>

<method-params>

<method-param>java.lang.String</method-param>

<method-param>java.lang.Object[]</method-param>

</method-params>

</query-method>

<dynamic-ql/>

</query>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

DeclaredSQL

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...

 

FIGURE 11-12. The jbosscmp-jdbc declared-sql element content model.
  • select Specifies what is to be selected and consists of the following elements:
  • distinct If this empty element is present, JBossCMP will add the DISTINCT keyword to the generated SELECT clause. The default is to use DISTINCT if method returns a java.util.Set
  • ejb-name This is the ejb-name of the entity that will be selected. Only required if the query is for an ejbSelect method.
  • field-name This is the name of the cmp-field that will be selected from the specified entity. The default is to select entire entity.
  • alias This specifies the alias that will be used for the main select table. The default is to use the ejb-name.
  • additional-columns Declares other columns to be selected to satisfy ordering by arbitrary columns with ejbFinders or to facilitate aggregate functions in ejbSelects.
  • from Declares additional SQL to append to the generated from clause.
  • where Declares the where clause for the query.
  • order Declares the order clause for the query.
  • other Declares the other sql that is appended to the end of a query.

See The jbosscmp-jdbc.xml DeclaredSQL Override. provides an example DeclaredSQL declaration and See DeclaredSQL SQL Mapping. the corresponding generated SQL:

The jbosscmp-jdbc.xml DeclaredSQL Override

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<query>

<query-method>

<method-name>findBadDudes_declaredsql</method-name>

<method-params>

<method-param>int</method-param>

</method-params>

</query-method>

<declared-sql>

<where><![CDATA[ badness > {0} ]]></where>

<order><![CDATA[ badness DESC ]]></order>

</declared-sql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

DeclaredSQL SQL Mapping

SELECT id

FROM gangster

WHERE badness > ?

ORDER BY badness DESC

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:

The jbosscmp-jdbc.xml DeclaredSQL Override With From Clause

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

<query>

<query-method>

<method-name>ejbSelectBoss_declaredsql</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</query-method>

<declared-sql>

<select>

<distinct/>

<ejb-name>GangsterEJB</ejb-name>

<alias>boss</alias>

</select>

<from><![CDATA[, gangster g, organization o]]></from>

<where><![CDATA[

(LCASE(g.name) = {0} OR LCASE(g.nick_name) = {0}) AND

g.organization = o.name AND o.the_boss = boss.id

]]></where>

</declared-sql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

The jbosscmp-jdbc.xml DeclaredSQL With From Clause SQL Mapping

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

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:

The jbosscmp-jdbc.xml DeclaredSQL ejbSelect Override

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>OrganizationEJB</ejb-name>

<query>

<query-method>

<method-name>ejbSelectOperatingZipCodes_declaredsql</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</query-method>

<declared-sql>

<select>

<distinct/>

<ejb-name>LocationEJB</ejb-name>

<field-name>zipCode</field-name>

<alias>hangout</alias>

</select>

<from><![CDATA[ , organization o, gangster g ]]></from>

<where><![CDATA[

LCASE(o.name) = {0} AND o.name = g.organization AND

g.hangout = hangout.id

]]></where>

<order><![CDATA[ hangout.zip ]]></order>

</declared-sql>

</query>

</entity>

</enterprise-beans>

</ejb-jar>

The jbosscmp-jdbc.xml DeclaredSQL ejbSelect SQL Mapping

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

Parameters

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:

  • A simple parameter can be of any type except for a known (mapped) DVC or an entity. A simple parameter only contains the argument number, such as {0}. When a simple parameter is set, the JDBC type used to set the parameter is determined by the datasourcemapping for the entity. An unknown DVC is serialized and then set as a parameter. Note that most databases do not support the use of a BLOB value in a WHERE clause.
  • A DVC parameter can be any known (mapped) DVC. A DVC parameter must be dereferenced down to a simple property (one that is not another DVC). For example, if we had a property of type ContactInfo (as declared in Dependent Value Classes (DVCs)), valid parameter declarations would be {0.email} and {0.cell.areaCode} but not {0.cell}. The JDBC type used to set a parameter is based on the class type of the property and the datasourcemapping of the entity. The JDBC type used to set the parameter is the JDBC type that is declared for that property in the dependent-value-class element.
  • An entity parameter can be any entity in the application. An entity parameter must be dereferenced down to a simple primary key field or simple property of a DVC primary key field. For example, if we had a parameter of type Gangster, a valid parameter declaration would be {0.gangsterId}. If we had some entity with a primary key field named info of type ContactInfo (as declared in Chapter 3), a valid parameter declaration would be {0.info.cell.areaCode}. Only fields that are members of the primary key of the entity can be dereferenced (this restriction may be removed in later versions). The JDBC type used to set the parameter is the JDBC type that is declared for that field in the entitydeclaration.

BMP Custom Finders

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

Custom Finder Example Code

public abstract class GangsterBean implements EntityBean {

public Collection ejbFindByPrimaryKeys(Collection keys)

{

return keys;

}

}

Optimized Loading

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.

Loading Scenario

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:

Loading Scenario Example Code
  1. public String createGangsterHtmlTable_none() throws FinderException {
  2. StringBuffer table = new StringBuffer();
  3. table.append("<table>");
  4. Collection gangsters = gangsterHome.findAll_none();
  5. for(Iterator iter = gangsters.iterator(); iter.hasNext(); )
  6. {
  7. Gangster gangster = (Gangster)iter.next();
  8. table.append("<tr>");
  9. table.append("<td>").append(gangster.getName());
  10. table.append("</td>");
  11. table.append("<td>").append(gangster.getNickName());
  12. table.append("</td>");
  13. table.append("<td>").append(gangster.getBadness());
  14. table.append("</td>");
  15. table.append("</tr>");
  16. }
  17. return table.toString();
  18. }

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:

Unoptimized findAll Query

SELECT t0_g.id

FROM gangster t0_g

ORDER BY t0_g.id ASC

Then at line 8, in order to load the eight Gangsters in the sample database, JBossCMP executes the following eight queries:

Unoptimized Load Queries

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=0)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=1)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=2)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=3)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=4)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=5)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=6)

SELECT name, nick_name, badness, hangout, organization

FROM gangster WHERE (id=7)

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:

 

Unoptimized Query Execution

id

name

nick_name

badness

hangout

organization

0

Yojimbo

Bodyguard

7

0

Yakuza

1

Takeshi

Master

10

1

Yakuza

2

Yuriko

Four finger

4

2

Yakuza

3

Chow

Killer

9

3

Triads

4

Shogi

Lightning

8

4

Triads

5

Valentino

Pizza-Face

4

5

Mafia

6

Toni

Toothless

2

6

Mafia

7

Corleone

Godfather

6

7

Mafia

Load Groups

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.:

The jbosscmp-jdbc.xml Load Group Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<load-groups>

<load-group>

<load-group-name>basic</load-group-name>

<field-name>name</field-name>

<field-name>nickName</field-name>

<field-name>badness</field-name>

</load-group>

<load-group>

<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>

</load-groups>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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.

Read-ahead

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.

on-find

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:

on-find Optimized findAll Query

SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness

FROM gangster t0_g

ORDER BY t0_g.id ASC

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:

 

on-find Optimized Query Execution

id

name

nick_name

badness

hangout

organization

0

Yojimbo

Bodyguard

7

0

Yakuza

1

Takeshi

Master

10

1

Yakuza

2

Yuriko

Four finger

4

2

Yakuza

3

Chow

Killer

9

3

Triads

4

Shogi

Lightning

8

4

Triads

5

Valentino

Pizza-Face

4

5

Mafia

6

Toni

Toothless

2

6

Mafia

7

Corleone

Godfather

6

7

Mafia

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:

The jbosscmp-jdbc.xml on-find Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<query>

<query-method>

<method-name>findAll_onfind</method-name>

<method-params/>

</query-method>

<jboss-ql><![CDATA[

SELECT OBJECT(g)

FROM gangster g

ORDER BY g.gangsterId

]]></jboss-ql>

<read-ahead>

<strategy>on-find</strategy>

<page-size>4</page-size>

<eager-load-group>basic</eager-load-group>

</read-ahead>

</query>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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.

on-load

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.

on-load (Unoptimized) findAll Query

SELECT t0_g.id

FROM gangster t0_g

ORDER BY t0_g.id ASC

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:

on-load Optimized Load Queries

SELECT id, name, nick_name, badness

FROM gangster

WHERE (id=0) OR (id=1) OR (id=2) OR (id=3)

SELECT id, name, nick_name, badness

FROM gangster

WHERE (id=4) OR (id=5) OR (id=6) OR (id=7)

The following table shows the execution of these queries:

 

on-load Optimized Query Execution

id

name

nick_name

badness

hangout

organization

0

Yojimbo

Bodyguard

7

0

Yakuza

1

Takeshi

Master

10

1

Yakuza

2

Yuriko

Four finger

4

2

Yakuza

3

Chow

Killer

9

3

Triads

4

Shogi

Lightning

8

4

Triads

5

Valentino

Pizza-Face

4

5

Mafia

6

Toni

Toothless

2

6

Mafia

7

Corleone

Godfather

6

7

Mafia

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 jbosscmp-jdbc.xml on-load Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<query>

<query-method>

<method-name>findAll_onload</method-name>

<method-params/>

</query-method>

<jboss-ql><![CDATA[

SELECT OBJECT(g)

FROM gangster g

ORDER BY g.gangsterId

]]></jboss-ql>

<read-ahead>

<strategy>on-load</strategy>

<page-size>4</page-size>

<eager-load-group>basic</eager-load-group>

</read-ahead>

</query>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

none

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.:

The jbosscmp-jdbc.xml none Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<query>

<query-method>

<method-name>findAll_none</method-name>

<method-params/>

</query-method>

<jboss-ql><![CDATA[

SELECT OBJECT(g)

FROM gangster g

ORDER BY g.gangsterId

]]></jboss-ql>

<read-ahead>

<strategy>none</strategy>

</read-ahead>

</query>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

Loading Process

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.

Commit Options

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:

  • A: JBossCMP assumes it is the sole user of the database; therefore, JBossCMP can cache the current value of an entity between transactions, which can result is substantial performance gains. As a result of this assumption, no data managed by JBossCMP can be changed outside of JBossCMP. For example, changing data in another program or with the use of direct JDBC (even within JBoss) will result in an inconsistent database state.
  • B: JBossCMP assumes that there is more than one user of the database but keeps the context information about entities between transactions. This context information is used for optimizing loading of the entity. 20 This is the default commit option.
  • C: JBossCMP discards all entity context information at the end of the transaction.
  • D: This is a JBoss specific commit option. This option is similar to commit option A, except that the data only remains valid for a specified amount of time.

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:

The jboss.xml Commit Option Declaration

<jboss>

<container-configurations>

<container-configuration>

<container-name>Standard CMP 2.x EntityBean</container-name>

<commit-option>A</commit-option>

</container-configuration>

</container-configurations>

</jboss>

Eager-loading Process

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:

The jbosscmp-jdbc.xml Eager Load Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<load-groups>

<load-group>

<load-group-name>most</load-group-name>

<field-name>name</field-name>

<field-name>nickName</field-name>

<field-name>badness</field-name>

<field-name>hangout</field-name>

<field-name>organization</field-name>

</load-group>

</load-groups>

<eager-load-group>most</eager-load-group>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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:

  1. If the entity context is still valid, no loading is necessary, and therefore the loading process is done. The entity context will be valid when using commit option A, or when using commit option D, and the data has not timed out.
  2. Any residual data in the entity context is flushed. This assures that old data does not bleed into the new load.
  3. The primary key value is injected back into the primary key fields. The primary key object is actually independent of the fields and needs to be reloaded after the flush in step 2.
  4. All data in the preload cache for this entity is loaded into the fields.
  5. JBossCMP determines the additional fields that still need to be loaded. Normally the fields to load are determined by the eager-load group of the entity, but can be overridden if the entity was located using a query or cmr-field with an on-find or on-load read-ahead strategy. If all of the fields have already been loaded, the load process skips to step 7.
  6. A query is executed to select the necessary column. If this entity is using the on-load strategy, a page of data is loaded as described in the on-load section. The data for the current entity is stored in the context and the data for the other entities is stored in the preload cache.
  7. The ejbLoad method of the entity is called.

Lazy-loading Process

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.:

The jbosscmp-jdbc.xml Lazy Load Group Declaration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>GangsterEJB</ejb-name>

...

<load-groups>

<load-group>

<load-group-name>basic</load-group-name>

<field-name>name</field-name>

<field-name>nickName</field-name>

<field-name>badness</field-name>

</load-group>

<load-group>

<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>

</load-groups>

...

<lazy-load-groups>

<load-group-name>basic</load-group-name>

<load-group-name>contact info</load-group-name>

</lazy-load-groups>

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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:

  1. All data in the preload cache for this entity is loaded into the fields.
  2. If the field value was loaded by the preload cache the lazy-load process is finished.
  3. JBossCMP finds all of the lazy load groups that contain this field, performs a set join on the groups, and removes any field that has already been loaded.
  4. A query is executed to select the necessary column. As in the basic load process, JBossCMP may load a block of entities. The data for the current entity is stored in the context and the data for the other entities is stored in the preload cache.
Relationships

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:

Relationship Lazy Loading Example Code
  1. public String createGangsterHangoutHtmlTable() throws FinderException
  2. {
  3. StringBuffer table = new StringBuffer();
  4. table.append("<table>");
  5. Collection gangsters = gangsterHome.findAll_onfind();
  6. for(Iterator iter = gangsters.iterator(); iter.hasNext(); )
  7. {
  8. Gangster gangster = (Gangster)iter.next();
  9. Location hangout = gangster.getHangout();
  10. table.append("<tr>");
  11. table.append("<td>").append(gangster.getName());
  12. table.append("</td>");
  13. table.append("<td>").append(gangster.getNickName());
  14. table.append("</td>");
  15. table.append("<td>").append(gangster.getBadness());
  16. table.append("</td>");
  17. table.append("<td>").append(hangout.getCity());
  18. table.append("</td>");
  19. table.append("<td>").append(hangout.getState());
  20. table.append("</td>");
  21. table.append("<td>").append(hangout.getZipCode());
  22. table.append("</td>");
  23. table.append("</tr>");
  24. }
  25. table.append("</table>");
  26. return table.toString();
  27. }

 

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:

The jbosscmp-jdbc.xml Relationship Lazy Loading Configuration

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>LocationEJB</ejb-name>

<load-groups>

<load-group>

<load-group-name>quick info</load-group-name>

<field-name>city</field-name>

<field-name>state</field-name>

<field-name>zipCode</field-name>

</load-group>

</load-groups>

<eager-load-group/>

</entity>

</enterprise-beans>

 

<relationships>

<ejb-relation>

<ejb-relation-name>Gangster-Hangout</ejb-relation-name>

<foreign-key-mapping/>

 

<ejb-relationship-role>

<ejb-relationship-role-name>gangster-has-a-hangout

</ejb-relationship-role-name>

<key-fields/>

 

<read-ahead>

<strategy>on-find</strategy>

<page-size>4</page-size>

<eager-load-group>quick info</eager-load-group>

</read-ahead>

</ejb-relationship-role>

 

<ejb-relationship-role>

<ejb-relationship-role-name>hangout-for-a-gangster

</ejb-relationship-role-name>

<key-fields>

<key-field>

<field-name>locationID</field-name>

<column-name>hangout</column-name>

</key-field>

</key-fields>

</ejb-relationship-role>

</ejb-relation>

</relationships>

</jbosscmp-jdbc>

At line 25, JBossCMP will execute the following query:

on-find Optimized findAll Query

SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness

FROM gangster t0_g

ORDER BY t0_g.id ASC

Then at line 29, JBossCMP executes the following two queries to load the city, state, and zip fields of the hideout:

on-find Optimized Relationship Load Queries

SELECT gangster.id, gangster.hangout,

location.city, location.st, location.zip

FROM gangster, location

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

FROM gangster, location

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:

 

on-find Optimized Relationship Query Execution

Gangster

Location

id

name

nick_name

badness

hangout

id

city

st

zip

0

Yojimbo

Bodyguard

7

0

0

San Fran

CA

94108

1

Takeshi

Master

10

1

1

San Fran

CA

94133

2

Yuriko

Four finger

4

2

2

San Fran

CA

94133

3

Chow

Killer

9

3

3

San Fran

CA

94133

4

Shogi

Lightning

8

4

4

San Fran

CA

94133

5

Valentino

Pizza-Face

4

5

5

New York

NY

10017

6

Toni

Toothless

2

6

6

Chicago

IL

60661

7

Corleone

Godfather

6

7

7

Las Vegas

NV

 

89109

Transactions

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:

No Transaction Loading Example Code
  1. public String createGangsterHtmlTable_no_tx() throws FinderException
  2. {
  3. StringBuffer table = new StringBuffer();
  4. table.append("<table>");
  5. Collection gangsters = gangsterHome.findFour();
  6. for(Iterator iter = gangsters.iterator(); iter.hasNext(); )
  7. {
  8. Gangster gangster = (Gangster)iter.next();
  9. table.append("<tr>");
  10. table.append("<td>").append(gangster.getName());
  11. table.append("</td>");
  12. table.append("<td>").append(gangster.getNickName());
  13. table.append("</td>");
  14. table.append("<td>").append(gangster.getBadness());
  15. table.append("</td>");
  16. table.append("</tr>");
  17. }
  18.  
  19. table.append("</table>");
  20. return table.toString();
  21. }

The query executed at line 53 is shown in See No Transaction on-find Optimized findAll Query.:

No Transaction on-find Optimized findAll Query

SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness

FROM gangster t0_g

WHERE t0_g.id < 4

ORDER BY t0_g.id ASC

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

No Transaction on-load Optimized Load Queries

SELECT id, name, nick_name, badness

FROM gangster

WHERE (id=0) OR (id=1) OR (id=2) OR (id=3)

SELECT id, name, nick_name, badness

FROM gangster

WHERE (id=1) OR (id=2) OR (id=3)

SELECT id, name, nick_name, badness

FROM gangster

WHERE (id=2) OR (id=3)

SELECT name, nick_name, badness

FROM gangster

WHERE (id=3)

See No Transaction on-find optimized query execution. shows the execution of the queries:

 

FIGURE 11-13. No Transaction on-find optimized query execution

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:

User Transaction Example Code

public String createGangsterHtmlTable_with_tx()

throws FinderException

{

UserTransaction tx = null;

try

{

InitialContext ctx = new InitialContext();

tx = (UserTransaction) ctx.lookup("UserTransaction");

tx.begin();

 

String table = createGangsterHtmlTable_no_tx();

 

if (tx.getStatus() == Status.STATUS_ACTIVE)

{

tx.commit();

}

return table;

}

catch (Exception e)

{

try

{

if (tx != null) tx.rollback();

}

catch (SystemException unused)

{

// eat the exception we are exceptioning out anyway

}

if (e instanceof FinderException)

{

throw (FinderException) e;

}

if (e instanceof RuntimeException)

{

throw (RuntimeException) e;

}

throw new EJBException(e);

}

}

Optimistic Locking

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..

The prototype optimistic locking entity container configuration

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jboss PUBLIC

"-//JBoss//DTD JBOSS 3.2//EN"

"http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">

 

<jboss>

<enterprise-beans>

<entity>

<ejb-name>EntityGroupLocking</ejb-name>

<local-jndi-name>local/EntityGroupLocking</local-jndi-name>

<configuration-name>Optimistic Container</configuration-name>

</entity>

</enterprise-beans>

...

<container-configurations>

<container-configuration

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

</locking-policy>

</container-configuration>

</container-configurations>

</jboss>

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.

 

FIGURE 11-14. The jbosscmp-jdbc optimistic-locking element content model
  • group-name: This element specifies that optimistic locking is based on the fields of a load-group. This value of this element must match one of the entity's load-group-name. The fields in this group will be used for optimistic locking.
  • modified-strategy: This element specifies that optimistic locking is based on the modified fields. This strategy implies that the fields that were modified during transaction will be used for optimistic locking.
  • read-strategy: This element specifies that optimistic locking is based on the fields read. This strategy implies that the fields that were read/changed in the transaction will be used for optimistic locking.
  • version-column: This element specifies that optimistic locking is based on a version column strategy. Specifying this element will add an additional version field of type java.lang.Long to the entity bean for optimistic locking. Each update of the entity will increase the value of this field. The field-name element allows for the specification of the name of the cmp field while the column-name element allows for the specification of the corresponding table column.
  • timestamp-column: This element specifies that optimistic locking is based on a timestamp column strategy. Specifying this element will add an additional version field of type java.util.Date to the entity bean for optimistic locking. Each update of the entity will set the value of this field to the current time. The field-name element allows for the specification of the name of the cmp field while the column-name element allows for the specification of the corresponding table column.
  • key-generator-factory: This element specifies that optimistic locking is based on key generation. The value of the element is the JNDI name of a org.jboss.ejb.plugins.keygenerator.KeyGeneratorFactory implementation. Specifying this element will add an additional version field to the entity bean for optimistic locking. The type of the field must be specified via the field-type element. Each update of the entity will update the key field by obtaining a new value from the key generator. The field-name element allows for the specification of the name of the cmp field while the column-name element allows for the specification of the corresponding table column.

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..

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">

 

<jbosscmp-jdbc>

<defaults>

<datasource>java:/DefaultDS</datasource>

<datasource-mapping>Hypersonic SQL</datasource-mapping>

</defaults>

 

<enterprise-beans>

 

<entity>

<ejb-name>EntityGroupLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entitygrouplocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<load-groups>

<load-group>

<load-group-name>string</load-group-name>

<field-name>stringField</field-name>

</load-group>

<load-group>

<load-group-name>all</load-group-name>

<field-name>stringField</field-name>

<field-name>dateField</field-name>

</load-group>

</load-groups>

 

<optimistic-locking>

<group-name>string</group-name>

</optimistic-locking>

</entity>

 

<entity>

<ejb-name>EntityModifiedLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entitymodifiedlocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<optimistic-locking>

<modified-strategy/>

</optimistic-locking>

</entity>

 

<entity>

<ejb-name>EntityReadLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entityreadlocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<optimistic-locking>

<read-strategy/>

</optimistic-locking>

</entity>

 

<entity>

<ejb-name>EntityVersionLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entityversionlocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<optimistic-locking>

<version-column/>

<field-name>versionField</field-name>

<column-name>ol_version</column-name>

<jdbc-type>INTEGER</jdbc-type>

<sql-type>INTEGER(5)</sql-type>

</optimistic-locking>

</entity>

 

<entity>

<ejb-name>EntityTimestampLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entitytimestamplocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<optimistic-locking>

<timestamp-column/>

<field-name>versionField</field-name>

<column-name>ol_timestamp</column-name>

<jdbc-type>TIMESTAMP</jdbc-type>

<sql-type>DATETIME</sql-type>

</optimistic-locking>

</entity>

 

<entity>

<ejb-name>EntityKeyGeneratorLocking</ejb-name>

<create-table>true</create-table>

<remove-table>true</remove-table>

<table-name>entitykeygenlocking</table-name>

<cmp-field>

<field-name>dateField</field-name>

</cmp-field>

<cmp-field>

<field-name>integerField</field-name>

</cmp-field>

<cmp-field>

<field-name>stringField</field-name>

</cmp-field>

 

<optimistic-locking>

<key-generator-factory>UUIDKeyGeneratorFactory

</key-generator-factory>

<field-type>java.lang.String</field-type>

<field-name>uuidField</field-name>

<column-name>ol_uuid</column-name>

<jdbc-type>VARCHAR</jdbc-type>

<sql-type>VARCHAR(32)</sql-type>

</optimistic-locking>

</entity>

 

</enterprise-beans>

 

</jbosscmp-jdbc>

Entity Commands and Primary Key Generation

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.

 

FIGURE 11-15. The jbosscmp-jdbc.xml entity-command element model
  • entity-command: Each entity-command element specifies an entity generation implementation.
  • entity-command/name: The name attribute specifies a name that allows the command defined in an entity-commands section to be referenced in the defaults and entity elements.
  • entity-command/class: The class attribute specifies the implementation of the org.jboss.ejb.plugins.cmp.jdbc. JDBCCreateEntityCommand that supports the key generation. Database vendor specific commands typically subclass the org.jboss.ejb.plugins.cmp.jdbc. JDBCIdentityColumnCreateCommand if the database generates the primary key as a side effect of doing an insert, or the org.jboss.ejb.plugins.cmp.jdbc.JDBCInsertPKCreateCommand if the command must insert the generated key.
  • entity-command/attribute: The optional attribute element(s) allows for the specification of arbitrary name/value property paris that will be available to the entity command implementation class. The attribute element has a required name attribute that specifies the name property, and the attribute element content is the value of the property. The attribute values are accessible through the org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityCommandMetaData.getAttribute(String) method.

Existing Entity Commands

The following are the current entity-command definitions found in the standardjbosscmp-jdbc.xml descriptor:

  • name="default" class="org.jboss.ejb.plugins.cmp.jdbc.JDBCCreateEntityCommand". The JDBCCreateEntityCommand is the default entity creation as it is the entity-command referenced in the standardjbosscmp-jdbc.xml defaults element. This entity-command executes an "INSERT INTO" query using the assigned primary key value.
  • name="no-select-before-insert" class="org.jboss.ejb.plugins.cmp.jdbc.JDBCCreateEntityCommand" This is a variation on "default" that skips select before insert by specifying an attribute name="SQLExceptionProcessor" that points to the jboss.jdbc:service=SQLExceptionProcessor service. The SQLExceptionProcessor service provides a boolean isDuplicateKey(SQLException e) operation that allows a for determination of any unique constraint violation.
  • name="pk-sql" class="org.jboss.ejb.plugins.cmp.jdbc.JDBCPkSqlCreateCommand" The JDBCPkSqlCreateCommand executes an "INSERT INTO" query statement provided by the pk-sql attribute to obtain the next primary key value. Its primary target usage are databases with sequence support.
  • name="mysql-get-generated-keys" class="org.jboss.ejb.plugins.cmp.jdbc.mysql.JDBCMySQLCreateCommand" The JDBCMySQLCreateCommand executes an "INSERT INTO" query using the getGeneratedKeys method from MySQL native java.sql.Statement interface implementation to fetch the generated key. It works under JDK versions 1.3 and 1.4.
  • name="oracle-sequence" class="org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCOracleCreateCommand" The JDBCOracleCreateCommand Create command for use with Oracle that uses a sequence in conjuction with a RETURNING clause to generate keys in a single statement. It has a required sequence element that specifies the name of the sequence column.
  • name="hsqldb-fetch-key" class="org.jboss.ejb.plugins.cmp.jdbc.hsqldb.JDBCHsqldbCreateCommand" The JDBCHsqldbCreateCommand executes an "INSERT INTO" query after executing a "CALL IDENTITY()" statement to fetch the generated key. It works under JDK versions 1.3 and 1.4.
  • name="sybase-fetch-key" class="org.jboss.ejb.plugins.cmp.jdbc.sybase.JDBCSybaseCreateCommand" The JDBCSybaseCreateCommand executes an "INSERT INTO" query after executing a "SELECT @@IDENTITY" statement to fetch the generated key. It works under JDK versions 1.3 and 1.4.
  • name="mssql-fetch-key" class="org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCSQLServerCreateCommand" The JDBCSQLServerCreateCommand for Microsoft SQL Server that uses the value from an IDENTITY columns. By default uses "SELECT SCOPE_IDENTITY()" to reduce the impact of triggers; can be overridden with "pk-sql" attribute e.g. for V7.
  • name="informix-serial" class="org.jboss.ejb.plugins.cmp.jdbc.informix.JDBCInformixCreateCommand" The JDBCInformixCreateCommand executes an "INSERT INTO" query after using the getSerial method from Informix native java.sql.Statement interface implementation to fetch the generated key. It works under JDK versions 1.3 and 1.4.
  • name="postgresql-fetch-seq" class="org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCPostgreSQLCreateCommand" The JDBCPostgreSQLCreateCommand for PostgreSQL that fetches the currval of the sequence. The optional sequence attribute can be used to change the name of the sequence, with thte default being table_pkColumn_seq.
  • name="key-generator" class="org.jboss.ejb.plugins.cmp.jdbc.JDBCKeyGeneratorCreateCommand" The JDBCKeyGeneratorCreateCommand executes an "INSERT INTO" query after obtaining a value for the primary key from the key generator referenced by the key-generator-factory. The key-generator-factory attribute must provide the name of a JNDI binding of the org.jboss.ejb.plugins.keygenerator.KeyGeneratorFactory implementation. It works under JDK versions 1.3 and 1.4.
  • name="get-generated-keys" class="org.jboss.ejb.plugins.cmp.jdbc.jdbc3.JDBCGetGeneratedKeysCreateCommand". The JDBCGetGeneratedKeysCreateCommand executes an "INSERT INTO" query using a statement built using the JDBC3 prepareStatement(String, Statement.RETURN_GENERATED_KEYS) that has the capability to retrieve the auto-generated key. The generated key is obtained by calling the PreparedStatement.getGeneratedKeys method . Since this requires JDBC3 support it is only available in JDK1.4.1+ with a supporting JDBC driver.

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..

A sample autogenerated key config for a known pk cmp-field

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>LocationEJB</ejb-name>

<pk-constraint>false</pk-constraint>

<table-name>location</table-name>

 

<cmp-field>

<field-name>locationID</field-name>

<column-name>id</column-name>

<auto-increment/>

</cmp-field>

...

<entity-command name="hsqldb-fetch-key"/>

 

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

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..

A sample autogenerated key config for an uknown-pk

<jbosscmp-jdbc>

<enterprise-beans>

<entity>

<ejb-name>LocationEJB</ejb-name>

<pk-constraint>false</pk-constraint>

<table-name>location</table-name>

 

<unknown-pk>

<unknown-pk-class>java.lang.Integer</unknown-pk-class>

<field-name>locationID</field-name>

<column-name>id</column-name>

<jdbc-type>INTEGER</jdbc-type>

<sql-type>INTEGER</sql-type>

<auto-increment/>

</unknown-pk>

...

<entity-command name="hsqldb-fetch-key"/>

 

</entity>

</enterprise-beans>

</jbosscmp-jdbc>

Defaults

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..

 

FIGURE 11-16. The jbosscmp-jdbc/defaults content model

An example of the defaults section follows:

<jbosscmp-jdbc>

<defaults>

<datasource>java:/DefaultDS</datasource>

<datasource-mapping>Hypersonic SQL</datasource-mapping>

<create-table>true</create-table>

<remove-table>false</remove-table>

<read-only>false</read-only>

<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>

<read-ahead>

<strategy>on-load</strategy>

<page-size>1000</page-size>

<eager-load-group>*</eager-load-group>

</read-ahead>

<list-cache-max>1000</list-cache-max>

</defaults>

</jbosscmp-jdbc>

A sample jbosscmp-jdbc.xml defaults declaration

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:

  • datasource This optional element is the jndi-name used to look up the datasource. All database connections used by an entity or relation-table are obtained from the datasource. Having different datasources for entities is not recommended, as it vastly constrains the domain over which finders and ejbSelects can query.
  • datasource-mapping This optional element specifies the name of the type-mapping, which determines how Java types are mapped to SQL types, and how EJB-QL functions are mapped to database specific functions. Type mappings are discussed in Type Mapping.
  • create-table This optional element when true, specifies that JBossCMP should attempt to create a table for the entity. When the application is deployed, JBossCMP checks if a table already exists before creating the table. If a table is found, it is logged, and the table is not created. This option is very useful during the early stages of development when the table structure changes often. The default is false.
  • post...
  • remove-table This optional element when true, JBossCMP will attempt to drop the table for each entity and each relation table mapped relationship. When the application is undeployed, JBossCMP will attempt to drop the table. This option is very useful during the early stages of development when the table structure changes often. The default is false.
  • read-only This optional element when true specifies that the bean provider will not be allowed to change the value of any fields. A field that is read-only will not be stored in, or inserted into, the database. If a primary key field is read-only, the create method will throw a CreateException. If a set accessor is called on 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. The read-only option can be overridden on a per cmp-field basis, which is discussed in. The default is false.
  • read-time-out This optional element is the amount of time in milliseconds that a read on a read-only field is valid. A value of 0 means that the value is always reloaded at the start of a transaction, and a value of -1 means that the value never times out. This option can also be overridden on a per cmp-field basis. If read-only is false, this value is ignored. The default is -1.
  • row-locking This optional element if true specifies that JBossCMP will lock all rows loaded in a transaction. Most databases implement this by using the SELECT FOR UPDATE syntax when loading the entity, but the actual syntax is determined by the row-locking-template in the datasource-mapping used by this entity. The default is false.
  • pk-constraint This optional element if true specifies that JBossCMP will add a primary key constraint when creating tables. The default is true.
  • preferred-relation-mapping: This optional element specifies the preferred mapping style for relationships. The preferred-relation-mapping element must be either "foreign-key" or "relation-table".
  • read-ahead This optional element controls caching of query results and cmr-fields for the entity. This option is discussed in Read-ahead.
  • list-cache-max This optional element specifies the number of read-lists that can be tracked by this entity. This option is discussed in on-load. The default is 1000.
  • fetch-size This optional element specifies the number of entities to read in one round-trip to the underlying datastore. The default is 0.
  • unknown-pk This optional element allows one to define the default mapping of an unknown primary key type of java.lang.Object maps to the persistent store.
  • entity-command This optional element allows one to define the default command for entity creation. This is described in detail in Entity Commands and Primary Key Generation.

Datasource Customization

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:

 

FIGURE 11-17. The jbosscmp-jdbc type-mapping element content model.

Function Mapping

 

FIGURE 11-18. The jbosscmp-jdbc function-mapping element content model
  • function-name: This required element gives the EJB-QL function name, e.g., concat, substring.
  • function-sql: This required element gives the SQL for the function as appropriate for the underlying database. Examples for a concat function include: "(?1 || ?2)", "concat(?1, ?2)", "(?1 + ?2)".

 

Type Mapping

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...

 

FIGURE 11-19. 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:

  • java-type: This required element gives the fully qualified name of the Java class to be mapped. If the class is a primitive wrapper class such as java.lang.Short , the mapping also applies to the primitive type.
  • jdbc-type: This required element gives the JDBC type that is used when setting parameters in a JDBC PreparedStatement or loading data from a JDBC ResultSet . The valid types are defined in java.sql.Types .
  • sql-type: This required element gives the SQL type that is used in create table statements. Valid sql-types are only limited by your database vendor.

An example mapping element for a short in Oracle9i is shown in See A sample short mapping for Oracle9i.

A sample short mapping for Oracle9i

<jbosscmp-jdbc>

<type-mappings>

<type-mapping>

<name>Oracle9i</name>

...

<mapping>

<java-type>java.lang.Short</java-type>

<jdbc-type>NUMERIC</jdbc-type>

<sql-type>NUMBER(5)</sql-type>

</mapping>

</type-mapping>

</type-mappings>

</jbosscmp-jdbc>

 

User Type Mappings

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..

The org.jboss.ejb.plugins.cmp.jdbc.Mapper interface

public interface Mapper

{

/** This method is called when CMP field is stored.

* @param fieldValue - CMP field value

* @return column value.

*/

Object toColumnValue(Object fieldValue);

 

/** This method is called when CMP field is loaded.

* @param columnValue - loaded column value.

* @return CMP field 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..

 

FIGURE 11-20. The user-type-mapping content model
  • java-type: the fully qualified name of the cmp field type in the mapping.
  • mapped-type: the fully qualified name of the database type in the mapping.
  • mapper: the fully qualified name of the Mapper interface implementation that handles the conversion between the java-type and mapped-type.

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.

16. This is the read-ahead technique from the legacy JAWS 1.1 CMP engine.

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.

18. It's actually worse than this. JBossCMP executes each of these queries three times; once for each cmp-field that is accessed. This is because the preloaded values are discarded between the cmp-field accessor calls.


© 2002-2004 JBoss Inc. All rights reserved.