7. Fetch Groups
Prev | Chapter 5. Persistent Classes | Next

## 7. Fetch Groups

Fetch groups are sets of fields that load together. They can be used 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 2.7.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, OpenJPA's JPA extensions allow you can add and remove fetch groups at runtime to vary the sets of fields that are eagerly loaded.

### 7.1. Custom Fetch Groups

OpenJPA places any field that is eagerly loaded according to the JPA metadata rules into the built-in default fetch group. As its name implies, the default fetch group is active by default. You may also define your own named fetch groups and activate or deactivate them at runtime, as described later in this chapter. OpenJPA will eagerly-load the fields in all active fetch groups when loading objects from the datastore.

You create fetch groups with the `org.apache.openjpa.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 `org.apache.openjpa.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 OpenJPA: `default` , `values`, `all`, `none`, and any name beginning with `jdo`, `jpa`, or `openjpa`.

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

- `String[] fetchGroups`: Other fetch groups whose fields to include in this group.

As you might expect, listing a `org.apache.openjpa.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.

- `recursionDepth`: If the attribute represents a relation, the maximum number of same-typed relations to eager-fetch from this field. Defaults to 1. For example, consider an `Employee` class with a `manager` field, also of type `Employee`. When we load an `Employee` and the `manager` field is in an active fetch group, the recursion depth (along with the max fetch depth setting, described below) determines whether we only retrieve the target `Employee` and his manager (depth 1), or whether we also retrieve the manager's manager (depth 2), or the manager's manager's manager (depth 3), etc. Use -1 for unlimited depth.

Example 5.19. Custom Fetch Group Metadata

Creates a detail fetch group consisting of the `publisher` and `articles` relations.

    import org.apache.openjpa.persistence.*;

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

A field can be a member of any number of fetch groups. A field can also declare a load fetch group. When you access a lazy-loaded field for the first time, OpenJPA makes a datastore trip to fetch that field's data. Sometimes, however, you know that whenever you access a lazy field A, you're likely to access lazy fields B and C as well. Therefore, it would be more efficient to fetch the data for A, B, and C in the same datastore trip. By setting A's load fetch group to the name of a fetch group containing B and C, you can tell OpenJPA to load all of these fields together when A is first accessed.

Use OpenJPA's `org.apache.openjpa.persistence.LoadFetchGroup` annotation to specify the load fetch group of any persistent field. The value of the annotation is the name of a declared fetch group whose members should be loaded along with the annotated field.

Example 5.20. Load Fetch Group Metadata

    import org.apache.openjpa.persistence.*;

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

       @ManyToOne(fetch=FetchType.LAZY)
       @LoadFetchGroup("detail")
       private Publisher publisher;

       ...
    }

### 7.2. Custom Fetch Group Configuration

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

You can also set the system's default maximum fetch depth with the `openjpa.MaxFetchDepth` configuration property. The maximum fetch depth determines how "deep" into the object graph to traverse when loading an instance. For example, with a `MaxFetchDepth` of 1, OpenJPA will load at most the target instance and its immediate relations. With a `MaxFetchDepth` of 2, OpenJPA may load the target instance, its immediate relations, and the relations of those relations. This works to arbitrary depth. In fact, the default `MaxFetchDepth` value is -1, which symbolizes infinite depth. Under this setting, OpenJPA will fetch configured relations until it reaches the edges of the object graph. Of course, which relation fields are loaded depends on whether the fields are eager or lazy, and on the active fetch groups. A fetch group member's recursion depth may also limit the fetch depth to something less than the configured maximum.

OpenJPA's `OpenJPAEntityManager` and `OpenJPAQuery` extensions to the standard `EntityManager` and `Query` interfaces provide access to a `org.apache.openjpa.persistence.FetchPlan` object. The `FetchPlan` maintains the set of active fetch groups and the maximum fetch depth. It begins with the groups and depth defined in the `openjpa.FetchGroups` and `openjpa.MaxFetchDepth` properties, but allows you to add or remove groups and change the maximum fetch depth 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 removeFetchGrop(String group);
    public FetchPlan removeFetchGroups(String... groups);
    public FetchPlan removeFetchGroups(Collection groups);
    public FetchPlan resetFetchGroups();
    public Collection<String> getFetchGroups();
    public void clearFetchGroups();
    public FetchPlan setMaxFetchDepth(int depth);
    public int getMaxFetchDepth();

Chapter 9, Runtime Extensions details the `OpenJPAEntityManager`, `OpenJPAQuery`, and `FetchPlan` interfaces.

Example 5.21. Using the FetchPlan

    import org.apache.openjpa.persistence.*;

    ...

    OpenJPAQuery kq = OpenJPAPersistence.cast(em.createQuery(...));
    kq.getFetchPlan().setMaxFetchDepth(3).addFetchGroup("detail");
    List results = kq.getResultList();

### 7.3. Per-field Fetch Configuration

In addition to controlling fetch configuration on a per-fetch-group basis, you can configure OpenJPA to include particular fields in the current fetch plan. This allows you to add individual fields that are not in the default fetch group or in any other active fetch groups to the set of fields that will be eagerly loaded from the database.

OpenJPA `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();

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

To include the fields defined in a super class by the subclass or to distinguish between fields that are defined in both super- and subclass, set `setExtendedPathLookup(boolean)` on `FetchPlan` to `true`. By default, this option is set to `false`, to reduce more extensive lookups for predominant use cases.

In order to avoid the cost of reflection, OpenJPA 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.22. Adding an Eager Field

    import org.apache.openjpa.persistence.*;

    ...

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

### 7.4. Implementation Notes

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

- 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, OpenJPA avoids the extra join just to select the foreign key values.

Prev | Up | Next
6. Persistent Fields | Home | 8. Eager Fetching