FileDocCategorySizeDatePackage
BatchFetchQueue.javaAPI DocHibernate 3.2.58510Thu Jun 15 14:41:26 BST 2006org.hibernate.engine

BatchFetchQueue

public class BatchFetchQueue extends Object
Tracks entity and collection keys that are available for batch fetching, and the queries which were used to load entities, which can be re-used as a subquery for loading owned collections.
author
Gavin King

Fields Summary
public static final Object
MARKER
private final Map
batchLoadableEntityKeys
Defines a sequence of {@link EntityKey} elements that are currently elegible for batch-fetching.

Even though this is a map, we only use the keys. A map was chosen in order to utilize a {@link SequencedHashMap} to maintain sequencing as well as uniqueness.

TODO : this would be better as a SequencedReferenceSet, but no such beast exists!

private final Map
subselectsByEntityKey
A map of {@link SubselectFetch subselect-fetch descriptors} keyed by the {@link EntityKey) against which the descriptor is registered.
private final PersistenceContext
context
The owning persistence context.
Constructors Summary
public BatchFetchQueue(PersistenceContext context)
Constructs a queue for the given context.

param
context The owning context.


	            	 
	   
		this.context = context;
	
Methods Summary
public voidaddBatchLoadableEntityKey(EntityKey key)
If an EntityKey represents a batch loadable entity, add it to the queue.

Note that the contract here is such that any key passed in should previously have been been checked for existence within the {@link PersistenceContext}; failure to do so may cause the referenced entity to be included in a batch even though it is already associated with the {@link PersistenceContext}.

		if ( key.isBatchLoadable() ) {
			batchLoadableEntityKeys.put( key, MARKER );
		}
	
public voidaddSubselect(EntityKey key, SubselectFetch subquery)
Adds a subselect fetch decriptor for the given entity key.

param
key The entity for which to register the subselect fetch.
param
subquery The fetch descriptor.

		subselectsByEntityKey.put(key, subquery);
	
public voidclear()
Clears all entries from this fetch queue.

		batchLoadableEntityKeys.clear();
		subselectsByEntityKey.clear();
	
public voidclearSubselects()
Clears all pending subselect fetches from the queue.

Called after flushing.

		subselectsByEntityKey.clear();
	
public java.io.Serializable[]getCollectionBatch(org.hibernate.persister.collection.CollectionPersister collectionPersister, java.io.Serializable id, int batchSize, org.hibernate.EntityMode entityMode)
Get a batch of uninitialized collection keys for a given role

param
collectionPersister The persister for the collection role.
param
id A key that must be included in the batch fetch
param
batchSize the maximum number of keys to return
return
an array of collection keys, of length batchSize (padded with nulls)

		Serializable[] keys = new Serializable[batchSize];
		keys[0] = id;
		int i = 1;
		//int count = 0;
		int end = -1;
		boolean checkForEnd = false;
		// this only works because collection entries are kept in a sequenced
		// map by persistence context (maybe we should do like entities and
		// keep a separate sequences set...)
		Iterator iter = context.getCollectionEntries().entrySet().iterator(); //TODO: calling entrySet on an IdentityMap is SLOW!!
		while ( iter.hasNext() ) {
			Map.Entry me = (Map.Entry) iter.next();

			CollectionEntry ce = (CollectionEntry) me.getValue();
			PersistentCollection collection = (PersistentCollection) me.getKey();
			if ( !collection.wasInitialized() && ce.getLoadedPersister() == collectionPersister ) {

				if ( checkForEnd && i == end ) {
					return keys; //the first key found after the given key
				}

				//if ( end == -1 && count > batchSize*10 ) return keys; //try out ten batches, max

				final boolean isEqual = collectionPersister.getKeyType().isEqual(
						id,
						ce.getLoadedKey(),
						entityMode,
						collectionPersister.getFactory()
				);

				if ( isEqual ) {
					end = i;
					//checkForEnd = false;
				}
				else if ( !isCached( ce.getLoadedKey(), collectionPersister, entityMode ) ) {
					keys[i++] = ce.getLoadedKey();
					//count++;
				}

				if ( i == batchSize ) {
					i = 1; //end of array, start filling again from start
					if ( end != -1 ) {
						checkForEnd = true;
					}
				}
			}

		}
		return keys; //we ran out of keys to try
	
public java.io.Serializable[]getEntityBatch(org.hibernate.persister.entity.EntityPersister persister, java.io.Serializable id, int batchSize, org.hibernate.EntityMode entityMode)
Get a batch of unloaded identifiers for this class, using a slightly complex algorithm that tries to grab keys registered immediately after the given key.

param
persister The persister for the entities being loaded.
param
id The identifier of the entity currently demanding load.
param
batchSize The maximum number of keys to return
return
an array of identifiers, of length batchSize (possibly padded with nulls)

		Serializable[] ids = new Serializable[batchSize];
		ids[0] = id; //first element of array is reserved for the actual instance we are loading!
		int i = 1;
		int end = -1;
		boolean checkForEnd = false;

		Iterator iter = batchLoadableEntityKeys.keySet().iterator();
		while ( iter.hasNext() ) {
			EntityKey key = (EntityKey) iter.next();
			if ( key.getEntityName().equals( persister.getEntityName() ) ) { //TODO: this needn't exclude subclasses...
				if ( checkForEnd && i == end ) {
					//the first id found after the given id
					return ids;
				}
				if ( persister.getIdentifierType().isEqual( id, key.getIdentifier(), entityMode ) ) {
					end = i;
				}
				else {
					if ( !isCached( key, persister, entityMode ) ) {
						ids[i++] = key.getIdentifier();
					}
				}
				if ( i == batchSize ) {
					i = 1; //end of array, start filling again from start
					if (end!=-1) checkForEnd = true;
				}
			}
		}
		return ids; //we ran out of ids to try
	
public SubselectFetchgetSubselect(EntityKey key)
Retrieve the fetch descriptor associated with the given entity key.

param
key The entity key for which to locate any defined subselect fetch.
return
The fetch descriptor; may return null if no subselect fetch queued for this entity key.

		return (SubselectFetch) subselectsByEntityKey.get(key);
	
private booleanisCached(EntityKey entityKey, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.EntityMode entityMode)

		if ( persister.hasCache() ) {
			CacheKey key = new CacheKey(
					entityKey.getIdentifier(),
					persister.getIdentifierType(),
					entityKey.getEntityName(),
					entityMode,
					context.getSession().getFactory()
			);
			return persister.getCache().getCache().get( key ) != null;
		}
		return false;
	
private booleanisCached(java.io.Serializable collectionKey, org.hibernate.persister.collection.CollectionPersister persister, org.hibernate.EntityMode entityMode)

		if ( persister.hasCache() ) {
			CacheKey cacheKey = new CacheKey(
					collectionKey,
			        persister.getKeyType(),
			        persister.getRole(),
			        entityMode,
			        context.getSession().getFactory()
			);
			return persister.getCache().getCache().get( cacheKey ) != null;
		}
		return false;
	
public voidremoveBatchLoadableEntityKey(EntityKey key)
After evicting or deleting or loading an entity, we don't need to batch fetch it anymore, remove it from the queue if necessary

		if ( key.isBatchLoadable() ) batchLoadableEntityKeys.remove(key);
	
public voidremoveSubselect(EntityKey key)
After evicting or deleting an entity, we don't need to know the query that was used to load it anymore (don't call this after loading the entity, since we might still need to load its collections)

		subselectsByEntityKey.remove(key);