Chapter 11. The CMP Engine

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

11.1. Getting Started

JBossCMP is the default persistence manager for EJB 2.0 applications. Because JBossCMP is a core feature of JBoss, no action beyond the basic JBoss installation 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 below.

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

11.1.1. 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 Figure 11.1, “The main CMP2 example classes”.

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
...
config:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 5 seconds for deploy...
    [junit] .
    [junit] Time: 3.474

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

09:54:42,018 INFO  [EjbModule] Deploying OrganizationEJB
09:54:42,399 INFO  [EjbModule] Deploying GangsterEJB
09:54:42,438 INFO  [EjbModule] Deploying JobEJB
09:54:42,468 INFO  [EjbModule] Deploying LocationEJB
09:54:42,507 INFO  [EjbModule] Deploying EJBTestRunnerEJB
09:54:42,587 INFO  [EjbModule] Deploying ReadAheadEJB
09:54:46,300 WARN  [JDBCTypeFactory] Type not mapped: int
09:54:47,223 INFO  [EJBDeployer] Deployed: file:/private/tmp/jboss-3.2.6/server/default/de
ploy/cmp2-ex1.jar
09:54:48,841 INFO  [OrganizationBean$Proxy] Creating organization Yakuza, Japanese Gangste
rs
09:54:48,918 INFO  [OrganizationBean$Proxy] Creating organization Mafia, Italian Bad Guys
09:54:48,925 INFO  [OrganizationBean$Proxy] Creating organization Triads, Kung Fu Movie Ex
tras
09:54:48,931 INFO  [GangsterBean$Proxy] Creating Gangster 0 'Bodyguard' Yojimbo
09:54:49,068 INFO  [GangsterBean$Proxy] Creating Gangster 1 'Master' Takeshi
09:54:49,106 INFO  [GangsterBean$Proxy] Creating Gangster 2 'Four finger' Yuriko
09:54:49,117 INFO  [GangsterBean$Proxy] Creating Gangster 3 'Killer' Chow
09:54:49,133 INFO  [GangsterBean$Proxy] Creating Gangster 4 'Lightning' Shogi
09:54:49,143 INFO  [GangsterBean$Proxy] Creating Gangster 5 'Pizza-Face' Valentino
09:54:49,184 INFO  [GangsterBean$Proxy] Creating Gangster 6 'Toohless' Toni
09:54:49,202 INFO  [GangsterBean$Proxy] Creating Gangster 7 'Godfather' Corleone
09:54:49,215 INFO  [JobBean$Proxy] Creating Job 10th Street Jeweler Heist
09:54:49,224 INFO  [JobBean$Proxy] Creating Job The Greate Train Robbery
09:54:49,258 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 shown 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.

