Chapter 7. Container-Managed Persistence

In this chapter we’ll use the RosterApp example application from the J2EE tutorial to explore container-managed persistence in a bit more depth than we did with Duke's Bank. You should read through the CMP tutorial notes before proceeding so that you have a good overview of the beans and their relationships.

You’ll find the code in j2eetutorial14/examples/ejb/cmproster. The application implements a player roster for sports teams playing in leagues. There are three entity beans PlayerEJB, TeamEJB and LeagueEJB and a single session bean, RosterEJB, which manipulates them and provides the business methods accessed by the client application. Only the session bean has a remote interface.

7.1. Building the Example

The EJBs are packaged in two separate JAR files, one for the entity beans and one for the session bean. As before, we’ve provided an ejb-jar.xml file for each one. You don’t need a jboss.xml file for this example. All the CMP information needed to build the database schema is included in the standard descriptor. We’ll look at JBoss-specific customization later.

To compile the code, first make sure you’re in the examples directory. Running the compile-cmp target will compile all the code in one go.

ant -f jboss-build.xml compile-cmp

Run the following package-team to build the team JAR file which contains the entity beans.

ant -f jboss-build.xml package-team 

The package-roster target builds the roster JAR.

ant -f jboss-build.xml package-roster

Both JAR files will be created in the jar directory. Build the client jar using the package-roster-client target.

ant -f jboss-build.xml package-roster-client 

Finally assemble the RosterApp EAR using the assemble-roster target.

ant -f jboss-build.xml assemble-roster

7.2. Deploying and Running the Application

Deploying the application is done with the deploy-cmp Ant target.

ant -f jboss-build.xml deploy-cmp

Copy the RosterApp.ear file from the jar directory to the JBoss deploy directory (or run Ant with the deploy-cmp target) and check the output from the server.

13:49:11,044 INFO  [EARDeployer] Init J2EE application: file:/private/tmp/jboss-4.0.2/serv
er/default/deploy/RosterApp.ear
13:49:11,884 INFO  [EjbModule] Deploying RosterBean
13:49:13,366 INFO  [EjbModule] Deploying TeamBean
13:49:13,751 INFO  [EjbModule] Deploying LeagueBean
13:49:13,842 INFO  [EjbModule] Deploying PlayerBean
13:49:14,377 INFO  [EJBDeployer] Deployed: file:/private/tmp/jboss-4.0.2/server/default/tm
p/deploy/tmp29312RosterApp.ear-contents/roster-ejb.jar
13:49:17,931 INFO  [EJBDeployer] Deployed: file:/private/tmp/jboss-4.0.2/server/default/tm
p/deploy/tmp29312RosterApp.ear-contents/team-ejb.jar
13:49:17,991 INFO  [EARDeployer] Started J2EE application: file:/private/tmp/jboss-4.0.2/s
erver/default/deploy/RosterApp.ear

There are a few things worth noting here. In the Duke’s Bank application, we specified the JNDI name we wanted a particular EJBHome reference to be bound under in the jboss.xml file. Without that information JBoss will default to using the EJB name. So the session bean is bound under RosterBean and so on. You can check these in the JMX Console as before.

The first time you deploy the application, JBoss will automatically create the required database tables. If you take a look at the database schema using the Hypersonic database manager (see Section 4.1.7.3, “The HSQL Database Manager Tool”), you will see that JBoss has created one table for each entity bean and an addition join table needed to handle the many-to-many relationship between players and teams. The table and column names correspond the names of the entity beans and their attributes. If these names are suitable, you won't need to further refine the schema. In this case we've had to manually map the position field on PlayerBean to a column named pos because the default column name, position, is a reserved token in HSQL. The schema is in the jbosscmp-jdbc.xml file.

Note that if you increase the logging level for the org.jboss.ejb.plugins.cmp package (Section 2.2.2, “Logging Service”) to DEBUG, the engine will log the SQL commands which it is executing. This can be useful in understanding how the engine works and how the various tuning parameters affect the loading of data.

7.2.1. Running the Client

The client performs some data creation and retrieval operations via the session bean interface. It creates leagues, teams and players which will be inserted into the database. The session bean methods it calls to retrieve data are mainly wrappers for EJB finder methods. The command to run the client and the expected output are shown below.

