5.6. Fetch Groups

5.6.1. Custom Fetch Groups
5.6.2. Custom Fetch Group Configuration
5.6.3. Per-field Fetch Configuration
5.6.4. Implementation Notes

Fetch groups are sets of fields that load together. They can be used to to pool together associated fields in order to provide performance improvements over standard data fetching. Specifying fetch groups allows for tuning of lazy loading and eager fetching behavior.

The JPA Overview's Section 5.2.6.1, “Fetch Type” describes how to use JPA metadata annotations to control whether a field is fetched eagerly or lazily. Fetch groups add a dynamic aspect to this standard ability. As you will see, Kodo's JPA extensions allow you can add and remove fetch groups at runtime to vary the sets of fields that are eagerly loaded.

The JDO Overview discusses standard JDO fetch group declarations in Section 5.5, “Fetch Group Element”. Chapter 12, FetchPlan covers runtime fetch group behavior.

5.6.1. Custom Fetch Groups

Fetch groups are a standard feature of JDO, but are not part of the JPA specification. This section describes Kodo's JPA fetch group implementation.

Kodo places any field that is eagerly loaded according to the JPA metadata rules into the built-in default fetch group. The default fetch group is always active; you cannot remove it at runtime. Thus fields in this group are always loaded immediately when an object is fetched from the datastore.

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. So to place a field in a custom fetch group, you must first exclude it from eager fetching in your JPA metadata, if it does not already default to lazy loading.

When lazy-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 EntityManager or Query 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 specified set of additional fetch groups.

You create fetch groups with the kodo.persistence.FetchGroup annotation. If your class only has one custom fetch group, you can place this annotation directly on the class declaration. Otherwise, use the kodo.persistence.FetchGroups annotation to declare an array of individual FetchGroup values. The FetchGroup annotation has the following properties:

  • String name: The name of the fetch group. Fetch group names are global, and are expected to be shared among classes. For example, a shopping website may use a detail fetch group in each product class to efficiently load all the data needed to display a product's "detail" page. The website might also define a sparse list fetch group containing only the fields needed to display a table of products, as in a search result.

    The following names are reserved for use by Kodo: default, values, all, none, and any name beginning with jdo, ejb, or kodo.

  • FetchAttribute[] attributes: The set of persistent fields or properties in the fetch group.

As you might expect, listing a kodo.persistence.FetchAttribute within a FetchGroup includes the corresponding persistent field or property in the fetch group. Each FetchAttribute has the following properties:

  • String name: The name of the persistent field or property to include in the fetch group.

  • depth: If the attribute represents a relation, the depth to which to recurse. The current fetch group will be applied to the related object when fetching it, and so on until the depth is exhausted or the related object has no relations in the current fetch group. Under the default depth of 1, the related object will be fetched, but none of its relations will be traversed, even if they are in the current fetch group. With a depth of 2, the related object will be fetched, and if it has any relations in the current fetch group, those will be fetched with a depth of 1. A depth of 0 indicates that the recursion continues until the graph is exhausted or a related object has no relations in the current fetch group.

Thus, to create a detail fetch group consisting of the publisher and articles relations, with the fetch group applied recursively to the related objects, use:

Example 5.16. Custom Fetch Group Metadata

import kodo.persistence.*;

@Entity
@FetchGroups({
    @FetchGroup(name="detail", attributes={
        @FetchAttribute(name="publisher" depth=0),
        @FetchAttribute(name="articles" depth=0)
    }),
    ...
})
public class Magazine
{
   ...
}
[Note]Note

Kodo currently only supports a depth of 0 for fetch attributes. This restriction will be lifted in a future release, along with the restriction limiting each attribute to a single fetch group.

5.6.2. Custom Fetch Group Configuration

You can control the default set of fetch groups with the kodo.FetchGroups configuration property. Set this property to a comma-separated list of fetch group names.

In JPA, Kodo's KodoEntityManager and KodoQuery extensions to the standard EntityManager and Query interfaces provide access to a kodo.persistence.FetchPlan object. The FetchPlan maintains the set of active fetch groups. It begins with the groups defined in the kodo.FetchGroups property, but allows you to add and remove groups for an individual EntityManager or Query through the methods below.

public FetchPlan addFetchGroup (String group);
public FetchPlan addFetchGroups (String... groups);
public FetchPlan addFetchGroups (Collection groups);
public FetchPlan removeFetchGroup (String group);
public FetchPlan removeFetchGroups (String... groups);
public FetchPlan removeFetchGroups (Collection groups);
public FetchPlan resetFetchGroups ();
public Collection<String> getFetchGroups (); 
public void clearFetchGroups ();

