In the previous sections, several times we use the phrase "when an entity is loaded." This is 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 sections describe the commit options and the loading processes. Commit OptionsCentral to the loading process are the commit options, which control when the cached 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. Detailed descriptions of the commit options follow:
The commit option is declared in the jboss.xml file. For a detailed description of this file, see Chapter 5, "EJBs on JBoss." The example shown in Listing 11.2 changes the commit option to A for all entity beans in the application. Listing 11.2. 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> The Eager-Loading ProcessWhen an entity is loaded, JBoss must determine what fields need to be loaded. By default, JBoss uses the eager-load-group of the last query that selected this entity. If the entity has not been selected in a query, or if the last query uses the none read-ahead strategy, JBoss uses the default eager-load-group declared for the entity. In the following configuration example, the basic load group is set as the default eager-load-group for the gangster entity bean: <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. The load process is as follows:
The Lazy-Loading ProcessLazy loading is the other half of eager loading. If a field is not eager loaded, it must be lazy loaded. When an access to an unloaded field of a bean is made, JBoss loads the field and all the fields of any lazy-load-group the field belong to. JBoss performs a set join and then removes any field that is already loaded. A configuration example is shown here: <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> When the bean provider calls getName() with this configuration, JBoss loads name, nickName, and badness, assuming that they are not already loaded. When the bean provider calls getNickName(), the name, nickName, badness, contactInfo, and hangout are loaded. The lazy-loading process is as follows:
RelationshipsRelationships are a special case in lazy loading because a CMR field is both a field and a query. As a field, it can be on-load block loaded, meaning that 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 by using on-find. Again, the easiest way to investigate the loading is to look at a usage scenario. Listing 11.3 generates an HTML table that contains each gangster and his hangout. Listing 11.3. Relationship Lazy-Loading Sample Codepublic 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's findAll_onfind query is unchanged from what is described earlier in this chapter. The configuration of the Location entity and Gangster-Hangout relationship is shown in Listing 11.4. Listing 11.4. 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> JBoss executes the following query for the finder: 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 when the hangout is accessed, JBoss executes the following two queries to load the city, state, and zip fields of the hangout: 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)) Table 11.4 shows the execution of the queries.
Lazy-Loading Result SetsBy default, when a multi-object finder or select method is executed, the JDBC result set is read to the end immediately. The client receives a collection of EJBLocalObject or CMP field values that 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 result set until the client tries to read the corresponding value from the collection. You can get this behavior for a query by 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> There are some issues you should be aware of when using lazy result set loading. You need to take special care 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 result in an exception. The remove() and size() methods work as you would expected. |