$ ant -f jboss-build.xml run-cmp 
Buildfile: jboss-build.xml 
 
run-cmp: 
[java] P10 Terry Smithson midfielder 100.0 
[java] P6 Ian Carlyle goalkeeper 555.0 
[java] P7 Rebecca Struthers midfielder 777.0 
[java] P8 Anne Anderson forward 65.0 
[java] P9 Jan Wesley defender 100.0 
 
[java] T1 Honey Bees Visalia 
[java] T2 Gophers Manteca 
[java] T5 Crows Orland 
 
[java] P2 Alice Smith defender 505.0 
[java] P22 Janice Walker defender 857.0 
[java] P25 Frank Fletcher defender 399.0 
[java] P5 Barney Bold defender 100.0 
[java] P9 Jan Wesley defender 100.0 
 
[java] L1 Mountain Soccer 
[java] L2 Valley Basketball 

The client doesn’t remove the data, so if you run it twice it will fail because it tries to create entities which already exist. If you want to run it again you’ll have to remove the data. The easiest way to do this (if you’re using HSQL) is to delete the contents of the data/hypersonic directory in the server configuration you are using (assuming you don’t have any other important data in there) and restart the server. We’ve also provided a SQL script to delete the data. You can run it with the db-delete target.

ant -f jboss-build.xml db-delete

You could also use SQL commands directly through the HSQL Manager tool to delete the data.

7.3. CMP Customization

There are many ways you can further customize the CMP engine’s behaviour by using the jbosscmp-jdbc.xml file. It is used for basic information such as the datasource name and type-mapping (Hypersonic, Oracle etc.) and whether the database tables should be automatically created on deployment and deleted on shutdown. You can customize the names of database tables and columns which the EJBs are mapped to and you can also tune the way in which the engine loads the data depending on how you expect it to be used. For example, by using the read-ahead element you can get it to read and cache blocks of data for multiple EJBs with a single SQL call, anticipating further access. Eager-loading groups can be specified, meaning that only some fields are loaded initially with the entity; the others are lazy-loaded if and when they are required. The accessing of relationships between EJBs can be tuned using similar mechanisms. This flexibility is impossible to achieve if you are using BMP where each bean must be loaded with a single SQL call. If on top of that you include having to write all your SQL and relationship management code by hand then the choice should be obvious. You should rarely, if ever, have to use BMP in your applications.

The details of tuning the CMP engine are beyond the scope of this document but you can get an idea of what’s available by examining the DTD (docs/dtd/jbosscmp-jdbc_4_0.dtd) which is well commented. There is also a standard setup in the conf directory called standardjbosscmp-jdbc.xml which contains values for the default settings and a list of type-mappings for common databases. The beginning of the file is shown below.

<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> 
        <row-locking>false</row-locking> 
        <pk-constraint>true</pk-constraint> 
        <fk-constraint>false</fk-constraint> 
        <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> 
... 

You can see that, among other things, this sets the datasource and mapping for use with the embedded Hypersonic database and sets table-creation to true and removal to false, so the schema will be created if it doesn’t already exist. The read-only and read-time-out elements specify whether data should be read-only and the time in milliseconds it is valid for. Note that many of these elements can be used at different granularities such as per-entity or even on a field-by-field basis (consult the DTD for details). The read-ahead element uses an on-load strategy which means that the EJB data will be loaded when it is accessed (rather than when the finder method is called) and the page-size setting means that the data for up to 1000 entities will be loaded with one SQL command. You can override this either in your own jbosscmp-jdbc.xml file’s list of default settings or by adding the information to a specific query configuration in the file.

A comprehensive explanation of the CMP engine and its various loading strategies can be found in the full JBoss 4 Application Server Guide.

7.3.1. XDoclet

Writing and maintaining deployment descriptors is a labour-intensive and error-prone job at the best of times, and detailed customization of the CMP engine can lead to some large and complex files. If you are using CMP (or indeed EJBs) in anger then it is worth learning to use the XDoclet code generation engine (http://xdoclet.sourceforge.net/). Using Javadoc-style attribute tags you place in your code, XDoclet will generate the deployment descriptors for you as well as the EJB interfaces and other artifacts if required. It fully supports JBoss CMP, and though the learning curve is quite steep, its use is thoroughly recommended (almost essential in fact) for real projects.