11.1.2. 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 ORGA
NIZATION WHERE name=?
14:03:28,011 DEBUG [OrganizationEJB] Executing SQL: SELECT desc, the_boss FROM ORGANIZATIO
N WHERE (name=?)
14:03:28,020 DEBUG [OrganizationEJB] Executing SQL: SELECT id FROM GANGSTER WHERE (organiz
ation=?)
14:03:28,044 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,052 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,070 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,229 DEBUG [GangsterEJB#findBadDudes_ejbql] Executing SQL: SELECT t0_g.id FROM GAN
GSTER t0_g WHERE (t0_g.badness > ?)
14:03:28,256 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,264 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,270 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,276 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,281 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE 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 W
HERE id=?
14:03:28,423 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,429 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,439 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,446 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,605 DEBUG [GangsterEJB#findBadDudes_declaredsql] Executing SQL: SELECT id FROM GA
NGSTER WHERE badness > ? ORDER BY badness DESC
14:03:28,613 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,631 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,641 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,647 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,655 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,783 DEBUG [GangsterEJB#ejbSelectBoss_ejbql] Executing SQL: SELECT DISTINCT t0_und
erling_organization_theBos.id FROM GANGSTER t1_underling, ORGANIZATION t4_underling_organi
zation, GANGSTER t0_underling_organization_theBos WHERE ((t1_underling.name = ?) OR (t1_un
derling.nick_name = ?)) AND t1_underling.organization=t4_underling_organization.name AND t
4_underling_organization.the_boss=t0_underling_organization_theBos.id
14:03:28,815 DEBUG [GangsterEJB#findByPrimaryKey] Executing SQL: SELECT id FROM GANGSTER W
HERE id=?
14:03:28,822 DEBUG [GangsterEJB#ejbSelectBoss_ejbql] Executing SQL: SELECT DISTINCT t0_und
erling_organization_theBos.id FROM GANGSTER t1_underling, ORGANIZATION t4_underling_organi
zation, GANGSTER t0_underling_organization_theBos WHERE ((t1_underling.name = ?) OR (t1_un
derling.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 W
HERE 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, pa
ge_area, page_exch, page_ext, email FROM GANGSTER WHERE (id=?)
14:03:29,987 DEBUG [GangsterEJB] Executing SQL: UPDATE GANGSTER SET cell_area=?, cell_exc
h=?, 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.

11.1.3. Read-ahead

The other main target runs a set of tests to demonstrate the optimized loading configurations presented in Section 11.7, “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.

11.2. 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 Figure 11.2, “The jbosscmp-jdbc top level content model.”.

The jbosscmp-jdbc top level content model.

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 Section 11.12, “Defaults” for the details of the defaults content.

  • enterprise-beans: The enterprise-beans element allows for customization of entity beans defined in the ejb-jar.xml enterprise-beans descriptor. This is described in detail in Section 11.3, “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 Section 11.5, “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 Section 11.4.6, “Dependent Value Classes (DVCs)” (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 Section 11.13, “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 Section 11.11, “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 Section 11.13.3, “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.

Example 11.1. 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. 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">

11.3. 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[3]. 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[4]. Local interfaces are not unique to CMP and are not discussed in this documentation. The simplified code for the Gangster entity follows:

Example 11.2. 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; 
}

Example 11.3. Entity Local Interface

// Gangster Local Interface 
public interface Gangster extends EJBLocalObject {    
    Integer getGangsterId();    
    String getName();
    String getNickName();
    void setNickName(String nickName); 
} 

Example 11.4. 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 below.

Example 11.5. 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 Section 11.6, “Queries”.

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

Example 11.6. 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 Section 11.12, “Defaults”.

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 Section 11.13.2, “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.

  • alter-table: If create-table is used to automatically create the schema, alter-table can be used to keep the schema current with changes to the entity bean. Alter table will perform the following specific tasks:

    • new fields will be created

    • fields which are no longer used will be removed

    • string fields which are shorter than the declared length will have their length increased to the declared length. (not supported by all databases)

  • 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 Section 11.4.4, “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 Section 11.7.3, “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.

  • clean-read-ahead-on-load: When an entity is loaded from the read ahead cache, JBoss can remove the data used from the read ahead cache. The default is false.
  • 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 Section 11.4, “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 Section 11.7.2, “Load Groups”.

  • eager-load-groups: This optional element defines one or more load grouping as eager load groups. This is discussed in Section 11.8.2, “Eager-loading Process”.

  • lazy-load-groups: This optional element defines one or more load grouping as lazy load groups. This is discussed in Section 11.8.3, “Lazy loading Process”.

  • query: This optional element specifies the definition of finders and selectors. This is discussed in Section 11.6, “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 Section 11.11, “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 Section 11.10, “Optimistic Locking”.

  • audit: This optional element defines the CMP fields that will be audited. This is described in detail in Section 11.4.5, “Auditing Entity Access”.

11.4. CMP-Fields

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

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

11.4.2. 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 Example 11.7, “Sample cmp-field abstract accessor declaration ” you would add the following to the ejb-jar.xml file:

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

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

The following is an example usage of 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.

  • check-dirty-after-get: This value defaults to false for primitive types and the basic java.lang immutable wrappers (Integer, String, etc...). For potentially mutable objects, JBoss will mark they field as potentially dirty after a get operation. If the dirty check on an object is too expensive, you can optimize it away by setting check-dirty-after-get to false.

  • state-factory: This specfies class name of a state factory object which can perform dirty checking for this field. State factory classes must implement the CMPFieldStateFactory interface.

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

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

11.4.5. Auditing 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 Figure 11.3, “The jbosscmp-jdbc.xml audit element content model ”.

The jbosscmp-jdbc.xml audit element content model

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

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

11.4.6. 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 the Enterprise JavaBeans Specification 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.

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[5]. 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[6]. A DVC mapping is specified within the dependent-value-classes element.

The jbosscmp-jdbc dependent-value-classes element model.

Figure 11.4. The jbosscmp-jdbc dependent-value-classes element model.

Here is an example of a simple ContactInfo DVC class.

public class ContactInfo 
    implements Serializable 
{
    /** The cell phone number. */
    private PhoneNumber cell;
    
    /** The pager number. */
    private PhoneNumber pager;
    
    /** The email address */
    private String email;

    // ...
}

The contact info includes a phone number, which is represented by another DVC class.

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;

    // ...
} 

The dependent-value-classes mapping for these two classes is relatively straight forward.

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

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 named info that uses the the ContactInfo DVC would have the following columns:

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:

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

When overriding property info for the entity, you need to refer to the property from a flat perspective as in cell.areaCode.

11.5. 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[7].

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.

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

public abstract class OrganizationBean
    implements EntityBean 
{
    public abstract Set getMemberGangsters();
    public abstract void setMemberGangsters(Set gangsters);
} 

Second, add the following to the GangsterBean class:

public abstract class GangsterBean
    implements EntityBean 
{
    public abstract Organization getOrganization();
    public abstract void setOrganization(Organization org);
}

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

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

Example 11.10. 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 relationships [8] 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.

11.5.3. 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 Figure 11.5, “The jbosscmp-jdbc.xml ejb-relation element content model”.

The jbosscmp-jdbc.xml ejb-relation element content model

Figure 11.5. The jbosscmp-jdbc.xml ejb-relation element content model

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

<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 relationship can be declared as read only using the read-only and read-time-out elements. They have the same semantics as their counterparts in the entity element.

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.

11.5.3.1. 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 Figure 11.6, “The jbosscmp-jdbc ejb-relationship-role element content model” .

The jbosscmp-jdbc ejb-relationship-role element content model

Figure 11.6. 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 must[9] 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 Section 11.8.3.1, “Relationships”.

  • batch-cascade-delete: When a relationship is marked as batch-delete in the ejb-jar.xml, the corresponding relationship can be marked with batch-cascade-delete. In this case, the cascade delete will be performed with a single SQL statement.

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. You can change this insert behavior using the jboss.xml insert-after-ejb-post-create container configuration flag. The following example illustrates the use of 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 Figure 11.7, “The jbosscmp-jdbc key-fields element content model”.

The jbosscmp-jdbc key-fields element content model

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

11.5.3.2. 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 below with the foreign key elements highlighted in bold:

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

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

The jbosscmp-jdbc relation-table-mapping element content model

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

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

11.6. 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 the Enterprise JavaBeans Specification.

11.6.1. 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_ejbql [10] finder on the GangsterHome interface:

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

Example 11.13. ejbSelect Declaration

public abstract class GangsterBean 
    implements EntityBean 
{
    public abstract Set ejbSelectBoss_ejbql(String name)
        throws FinderException;
}

11.6.2. 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. The EJB-QL query is declared in a query element, which is contained in the entity element. The following are the declarations for findBadDudes_ejbql and ejbSelectBoss_ejbql queries.

<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

11.6.3. 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 Figure 11.9, “The jbosscmp-jdbc query element content model”.

The jbosscmp-jdbc query element content model

Figure 11.9. 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 Section 11.7, “Optimized Loading”.

11.6.4. 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. The following example provides an example JBossQL declaration.

<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>
</jbosscmp-jdbc>

The corresponding generated SQL is straightforward.

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.

<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>
</jbosscmp-jdbc>

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

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

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

11.6.6. 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 Figure 11.10, “The jbosscmp-jdbc declared-sql element content model.>”.

The jbosscmp-jdbc declared-sql element content model.>

Figure 11.10. 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 finders or to facilitate aggregate functions in selects.

  • 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 additional SQL that is appended to the end of a query.

The following is an example DeclaredSQL declaration.

<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>
</jbosscmp-jdbc>

The generated SQL would be:

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. The following is example DeclaredSQL declaration with an additional 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>
</jbosscmp-jdbc>

The generated SQL would be:

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 that syntax, so the declaration of an alias is preferred. The optional empty distinct element 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.

Now we well see an example which overrides an ejbSelct to select all of the zip codes in which an Organization operates.

<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>
</jbosscmp-jdbc>

The corresponding SQL would be:

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

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

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

  • DVC: 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 CVS property of type ContactInfo, 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.

  • entity: 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, 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.

11.6.7. EJBQL 2.1 and SQL92 queries

The default query compiler doesn't dully support EJB-QL 2.1 or the SQL92 standard. If you need either of these functions, you can replace the query compiler. The default compiler is specified in standardjbosscmp-jdbc.xml.

<defaults>
    ...
    <ql-compiler>org.jboss.ejb.plugins.cmp.jdbc.JDBCEJBQLCompiler</ql-compiler>
    ...
</defaults>

To use the SQL92 compiler, simply specifyt the SQL92 compiler in ql-compiler element.

<defaults>
    ...
    <ql-compiler>org.jboss.ejb.plugins.cmp.jdbc.EJBQLToSQL92Compiler</ql-compiler>
    ...
</defaults>

This changes the query compiler for all beans in the entire system. You can also specify the ql-compiler for each element in jbosscmp-jdbc.xml. Here is an example using one of our earlier queries.

<query>
    <query-method>
        <method-name>findBadDudes_ejbql</method-name>
        <method-params>
            lt;method-param>int</method-param>
        </method-params>
    </query-method>
    <ejb-ql><![CDATA[
        SELECT OBJECT(g)
        FROM gangster g
        WHERE g.badness > ?1]]>
    </ejb-ql>
    <ql-compiler>org.jboss.ejb.plugins.cmp.jdbc.EJBQLToSQL92Compiler</ql-compiler>
</query>

One important limitation of SQL92 query compiler is that it always selects all the fields of an entity regardless the read-ahead strategy in use. For example, if a query is configured with the on-load read-ahead strategy, the first query will include all the fields, not just primary key fields but only the primary key fields will be read from the ResultSet. Then, on load, other fields will be actually loaded into the read-ahead cache. The on-find read-ahead with the default load group * works as expected.

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

Example 11.14. Custom Finder Example Code

public abstract class GangsterBean
    implements EntityBean 
{
    public Collection ejbFindByPrimaryKeys(Collection keys)
    {
        return keys;
    }
}        

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

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

Example 11.15. Loading Scenario Example Code

public String createGangsterHtmlTable_none() 
    throws FinderException 
{
    StringBuffer table = new StringBuffer();
    table.append("<table>");

    Collection gangsters = gangsterHome.findAll_none();
    for(Iterator iter = gangsters.iterator(); iter.hasNext(); ) {
	Gangster gangster = (Gangster)iter.next();
	table.append("<tr>");
	table.append("<td>").append(gangster.getName());
	table.append("</td>");
	table.append("<td>").append(gangster.getNickName());
	table.append("</td>");
	table.append("<td>").append(gangster.getBadness());
	table.append("</td>");
	table.append("</tr>");
    }

    return table.toString();
}

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:

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

Example 11.17. 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" problem[13] 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 Section 11.8.2, “Eager-loading Process”. The following table shows the execution of the queries:

Table 11.1. Unoptimized Query Execution

idnamenick_namebadnesshangoutorganization
0YojimboBodyguard70Yakuza
1TakeshiMaster101Yakuza
2YurikoFour finger42Yakuza
3ChowKiller93Triads
4ShogiLightning84Triads
5ValentinoPizza-Face45Mafia
6ToniToothless26Mafia
7CorleoneGodfather67Mafia

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

<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 this example, 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.

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

11.7.3.1. on-find

The on-find strategy reads additional columns when the query is invoked. If the query in the scenario detailed Example 11.15, “Loading Scenario Example Code” is on-find optimized, JBossCMP will execute the following query at line 5:

SELECT t0_g.id, t0_g.name, t0_g.nick_name, t0_g.badness 
    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:

Table 11.2. on-find Optimized Query Execution

idnamenick_namebadnesshangoutorganization
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-ahead 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:

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

11.7.3.1.1. Left join read ahead

Left join read ahead is an enhanced on-find read-ahead strategy. It allows you to preload in one SQL query not only fields from the base instance but also related instances which can be reached from the base instance by CMR navigation. There are no limitation for the depth of CMR navigations. There are also no limitations for cardinality of CMR fields used in navigation and relationship type mapping, i.e. both foreign key and relation-table mapping styles are supported. Let's look at some examples. Entity and relationship declarations can be found below.

11.7.3.1.2. D#findByPrimaryKey

Suppose we have an entity D. A typical SQL query generated for the findByPrimaryKey would look like this:

SELECT t0_D.id, t0_D.name FROM D t0_D WHERE t0_D.id=?

Suppose that while executing findByPrimaryKey we also want to preload two collection-valued CMR fields bs and cs.

<query>
    <query-method>
        <method-name>findByPrimaryKey</method-name>
        <method-params>
            <method-param>java.lang.Long</method-param>
        </method-params>
    </query-method>
    <jboss-ql><![CDATA[SELECT OBJECT(o) FROM D AS o WHERE o.id = ?1]]></jboss-ql>
    <read-ahead>
        <strategy>on-find</strategy>
        <page-size>4</page-size>
        <eager-load-group>basic</eager-load-group>
        <left-join cmr-field="bs" eager-load-group="basic"/>
        <left-join cmr-field="cs" eager-load-group="basic"/>
    </read-ahead>
</query>

The left-join declares the relations to be eager loaded. The generated SQL would look like this:

SELECT t0_D.id, t0_D.name,
       t1_D_bs.id, t1_D_bs.name,
       t2_D_cs.id, t2_D_cs.name
  FROM D t0_D
       LEFT OUTER JOIN B t1_D_bs ON t0_D.id=t1_D_bs.D_FK
       LEFT OUTER JOIN C t2_D_cs ON t0_D.id=t2_D_cs.D_FK
 WHERE t0_D.id=?

For the D with the specific id we preload all its related B's and C's and can access those instance loading them from the read ahead cache, not from the database.

11.7.3.1.3. D#findAll

In the same way, we could optimize the findAll method on D selects all the D's. A normal findAll query would look like this:

SELECT DISTINCT t0_o.id, t0_o.name FROM D t0_o ORDER BY t0_o.id DESC

To preload the relations, we simply need to add the left-join elements to the query.

<query>
    <query-method>
        <method-name>findAll</method-name>
    </query-method>
    <jboss-ql><![CDATA[SELECT DISTINCT OBJECT(o) FROM D AS o ORDER BY o.id DESC]]></jboss-ql>
    <read-ahead>
        <strategy>on-find</strategy>
        <page-size>4</page-size>
        <eager-load-group>basic</eager-load-group>
        <left-join cmr-field="bs" eager-load-group="basic"/>
        <left-join cmr-field="cs" eager-load-group="basic"/>
    </read-ahead>
</query>

And here is the generated SQL:

SELECT DISTINCT t0_o.id, t0_o.name,
                t1_o_bs.id, t1_o_bs.name,
                t2_o_cs.id, t2_o_cs.name
  FROM D t0_o
       LEFT OUTER JOIN B t1_o_bs ON t0_o.id=t1_o_bs.D_FK
       LEFT OUTER JOIN C t2_o_cs ON t0_o.id=t2_o_cs.D_FK
 ORDER BY t0_o.id DESC

Now the simple findAll query now preloads the related B and C objects for each D object.

11.7.3.1.4. A#findAll

Now let's look at a more complex configuration. Here we want to preload instance A along with several relations.

  • its parent (self-relation) reached from A with CMR field parent

  • the B reached from A with CMR field b, and the related C reached from B with CMR field c

  • B reached from A but this time with CMR field b2 and related to it C reached from B with CMR field c.

For reference, the standard query would be:

SELECT t0_o.id, t0_o.name FROM A t0_o ORDER BY t0_o.id DESC FOR UPDATE

The following metadata describes our preloading plan.

<query>
    <query-method>
        <method-name>findAll</method-name>
    </query-method>
    <jboss-ql><![CDATA[SELECT OBJECT(o) FROM A AS o ORDER BY o.id DESC]]></jboss-ql>
    <read-ahead>
        <strategy>on-find</strategy>
        <page-size>4</page-size>
        <eager-load-group>basic</eager-load-group>
        <left-join cmr-field="parent" eager-load-group="basic"/>
        <left-join cmr-field="b" eager-load-group="basic">
            <left-join cmr-field="c" eager-load-group="basic"/>
        </left-join>
        <left-join cmr-field="b2" eager-load-group="basic">
            <left-join cmr-field="c" eager-load-group="basic"/>
        </left-join>
    </read-ahead>
</query>

The SQL query generated would be:

SELECT t0_o.id, t0_o.name,
       t1_o_parent.id, t1_o_parent.name,
       t2_o_b.id, t2_o_b.name,
       t3_o_b_c.id, t3_o_b_c.name,
       t4_o_b2.id, t4_o_b2.name,
       t5_o_b2_c.id, t5_o_b2_c.name
  FROM A t0_o
       LEFT OUTER JOIN A t1_o_parent ON t0_o.PARENT=t1_o_parent.id
       LEFT OUTER JOIN B t2_o_b ON t0_o.B_FK=t2_o_b.id
       LEFT OUTER JOIN C t3_o_b_c ON t2_o_b.C_FK=t3_o_b_c.id
       LEFT OUTER JOIN B t4_o_b2 ON t0_o.B2_FK=t4_o_b2.id
       LEFT OUTER JOIN C t5_o_b2_c ON t4_o_b2.C_FK=t5_o_b2_c.id
 ORDER BY t0_o.id DESC FOR UPDATE

With this configuration, you can navigate CMRs from any found instance of A without an additional database load.

11.7.3.1.5. A#findMeParentGrandParent

Here is some more example of self-relation. Suppose, we want to write a method that would preload an instance, its parent, grand-parent and its grand-grand-parent in one query. To do this, we would used nested left-join declaration.

<query>
    <query-method>
        <method-name>findMeParentGrandParent</method-name>
        <method-params>
            <method-param>java.lang.Long</method-param>
        </method-params>
    </query-method>
    <jboss-ql><![CDATA[SELECT OBJECT(o) FROM A AS o WHERE o.id = ?1]]></jboss-ql>
    <read-ahead>
        <strategy>on-find</strategy>
        <page-size>4</page-size>
        <eager-load-group>*</eager-load-group>
        <left-join cmr-field="parent" eager-load-group="basic">
            <left-join cmr-field="parent" eager-load-group="basic">
                <left-join cmr-field="parent" eager-load-group="basic"/>
            </left-join>
        </left-join>
    </read-ahead>
</query>

The generated SQL would be:

SELECT t0_o.id, t0_o.name, t0_o.secondName, t0_o.B_FK, t0_o.B2_FK, t0_o.PARENT,
       t1_o_parent.id, t1_o_parent.name,
       t2_o_parent_parent.id, t2_o_parent_parent.name,
       t3_o_parent_parent_parent.id, t3_o_parent_parent_parent.name
  FROM A t0_o
       LEFT OUTER JOIN A t1_o_parent ON t0_o.PARENT=t1_o_parent.id
       LEFT OUTER JOIN A t2_o_parent_parent ON t1_o_parent.PARENT=t2_o_parent_parent.id
       LEFT OUTER JOIN A t3_o_parent_parent_parent ON t2_o_parent_parent.PARENT=t3_o_parent_parent_parent.id
 WHERE (t0_o.id = ?) FOR UPDATE

Note, if we remove left-join metadata we will have only

SELECT t0_o.id, t0_o.name, t0_o.secondName, t0_o.B2_FK, t0_o.PARENT FOR UPDATE

11.7.3.2. 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. This strategy is based on the theory that the results of a find or select 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 Example 11.15, “Loading Scenario Example Code” remains unchanged.

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

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

Table 11.3. on-load Optimized Query Execution

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

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

11.7.3.3. 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 the following example.

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

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

11.8.1. 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 the Enterprise JavaBeans Specification, but the last one 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. 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, EJBs on JBoss. The following example changes the commit option to A for all entity beans in the application:

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

11.8.2. 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 the bean[16]. 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 will use the default eager-load-group declared for the entity. In the following example configuration, the basic load group is set as the default eager-load-group for the GangsterEJB entity:

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

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

  • Any residual data in the entity context is flushed. This assures that old data does not bleed into the new load.

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

  • All data in the preload cache for this entity is loaded into the fields.

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

  • 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 Section 11.7.3.2, “on-load”. The data for the current entity is stored in the context and the data for the other entities is stored in the preload cache.

  • The ejbLoad method of the entity is called.

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

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

  • All data in the preload cache for this entity is loaded into the fields.

  • If the field value was loaded by the preload cache the lazy load process is finished.

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

  • A query is executed to select the necessary columns. 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.

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

Example 11.23. Relationship Lazy Loading Example Code

public String createGangsterHangoutHtmlTable() 
    throws FinderException
{
    StringBuffer table = new StringBuffer();
    table.append("<table>");
    Collection gangsters = gangsterHome.findAll_onfind();
    for (Iterator iter = gangsters.iterator(); iter.hasNext(); ) {
        Gangster gangster = (Gangster)iter.next();

        Location hangout = gangster.getHangout();
        table.append("<tr>");
        table.append("<td>").append(gangster.getName());
        table.append("</td>");
        table.append("<td>").append(gangster.getNickName());
        table.append("</td>");
        table.append("<td>").append(gangster.getBadness());
        table.append("</td>");
        table.append("<td>").append(hangout.getCity());
        table.append("</td>");
        table.append("<td>").append(hangout.getState());
        table.append("</td>");
        table.append("<td>").append(hangout.getZipCode());
        table.append("</td>");
        table.append("</tr>");
    }

    table.append("</table>");return table.toString();
}

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:

Example 11.24. 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-filaelds>
            </ejb-relationship-role>
        </ejb-relation>
    </relationships>
</jbosscmp-jdbc>

At line 25, JBossCMP will execute the following 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:

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:

idnamenick_namebadnesshangoutidcitystzip
0YojimboBodyguard700San FranCA94108
1TakeshiMaster1011San FranCA94133
2YurikoFour finger422San FranCA94133
3ChowKiller933San FranCA94133
4ShogiLightning844San FranCA94133
5ValentinoPizza-Face455New YorkNY10017
6ToniToothless266ChicagoIL60661
7CorleoneGodfather677Las VegasNV89109

11.8.4. Lazy loading result sets

By default, when a multiobject finder or ejbSelect method is executed the ResultSet is read to the end immediately. The client receives a collection of EJBLocalObject or CMP field values which it can then iterate through. For big result sets this approach is not efficient. In some cases it is better to delay reading the next row in the ResultSet until the client tries to read the corresponding value from the collection. You can get this behaviour for a query using the lazy-resultset-loading element.

<query>
    <query-method>
        <method-name>findAll</method-name>
    </query-method>
    <jboss-ql><![CDATA[select object(o) from A o]]></jboss-ql>
    <lazy-resultset-loading>true</lazy-resultset-loading>
</query>

The are some issues you should be aware of when using lazy result set loading. Special care should be taken when working with a Collection associated with a lazily loaded result set. The first call to iterator() returns a special Iterator that reads from the ResultSet. Until this Iterator has been exhausted, subsequent calls to iterator() or calls to the add() method will result in an exception. The remove() and size() methods work as would be expected.

11.9. 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 Example 11.15, “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:

Example 11.25. No Transaction Loading Example Code

public String createGangsterHtmlTable_no_tx() throws FinderException
{
    StringBuffer table = new StringBuffer();
    table.append("<table>");

    Collection gangsters = gangsterHome.findFour();
    for(Iterator iter = gangsters.iterator(); iter.hasNext(); ) {
        Gangster gangster = (Gangster)iter.next();
        table.append("<tr>");
        table.append("<td>").append(gangster.getName());
        table.append("</td>");
        table.append("<td>").append(gangster.getNickName());
        table.append("</td>");
        table.append("<td>").append(gangster.getBadness());
        table.append("</td>");
        table.append("</tr>");
    }
    
    table.append("</table>");
    return table.toString();
}   

The following is the query executed at line 53.

Example 11.26. 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 < 4followi
  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[17] (one for each loop):

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

The following figure shows the execution of the queries:

No Transaction on-find optimized query execution

Figure 11.11. 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 declared method with a user transaction:

Example 11.28. 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);
    }
}

11.10. Optimistic Locking

JBoss has supports for optimistic locking of entity beans. Optimistic locking allows multiple instances of the same entity bean to be active simultaneously. Consistency is enforced based on the optimistic locking policy choice. 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 below and the description of the elements follows.

The jbosscmp-jdbc optimistic-locking element content model

Figure 11.12. 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 below.

Example 11.29. A sample jbosscmp-jdbc.xml descriptor illustrating the optimistic locking strategies

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

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

The jbosscmp-jdbc.xml entity-command element model

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

11.11.1. Existing Entity Commands

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

  • default (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.

  • no-select-before-insert: (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.

  • pk-sql (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.

  • mysql-get-generated-keys: (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.

  • oracle-sequence: (org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCOracleCreateCommand) The JDBCOracleCreateCommand is a 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.

  • hsqldb-fetch-key: (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.

  • sybase-fetch-key: (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.

  • mssql-fetch-key: (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.

  • informix-serial: (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.

  • postgresql-fetch-seq" (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 the default being table_pkColumn_seq.

  • key-generator: (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.

  • get-generated-keys: (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 below.

Example 11.30. 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 below.

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

11.12. Defaults

JBossCMP global defaults are defined in the standardjbosscmp-jdbc.xml file of the server/<server-name>/conf/ directory. 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 below.

The jbosscmp-jdbc/defaults content model

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

11.12.1. 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 Section 11.13.2, “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.

  • alter-table: If create-table is used to automatically create the schema, alter-table can be used to keep the schema current with changes to the entity bean. Alter table will perform the following specific tasks:

    • new fields will be created

    • fields which are no longer used will be removed

    • string fields which are shorter than the declared length will have their length increased to the declared length. (not supported by all databases)

  • 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 field basis. 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 Section 11.7.3, “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 Section 11.7.3.2, “on-load”. The default is 1000.

  • clean-read-ahead-on-load: When an entity is loaded from the read ahead cache, JBoss can remove the data used from the read ahead cache. The default is false.
  • 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 Section 11.11, “Entity Commands and Primary Key Generation”.

11.13. 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 Figure 11.15, “The jbosscmp-jdbc type-mapping element content model.”. The elements are:

  • name: This required element provides the name identifying the database customization. It is used to refer to the mapping by the datasource-mapping elements found in defaults and entity.

  • row-locking-template: This required element gives the PreparedStatement template used to create a row lock on the selected rows. The template must support three arguments:

    1. the select clause

    2. the from clause. The order of the tables is currently not guaranteed

    3. the where clause

    If row locking is not supported in select statement this element should be empty. The most common form of row locking is select for update as in: SELECT ?1 FROM ?2 WHERE ?3 FOR UPDATE.

  • pk-constraint-template: This required element gives the PreparedStatement template used to create a primary key constraint in the create table statement. The template must support two arguments

    1. Primary key constraint name; which is always pk_{table-name}

    2. Comma separated list of primary key column names

    If a primary key constraint clause is not supported in a create table statement this element should be empty. The most common form of a primary key constraint is: CONSTRAINT ?1 PRIMARY KEY (?2)

  • fk-constraint-template: This is the template used to create a foreign key constraint in separate statement. The template must support five arguments:

    1. Table name

    2. Foreign key constraint name; which is always fk_{table-name}_{cmr-field-name}

    3. Comma separated list of foreign key column names

    4. References table name

    5. Comma separated list of the referenced primary key column names

    If the datasource does not support foreign key constraints this element should be empty. The most common form of a foreign key constraint is: ALTER TABLE ?1 ADD CONSTRAINT ?2 FOREIGN KEY (?3) REFERENCES ?4 (?5).

  • auto-increment-template: This declares the SQL template for specifying auto increment columns.

  • add-column-template: When alter-table is true, this SQL template specifies the syntax for adding a column to an existing table. The default value is ALTER TABLE ?1 ADD ?2 ?3. The parameters are:

    1. the table name

    2. the column name

    3. the column type

  • drop-column-template: When alter-table is true, this SQL template specifies the syntax for droping a column to from an existing table. The default value is ALTER TABLE ?1 DROP ?2. The parameters are:

    1. the table name

    2. the column name

  • alter-column-template: When alter-table is true, this SQL template specifies the syntax for droping a column to from an existing table. The default value is ALTER TABLE ?1 ALTER ?2 TYPE ?3. The parameters are:

    1. the table name

    2. the column name

    3. the column type

  • alias-header-prefix: This required element gives the prefix used in creating the alias header. An alias header is prepended to a generated table alias by the EJB-QL compiler to prevent name collisions. The alias header is constructed as follows: alias-header-prefix + int_counter + alias-header-suffix. An example alias header would be t0_ for an alias-header-prefix of "t" and an alias-header-suffix of "_".

  • alias-header-suffix: This required element gives the suffix portion of the generated alias header.

  • alias-max-length: This required element gives the maximum allowed length for the generated alias header.

  • subquery-supported: This required element specifies if this type-mapping subqueries as either true or false. Some EJB-QL operators are mapped to exists subqueries. If subquery-supported is false, the EJB-QL compiler will use a left join and is null.

  • true-mapping: This required element defines true identity in EJB-QL queries. Examples include TRUE, 1, and (1=1).

  • false-mapping: This required element defines false identity in EJB-QL queries. Examples include FALSE, 0, and (1=0).

  • function-mapping: This optional element specifies one or more the mappings from an EJB-QL function to an SQL implementation. See Section 11.13.1, “Function Mapping” for the details.

  • mapping: This required element specifies the mappings from a Java type to the corresponding JDBC and SQL type. See Section 11.13.2, “Type Mapping” for the details.

The jbosscmp-jdbc type-mapping element content model.

Figure 11.15. The jbosscmp-jdbc type-mapping element content model.

11.13.1. Function Mapping

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

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

The jbosscmp-jdbc mapping element content model.

Figure 11.16. 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 types are only limited by your database vendor.

An example mapping element for a short in Oracle9i is shown below.

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

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

Example 11.32. 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 below.

The user-type-mapping content model >

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

  • check-dirty-after-get: This value defaults to false for primitive types and the basic java.lang immutable wrappers (Integer, String, etc...). For potentially mutable objects, JBoss will mark they field as potentially dirty after a get operation. If the dirty check on an object is too expensive, you can optimize it away by setting check-dirty-after-get to false.

  • state-factory: This specfies class name of a state factory object which can perform dirty checking for this field. State factory classes must implement the CMPFieldStateFactory interface.



[3] 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.

[4] Most J2EE servers, including JBoss, can optimize in-VM calls over a remote interface by using pass-by-reference semantics.

[5] The requirement that a DVC use the JavaBeans naming convention will be removed in a future release of JBossCMP.

[6] 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.

[7] The EJB specification does not even allow for relationships between entities in different applications within the same VM

[8] 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.

[9] 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.

[10] Ignore the ejbql suffix; it is not required. Later this query will be implemented using JBossQL and declared SQL, and the suffix is used to separate the different query specifications in the jbosscmp-jdbc.xml file.

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

[17] 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.