An extent provides you with access to all the persistent instances of a class and, optionally, its subclasses. You can iterate over the elements of the extent or perform a query on the extent. The JDO Extent interface represents the extent of a class. Later in this chapter, we will discuss the IgnoreCache flag, which controls whether instances made persistent or deleted during the current transaction are contained in the extent.
You control whether an extent is maintained for a class in the metadata. You use the metadata class element's requires-extent attribute to indicate whether the persistent class has an extent. It has a default value of "true".
If your application does not need to iterate over the instances of a class or perform a query on the extent, you can set the requires-extent attribute to "false" explicitly. Even if a class does not have an extent, you can still make instances persistent, establish references to them, and navigate to them in your application and queries.
JDO 1.0.1 requires that if a class has a requires-extent set to "true", none of its subclasses can set requires-extent to "false". If your application specifies the subclass's parameter to be true when calling the getExtent( ) method for a base class, all subclass instances are included in the iteration of the extent.
You access the Extent associated with a class by calling the following PersistenceManager method:
Extent getExtent(Class persistentClass, boolean subclasses);
It returns an Extent that contains all the instances in the class specified by the persistentClass parameter and all the instances of its subclasses, if the subclasses parameter is true. If the class identified by the persistentClass parameter does not have an extent, a JDOUserException is thrown. This occurs only if the metadata for the class has the requires-extent attribute set to "false".
The Extent interface has methods you can use to access the components that were used initially to construct the Extent:
PersistenceManager getPersistenceManager( ); Class getCandidateClass( ); boolean hasSubclasses( );
An Extent is not a Java collection instance that has all the instances of the class populated in memory. This is a common misunderstanding. Common Collection behaviors are not possible. For example, you cannot determine whether one Extent contains another, the size of the Extent, or whether the Extent contains a specific instance. Such operations are performed by executing a query against the Extent. An Extent instance is logically a holder of the following information:
The class of the instances in the Extent
Whether subclasses are part of the Extent
A collection of active iterators over the Extent
No datastore action is taken when you construct an Extent. The contents of the Extent are accessed when a query is executed or you use an Iterator to iterate over its elements. An Extent is often used as a parameter to a Query instance. When you perform a query on an Extent, the Extent is used only to identify the prospective datastore instances; its elements are typically not instantiated in the JVM. Chapter 9 covers queries in detail.
You call the following Extent method to acquire an Iterator to iterate over all the instances in the Extent:
Iterator iterator( );
You can call iterator( ) multiple times to construct multiple Iterator instances that can iterate over the extent independently. Extent does not provide any other Collection methods. If you call any mutating Iterator method, including remove( ) , an UnsupportedOperationException is thrown. If you have already accessed a specific instance in the Extent and it is in memory, it is returned. This instance also contains any updates you may have made to it.
An Extent can have a very large number of instances. It might be common for you to iterate over the elements of an Extent. Extents are supposed to be implemented such that you do not get out-of-memory conditions during iteration. If your application does have limitations on the number of instances that can reside in memory, Chapter 13 describes the ability to evict instances from the cache as a means of limiting memory growth.
When you have finished using an extent Iterator, you should close it to free all its associated resources. You can call the following Extent method to close an Iterator acquired from the Extent:
void close(Iterator iterator);
After this call, the Iterator returns false to hasNext( ) and throws NoSuchElementException if next( ) is called. The Extent itself can still be used to acquire other iterators and perform queries. You can also call the following Extent method to close all of the iterators acquired from the Extent:
void closeAll( );
The following program demonstrates the use of an Extent. It accesses the MediaContent extent on line [1] and acquires an Iterator on line [2]. It then iterates through the extent, accessing each MediaContent instance on line [3].
package com.mediamania.store; import java.util.Iterator; import javax.jdo.PersistenceManager; import javax.jdo.Extent; import com.mediamania.MediaManiaApp; import com.mediamania.content.MediaContent; public class GetMediaContent extends MediaManiaApp { public static void main(String[] args) { GetMediaContent content = new GetMediaContent( ); content.executeTransaction( ); } public void execute( ) { Extent mediaExtent = pm.getExtent(MediaContent.class, true); [1] Iterator iter = mediaExtent.iterator( ); [2] while (iter.hasNext( )) { MediaContent media = (MediaContent) iter.next( ); [3] System.out.println(media.getDescription( )); } } }
The IgnoreCache flag in the PersistenceManager controls whether instances made persistent or deleted in the current transaction are included during Extent iteration or queries. We cover the effect of IgnoreCache on queries in Chapter 9. If you have set the IgnoreCache flag to false, an implementation that performs queries in the datastore server will need to flush the instances in the application cache to the datastore, so their currently cached state can be reflected in the query result. You can set IgnoreCache to true as a performance-optimizing hint, so the implementation can avoid flushing the cache when a query is executed or an Extent is iterated.
You can use the following PersistenceManager methods to get and set the IgnoreCache flag associated with a PersistenceManager:
boolean getIgnoreCache( ); void setIgnoreCache(boolean flag);
The IgnoreCache flag affects the extent Iterators for all Extents obtained from the PersistenceManager.
If you have the IgnoreCache flag set to false in the PersistenceManager when you call iterator( ) to obtain an Iterator instance from an Extent, then:
The Iterator will return instances that were made persistent in the transaction prior to calling iterator( ).
The Iterator will not return instances deleted in the transaction prior to the call to iterator( ).
Setting the IgnoreCache flag to true is only a hint that the Extent can return approximate results by ignoring persistent instances that have been added, modified, or deleted in the current transaction. If IgnoreCache is set to true in the PersistenceManager when an Iterator is obtained, new and deleted instances in the current transaction might be ignored by the Iterator, but it is at the option of the implementation. That is, new instances might not be returned, and deleted instances might be returned. Iterating an Extent with IgnoreCache set to true can differ among implementations. Therefore, to be portable you should set the IgnoreCache flag to false.