12.3 Retrieval of Fields

You should not be concerned about how and when the JDO implementation accesses fields from the datastore. When you access a field, the JDO implementation provides the field's value. But some facilities let you instruct the JDO implementation to load all or a particular subset of fields of an instance together. You can analyze your application's field-access requirements and optimize the performance of accessing fields from the datastore.

12.3.1 Default Fetch Group

A fetch group is a group of fields retrieved together from the datastore. JDO implementations usually can retrieve a group of fields as a unit more efficiently than they can retrieve each field individually. In addition, you may have a specific subset of fields that your applications always use together; in this case, accessing these fields as a unit may be more efficient. Conversely, fields that are rarely accessed could be placed in a separate fetch group that is retrieved only when necessary. When fields that are not contained in any fetch group are accessed, they can be retrieved from the datastore individually.

JDO defines one fetch group, called the default fetch group (DFG). A field element's default-fetch-group attribute specifies whether a field should be in the default fetch group. This attribute defaults to "true" for nonkey fields of the following types:

  • Primitive types

  • java.util.Date

  • Fields in the java.lang package of the types listed in Table 4-2

  • java.math.BigDecimal and java.math.BigInteger

An instance in the hollow state does not have its default fetch group fields loaded, but they get loaded when the instance transitions to persistent-clean or persistent-dirty.

The default fetch group can only contain persistent fields, so you cannot set the default-fetch-group attribute to "true" for fields whose persistence-modifier is "transactional" or "none". You cannot place a primary-key field in the default fetch group; a primary-key field is always loaded in an instance. When an instance is first instantiated from the datastore and placed in the hollow state, the primary-key fields are set. Since they uniquely identify an instance in the datastore, they are used to fetch the other field values when they are needed.

In fact, the following field-level metadata declarations are mutually exclusive; only one can be specified:

  • default-fetch-group = "true"

  • primary-key = "true"

  • persistence-modifier = "transactional"

  • persistence-modifier = "none"

An implementation can support other fetch groups in addition to the default fetch group. A class can have multiple fetch groups, which you must specify in the metadata using vendor-specific metadata extensions. Such additional fetch groups allow you to partition a class's fields into separate groups that should be processed as distinct units.

12.3.2 Retrieving All Fields

In some situations, you need to fetch all the field values for one or more instances from the datastore. For example, when you execute a query, a Collection is returned that you can iterate through to access each of its elements. The instances in the query result might not be fetched from the datastore. It will probably be more efficient to access them from the datastore as a group, rather than individually.

You can call the following PersistenceManager methods to make sure that all of the persistent fields have been loaded into the parameter instances:

void retrieve(Object obj);
void retrieveAll(Collection objs);
void retrieveAll(Object[] objs);

These methods do not read and set any fields that have been modified in the transaction; any updates you may have made to fields will not be lost. Furthermore, if an instance in the persistent-dirty state is passed to retrieve( ) or retrieveAll( ), it will be persistent-dirty upon return. These retrieve( ) and retrieveAll( ) methods load all of the fields that have not been loaded already.

Suppose you want to load only the fields in the default fetch group. You can do so by calling one of the following methods, passing true for the DFGonly parameter:

void retrieveAll(Collection objs, boolean DFGonly);
void retrieveAll(Object[] objs, boolean DFGonly);

This tells the JDO implementation that you need to retrieve only the fields in the default fetch group. After you call this method, if you access any of the default fetch group fields of the parameter instances, the implementation will not need to access the datastore to retrieve the field value. Passing a value of false for the DFGonly parameter is equivalent to calling retrieve( ) or retrieveAll( ) without the DFGonly parameter. Since these methods are just a hint, the implementation may still retrieve all the fields, regardless of the DFGonly parameter value. You may notice that there is no method named retrieve( ) that accepts the DFGonly parameter. We omitted this deliberately, because in most of the cases where you want to retrieve only the fields in the default fetch group, you have a collection of instances.

Using the retrieveAll( ) methods with the DFGonly parameter optimizes performance in applications that need to retrieve a large number of instances in the cache, when you need only the fields in the default fetch group and do not want to incur the overhead of retrieving all the fields. A common example is passing a partial result (e.g., the first 10 instances of the query result) of a JDOQL query to retrieveAll( ) with a value of "true" for DFGonly.

Figure 12-1 illustrates the state transitions that occur when you call these methods. In addition, jdoPostLoad( ) is called if the instance's class implements the InstanceCallbacks interface. We cover the InstanceCallbacks interface later in this chapter.

Figure 12-1. State transitions when retrieve methods are called in a datastore transaction

If you call retrieve( ) for an instance that contains references to other persistent instances, the references are initialized to refer to the related instances. The referenced instances must be instantiated in the cache, if they are not already resident in the cache. They may be in the hollow state; their fields do not need to be fetched.

Some implementations support a preread policy that you can use to instruct the JDO implementation to fetch the field values of related instances when an instance is accessed. You usually specify preread policies with vendor-specific metadata, since JDO 1.0.1 does not specify them. The JDO expert group is considering this as a possible feature in JDO 2.0.

12.3.3 The Management of Fields

The JDO implementation completely controls whether the fields of a persistent instance are fetched from the datastore. During enhancement, the jdoFlags field is added to a persistent class to indicate the state of the default fetch group. The value of the jdoFlags field directly affects the behavior of default-fetch-group field accesses.

An implementation can choose from a variety of field-management strategies:

  • Never cache any field values in an instance, but fetch a field's value each time it is accessed by the application.

  • Selectively fetch and cache the values of specific fields in the instance.

  • Fetch the values for all the fields in the default fetch group at one time, taking advantage of this performance optimization when managing the instance.

  • Manage updates to fields in the default fetch group individually. This results in the instance always delegating field changes to the PersistenceManager. With this strategy, the PersistenceManager can reliably tell when any field changes, and it can optimize the writing of data to the datastore.

Your application is insulated from the specific techniques an implementation uses to manage fields. Class enhancement makes your application binary-compatible across all implementations, with an interface that gives implementations a lot of flexibility in how they manage fields. Be aware that each implementation employs one or more field-management strategies that can affect the performance of your application.