14.5. Fetch Groups

14.5.1. Normal Default Fetch Group Behavior
14.5.2. Kodo JDO Fetch Group Behavior
14.5.3. Custom Fetch Group Configuration
14.5.4. Per-field Fetch Configuration

The JDO specification defines a concept of a default fetch group, but it does not touch upon additional, non-default fetch groups. Kodo JDO extends the JDO specification's fetch group concept to allow multiple fetch groups in a given class. These fetch groups can be used to pool together associated fields in order to provide performance improvements over Kodo JDO's normal fetch group behavior. Specifying fetch groups allows for tuning of lazy loading and eager fetching behavior.

14.5.1. Normal Default Fetch Group Behavior

First, let's talk about how Kodo JDO behaves when loading data with just the regular JDO default fetch group information. Imagine the following class and metadata definitions:

public class FetchGroupExample
{
    private int                  a;
    private String               b;
    private BigInteger           c;
    private Date                 d;
    private String               e;
    private String               f;
    private FetchGroupExample    g;
}


<?xml version="1.0"?>
<jdo>
  <package name="">
    <class name="FetchGroupExample">
      <field name="a"/>
      <field name="b"/>
      <field name="c"/>
      <field name="d"/>
      <field name="e"/>
      <field name="f"/>
      <field name="g"/>
    </class>
  </package>
</jdo>

In this example, the default fetch group behavior is left undefined for all fields. So, the default values defined in the JDO specification will be used: all fields except g will be in the default fetch group. g will be left out of the default fetch group because it is a reference to another persistence-capable object.

Kodo JDO will load all fields in the object in the initial select statement, including the primary key of g. This primary key will be loaded because the related object may already be in the PersistenceManager's cache, so we may be able to set up this relation up-front, and since we're already going to the database for all the other fields, we might as well check the primary key. In general, this behavior is ideal, since the cost of executing a select statement including the extra fields for one-one relations from the database is minimal compared to the cost of going back to the database for this information when it's needed.

However, in some situations, it is undesirable to load certain parts of an object up-front. Sometimes, a table in the database will be comprised of many columns, so selecting the extra data -- especially if the returned result set is expected to be large -- can impose a significant overhead. Imagine loading all fields in all Employee objects associated with a large company when generating a report listing all employees. All we really needed might have been employee number and name, so loading the entire object up-front could incur a quite significant amount of unneeded data to be transferred.

To improve upon this situation, the extra fields could be defined to not be in the object's default fetch group. By doing this, the developer is providing a hint to the JDO implementation that the identified data should be lazily loaded, rather than materialized at initialization time. (In the above example, had we explicitly excluded field g from the default fetch group, Kodo would not have loaded the primary key values for this field.)

Kodo JDO's handling of fields implicitly excluded from the default fetch group is a bit more complex when dealing with multiple-table class inheritance hierarchies. As mentioned above, Kodo loads the primary keys for implicitly excluded fields when selecting data from the database. This extra data loading is not performed if the column holding the data is in a table that would not otherwise be selected. That is, we do not add an extra join in order to load this data.

14.5.2. Kodo JDO Fetch Group Behavior

Kodo JDO improves upon the JDO specification's fetch group configuration options by defining a syntax for declaring extra fetch groups in addition to the default fetch group. A field can be a member of zero or one fetch groups, including the default fetch group. That is, fields in the default fetch group cannot be in an additional fetch group, and a field cannot declare itself a member of more than one fetch group.

When loading an object, fields in these custom fetch groups are not included in the initial select statements (unless configured otherwise; see the next paragraph), just as if they had been left out of the default fetch group. Upon lazily loading a field, Kodo checks to see if that field declares itself to be a member of a fetch group. If so, Kodo will load all fields in that fetch group.

Additionally, it is possible to configure a Kodo persistence manager, query, or extent to use a particular fetch group or set of fetch groups when loading new objects, as described later in this chapter. When this is the case, Kodo loads the default fetch group plus any fields in the set of additional fetch groups specified.

So, a custom fetch group configuration for our FetchGroupExample class might look like this:

Example 14.19. Custom Fetch Group Meta-Data

<?xml version="1.0"?>
<jdo>
  <package name="">
    <class name="FetchGroupExample">
      <field name="a" default-fetch-group="true"/>
      <field name="b" default-fetch-group="false">
        <extension vendor-name="kodo" key="fetch-group" value="g1"/>
      </field>
      <field name="c" default-fetch-group="false">
        <extension vendor-name="kodo" key="fetch-group" value="g1"/>
      </field>
      <field name="d" default-fetch-group="false">
        <extension vendor-name="kodo" key="fetch-group" value="g1"/>
      </field>
      <field name="e" default-fetch-group="false">
        <extension vendor-name="kodo" key="fetch-group" value="g2"/>
      </field>
      <field name="f" default-fetch-group="false">
        <extension vendor-name="kodo" key="fetch-group" value="g2"/>
      </field>
      <field name="g"/>
    </class>
  </package>
</jdo>

In this example, fields a and g would be loaded whenever a new object is loaded. (Only the data in column g would be loaded -- the object on the other side of this relation would not be loaded since this field is not in the default fetch group.) When lazily loading field b, fields c and d will also be loaded, because they are all in the same fetch group -- g1.

If the persistence manager were configured to load fetch group g2 when loading new objects, then fields e and f would be loaded along with the default fetch group members at initial load time. Also, if any relations were traversed, then the fetch groups named would be applied to the loading of the related objects.

14.5.3. Custom Fetch Group Configuration

You can control the default set of fetch groups Kodo JDO will load when initializing a persistent object through the kodo.FetchGroups configuration property. Set this property to a comma-separated list of fetch group names.

As mentioned above, it is also possible to override the global FetchGroups property on individual Kodo persistence managers, queries, and extents. All of these components give you access to their internal FetchConfiguration object for controlling object loading behavior. Chapter 10, JDO Runtime Extensions details these interfaces, including the FetchConfiguration in Section 10.5, “Fetch Configuration”.

Example 14.20. Adding a Fetch Group to a Query

import kodo.query.*;

...

KodoQuery kq = (KodoQuery) pm.newQuery (FetchGroupExample.class, "a > 5");
kq.getFetchConfiguration ().addFetchGroup ("g1");
Collection results = (Collection) kq.execute ();

14.5.4. Per-field Fetch Configuration

In addition to controlling fetch configuration on a per-fetch-group basis, you can configure Kodo to include certain particular fields in the current fetch configuration. This allows you to add individual fields that are not in the default fetch group or in any other currently-active fetch groups to the set of fields that will be eagerly loaded from the database. Per-field control of the fetch configuration happens through the FetchConfiguration interface.

Example 14.21. Adding a Single Field to a PersistenceManager

import kodo.runtime.*;

...

KodoPersistenceManager kpm = (KodoPersistenceManager) pm;
kpm.getFetchConfiguration ().addField (Person.class, "firstName");

The field named in the method invocation must be defined in the class specified in the invocation. This applies for inheritance hierarchies. So, if the field firstName is defined in class Person, you must invoke addField (Person.class, "firstName") and not addField (Employee.class, "firstName"). This is stricter than Java's default field-masking algorithms, which would allow the latter method behavior if Employee did not also define a field called firstName

In order to avoid the cost of reflection, Kodo does not perform any validation of the field name / class name pairs that you put into the fetch configuration. If you specify nonexistant class / field pairs, nothing adverse will happen, but you will receive no notification of the fact that the specified configuration is not being used.

The FetchConfiguration interface provides convenience methods to add more than one field at a time and to use a single fully-qualified field name rather than a class and a field name.