db-jdo-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Craig L Russell <Craig.Russ...@Sun.COM>
Subject Re: [IMPORTANT] Fetch-depth
Date Thu, 26 Jan 2006 17:18:35 GMT

Here's the latest work in progress. I've incorporated all the  
feedback received from the previous discussion.


Fetch Plan
A fetch plan defines rules for instantiating the loaded state for an  
object graph. It specifies fields to be loaded for all of the  
instances in the graph. Using fetch plans, users can control the  
field fetching behavior of many JDO APIs. A fetch plan can be  
associated with a PersistenceManager and, independently, with a Query  
and with an Extent.
A fetch plan also defines rules for creating the detached object  
graph for the detach APIs and for automatic detachment at commit with  
DetachAllOnCommit set to true.
A fetch plan consists of a number of fetch groups that are combined  
additively for each affected class; a fetch size that governs the  
number of instances of multi-valued fields retrieved by queries; a  
fetch-depth per field that governs the depth of the object graph  
fetched for that field; and flags that govern the behavior of  
The default fetch plan contains exactly one fetch group, "default".  
It has a fetch size of 0, and detachment option DETACH_LOAD_FIELDS.  
The default fetch plan is in effect when the PersistenceManager is  
first acquired from the PersistenceManagerFactory.
With the default fetch plan in effect, the behavior of JDO 2 is very  
similar to the behavior of JDO 1. That is, when instances are loaded  
into memory in response to queries or navigation, fields in the  
default fetch group are loaded, and the jdoPostLoad callback is  
executed the first time an instance is fetched from the datastore.  
The implementation is allowed to load additional fields, as in JDO 1.  
Upon detachment, fields that are have been loaded into the detached  
instances are preserved, regardless of whether they were loaded  
automatically by the implementation or loaded in response to  
application access; and fields that have not been loaded are marked  
in the detached instances as not loaded.
This behavior is sufficient for the most basic use case for  
detachment, where the detached instances are simply “data transfer  
objects” containing primitive fields. The detached instances can be  
modified in place or serialized and sent to another tier to be  
changed and sent back. Upon being received back, the instances can be  
attached and if there are no version conflicts, the changes can be  
applied to the datastore.
The most common use case for fetch groups is to restrict the fields  
loaded for an instance to the primitive values and avoid loading  
related instances for queries. For more control over the default  
behavior, the “default” fetch group can simply be redefined for  
specific classes. For example, a String field that contains a  
typically large document can be defined as not part of the default  
fetch group, and the field will be loaded only when accessed by the  
application. Similarly, the Order field associated with OrderLine  
might be defined as part of the default fetch group of OrderLine, and  
queries on OrderLine will always load the corresponding Order  
instance as well. This can easily improve the performance of  
applications that always need the Order whenever OrderLine instances  
are loaded.
For explicit detachment, the parameters of the detach method are each  
treated as roots for the purpose of determining the detached object  
graph. The fetch plan is applied to each of the roots as if no other  
roots were also being detached. The roots and their corresponding  
object graphs are combined and the resulting object graph is detached  
in its entirety.
Fetch Groups
Fetch groups are used to identify the list of fields and the  
associated field recursion-depth for each class for which the fetch  
plan is applied.
Fetch groups are identified by name and apply to one or more classes.  
Names have global scope so the same fetch group name can be used for  
any number of classes. This makes it possible to specify fetch groups  
per PersistenceManager instead of per extent. This greatly simplifies  
the use of fetch groups in an application.
The default fetch group (named "default") for each class is created  
by the JDO implementation according to the rules in the JDO 1.0.1  
specification. That is, it includes all fields that by default belong  
to the default fetch group (i.e. single-valued fields), and causes  
the jdoPostLoad method to be called the first time fields are loaded.  
It may also be defined by the user in the metadata like any other  
fetch group, in order to make use of JDO 2 features.
The implementation must also define another fetch group named "all"  
for each class. The "all" group contains all fields in the class, but  
can be redefined by the user, for example to add fetch-depth to  
certain fields, or to exclude some fields from being loaded.
If a fetch plan other than the default fetch plan is active for a  
PersistenceManager, the behavior of several APIs changes:
For detachCopy the JDO implementation must ensure that the graph  
specified by the active fetch groups is copied, based on the  
For refresh, after clearing fields in the instances, the JDO  
implementation uses the fetch plan to determine which fields to load  
from the datastore.
For retrieve with FGonly true, the implementation uses the fetch plan  
to determine which fields are loaded from the datastore. With FGonly  
false, the implementation reverts to JDO 1 behavior, which loads all  
fields from the datastore; in this case, no related instances are  
When executing a query the JDO implementation loads the fields as  
specified in the fetch plan associated with the Query instance.
When the application dereferences an unloaded field, the JDO  
implementation uses the current fetch plan and the load-fetch-group  
of the field to create the fetch strategy for the field. The specific  
behavior depends on whether the unloaded field is a relation to  
another persistence-capable class.
for non-relation fields, the current fetch plan is applied to the  
field’s owning instance, and the fields in the field’s load-fetch- 
group, plus the field itself are added to the list of fields.
for relation fields, the fields in the owning instance are fetched as  
immediately above, and additionally the instances referred by the  
field are loaded using the current fetch plan plus the field’s load- 
A12.7-3 [FetchPlan getFetchPlan();
This method retrieves the fetch plan associated with the  
PersistenceManager. It always returns the identical instance for the  
same PersistenceManager.]
When relationship fields are included in the active fetch plan, it  
may be possible to retrieve the entire contents of the datastore,  
which might not be the desired effect. To avoid this behavior, and to  
allow the application to control the amount of data retrieved from  
the datastore, the MaxFetchDepth property of the fetch plan is used.  
The MaxFetchDepth is the depth of references (fields of relationship  
types) to instantiate, starting with the root instances.
Setting MaxFetchDepth to 1 limits the instances retrieved to the root  
instances and instances directly reachable from the root instances  
through a field in the fetch plan for the root class(es). Setting  
MaxFetchDepth to 0 has no meaning, and JDOUserException will be  
thrown. Setting MaxFetchDepth to -1 does not limit the instances  
retrieved via relationship fields in the fetch plan. Caution should  
be exercised to avoid retrieving more instances than desired.
For example, assume the class Employee defines field dept of type  
Department, and class Department defines field comp of type Company.  
When a query for Employee is executed, with a fetch plan that  
includes Employee.dept and Department.comp and with MaxFetchDepth set  
to 1, the Departments referenced by Employees returned from the query  
are instantiated, but the Company field is not instantiated. With the  
MaxFetchDepth set to 2, Departments and their corresponding Companys  
are instantiated for the Employee instances returned by the query.
Root instances
Root instances are parameter instances for retrieve, detachCopy, and  
refresh; result instances for queries. Root instances for  
DetachAllOnCommit are defined explicitly by the user via the  
FetchPlan property DetachmentRoots or DetachmentRootClasses. If not  
set explicitly, the detachment roots consist of the union of all root  
instances of methods executed since the last commit or rollback. Once  
set explicitly, the detachment roots will not be changed until  
commit, at which time the detachment roots will be set to the empty  
For object models with bidirectional relationships or self- 
referencing relationships, it is useful to limit the depth of the  
object graph retrieved through these relationships recursively. The  
recursion-depth attribute of the field element is used for this  
purpose. The recursion-depth for a relationship field specifies the  
number of times an instance of the same class, subclass, or  
superclass can be fetched via traversing this field.
A value of -1 means that the recursion-depth is not limited by  
traversing this field. If a field is defined in multiple fetch  
groups, the recursion-depth is the largest of the values specified,  
treating -1 as a very large positive number. If not specified in any  
fetch group or in the base field definition, the default is 1.
For example, assume a class Directory with a field parent of type  
Directory and a field children of type Set<Directory>, and assume the  
recursion-depth of the parent field is set to -1 and the recursion- 
depth of the children field is set to 2. When a query for a Directory  
is executed, all parents of the selected Directory instances will be  
retrieved, and all of the parents’ parents until a parent is found  
with a null parent. Additionally, all children of the selected  
Directory will be retrieved and all children of the children of the  
selected Directory.
The FetchPlan interface
Fetch groups are activated using methods on the interface FetchPlan.  
PersistenceManager and Query have getFetchPlan() methods.  When a  
Query is retrieved from a PersistenceManager, its FetchPlan is  
initialized to the same settings as that of the PersistenceManager.   
Subsequent modifications of the Query FetchPlan are not reflected in  
the FetchPlan of the PersistenceManager. When an Extent is created,  
the FetchPlan of the PersistenceManager initializes the FetchPlan for  
the Extent.
Mutating FetchPlan methods return the FetchPlan instance to allow  
method chaining.
package javax.jdo;
public interface FetchPlan {
String DEFAULT = “default”;
String ALL = “all”;
A12.7.1-1 [/** Add the fetchgroup to the set of active fetch groups.  
Duplicate names will be removed.*/
FetchPlan addGroup(String fetchGroupName);
/** Remove the fetch group from the set active fetch groups. */
FetchPlan removeGroup(String fetchGroupName);
/** Remove all active groups, including the default fetch group. */
FetchPlan clearGroups();
/** Return an immutable Set of the names of all active fetch groups. */
Set getGroups();
/** Set a Collection of group names to replace the current groups.  
Duplicate names will be removed.*/
FetchPlan setGroups(Collection fetchGroupNames);
/** Set an array of group names to replace the current groups.  
Duplicate names will be removed.*/
FetchPlan setGroups(String[] fetchGroupNames);
/** Set a single group to replace the current groups. */
FetchPlan setGroup(String fetchGroupName);]
/** Set the roots for DetachAllOnCommit */
FetchPlan setDetachmentRoots(Collection roots);
/** Get the roots for DetachAllOnCommit */
Collection getDetachmentRoots();
/** Set the roots for DetachAllOnCommit */
FetchPlan setDetachmentRootClasses(Class[] rootClasses);
/** Get the roots for DetachAllOnCommit */
Class[] getDetachmentRootClasses();
A12.7.1-2 [/** Set the fetch size for large result set support. */
FetchPlan setFetchSize(int fetchSize);
/** Return the fetch size; 0 if not set; -1 for greedy fetching. */
int getFetchSize();]
A12.7.1-3 [/** Set detachment options */
FetchPlan setDetachmentOptions(int options);
/** Return the detachment options */
int getDetachmentOptions();]
The getGroups method returns a collection of names. After a call to  
clearGroups() this method returns an empty Set. It is legal to remove  
the default fetch group explicitly via pm.getFetchPlan().removeGroup 
("default"), or to use setGroups() with a collection that does not  
contain "default". This makes it possible to have only a given fetch  
group active without the default fetch group. If no fetch groups are  
active then a Set with no elements is returned. In this case, loading  
an instance might not result in loading the default fetch group  
fields and the jdoPostLoad method will only be called if there is an  
active fetch group that declares post-load=”true”.
The fetch size allows users to explicitly control the number of  
instances retrieved from queries. A positive value is the number of  
result instances to be fetched. A value of FETCH_SIZE_GREEDY  
indicates that all results should be obtained immediately. A value of  
FETCH_SIZE_OPTIMAL indicates that the JDO implementation should try  
to optimize the fetching of results.
Note that the graph and fields specified by a FetchPlan is strictly  
the union of all the active fetch groups not based on any complicated  
set mathematics. So, if a field f1 is in fetch groups A and B, and  
both A and B are added to the FetchPlan,and subsequently B is removed  
from the active fetch groups and the instance is loaded, then the  
field f1 will be loaded, because it is in fetch group A.

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!

View raw message