Chapter 9, Runtime Extensions details the KodoEntityManager, KodoQuery, and FetchPlan interfaces.

Example 5.17. Using the FetchPlan

import kodo.persistence.*;

...

KodoQuery kq = KodoPersistence.cast (em.createQuery (...));
kq.getFetchPlan ().addFetchGroup ("detail");
List results = kq.getResultList ();

In JDO, you can override the global FetchGroups property at runtime using the FetchPlan of individual PersistenceManagers, Queries, and Extents. See Chapter 12, FetchPlan for details. The extended KodoFetchPlan interface adds an additional useful methods for manipulating fetch groups:

public void resetGroups ();

resetGroups resets the active fetch group set to the groups defined in the kodo.FetchGroups configuration property. Chapter 9, Runtime Extensions provides more information on Kodo extensions to standard JDO interfaces.

5.6.3. Per-field Fetch Configuration

In addition to controlling fetch configuration on a per-fetch-group basis, you can configure Kodo to include 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.

JPA FetchPlan methods:

public FetchPlan addField (String field);
public FetchPlan addFields (String... fields);
public FetchPlan addFields (Class cls, String... fields);
public FetchPlan addFields (Collection fields);
public FetchPlan addFields (Class cls, Collection fields);
public FetchPlan removeField (String field);
public FetchPlan removeFields (String... fields);
public FetchPlan removeFields (Class cls, String... fields);
public FetchPlan removeFields (Collection fields);
public FetchPlan removeFields (Class cls, Collection fields);
public Collection<String> getFields (); 
public void clearFields ();

JDO KodoFetchPlan methods:

public KodoFetchPlan setFields (String[] fields);
public KodoFetchPlan setFields (Class cls, String[] fields);
public KodoFetchPlan setFields (Collection fields);
public KodoFetchPlan setFields (Class cls, Collection fields);
public KodoFetchPlan addField (String field);
public KodoFetchPlan addField (Class cls, String field);
public KodoFetchPlan removeField (String field);
public KodoFetchPlan removeField (Class cls, String field);
public boolean hasField (Class cls, String field);
public Collection getFields (); 
public KodoFetchPlan clearFields ();

The methods that take only string arguments use the fully-qualified field name, such as org.mag.Magazine.publisher. Similarly, getFields returns the set of fully-qualified field names. In all methods, the named field must be defined in the class specified in the invocation, not a superclass. So, if the field publisher is defined in base class Publication rather than subclass Magazine, you must invoke addField (Publication.class, "publisher") and not addField (Magazine.class, "publisher"). This is stricter than Java's default field-masking algorithms, which would allow the latter method behavior if Magazine did not also define a field called publisher.

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 non-existent class / field pairs, nothing adverse will happen, but you will receive no notification of the fact that the specified configuration is not being used.

Example 5.18. Adding an Eager Field

JPA:

import kodo.persistence.*;

...

KodoEntityManager kem = KodoPersistence.cast (em);
kem.getFetchPlan ().addField (Magazine.class, "publisher");
Magazine mag = em.find (Magazine.class, magId);

JDO:

import kodo.jdo.*;

...

PersistenceManager pm = ...;
KodoFetchPlan fetch = (KodoFetchPlan) pm.getFetchPlan ();
fetch.addField (Magazine.class, "publisher");
Magazine mag = (Magazine) pm.getObjectById (magId);

5.6.4. Implementation Notes

  • JPA defaults to eagerly loading direct relations to other persistence-capable objects. JDO defaults to lazily loading direct these relations. Kodo respects the defaults of the metadata format you choose to use. If you use JPA annotations for metadata, Kodo will include @OneToOne and @ManyToOne relations in the default fetch group unless you explicitly set fetch=FetchType.LAZY. If you use JDO metadata, Kodo will exclude direct relations from the default fetch group unless you explicitly set default-fetch-group="true".

  • Even when a direct relation is not eagerly fetched, Kodo selects the foreign key columns and caches the values. This way when you do traverse the relation, Kodo can often find the related object in its cache, or at least avoid joins when loading the related object from the database.

    In JDO, you can force Kodo not to select the foreign key values by explicitly setting default-fetch-group="false" for the field in your JDO metadata.

  • The above implicit foreign key-selecting behavior does not always apply when the relation is in a subclass table. If the subclass table would not otherwise be joined into the select, Kodo avoids the extra join just to select the foreign key values.

 

Skip navigation bar   Back to Top