FileDocCategorySizeDatePackage
FetchGroupManager.javaAPI DocGlassfish v2 API18504Tue May 22 16:54:16 BST 2007oracle.toplink.essentials.descriptors

FetchGroupManager

public class FetchGroupManager extends Object

Purpose: The fetch group manager controls the named fetch groups defined at the descriptor level. TopLink supports multiple, overlapped fetch groups, optionally with one of them as the default fetch group. The domain object must implement oracle.toplink.essentials.queryframework.FetchGroupTracker interface, in order to make use of the fetch group performance enhancement feature. Please refer to FetchGroup class for the prons and cons of fetch group usage.

see
oracle.toplink.essentials.queryframework.FetchGroup
see
oracle.toplink.essentials.queryframework.FetchGroupTracker
author
King Wang
since
TopLink 10.1.3.

Fields Summary
private Map
fetchGroups
private FetchGroup
defaultFetchGroup
private ClassDescriptor
descriptor
Constructors Summary
public FetchGroupManager()
Constructor


          
      
    
Methods Summary
public voidaddFetchGroup(oracle.toplink.essentials.queryframework.FetchGroup group)
Add a named fetch group to the descriptor

        //create a new fetch group and put it in the group map.
        getFetchGroups().put(group.getName(), group);
    
public java.lang.Objectclone()
INTERNAL: Clone the fetch group manager

        Object object = null;
        try {
            object = super.clone();
        } catch (Exception exception) {
            ;
        }
        return object;
    
public voidcopyFetchGroupInto(java.lang.Object source, java.lang.Object target)
INTERNAL: Copy fetch group refrerence from the source object to the target

        if (isPartialObject(source)) {
            ((FetchGroupTracker)target).setFetchGroup(((FetchGroupTracker)source).getFetchGroup());
        }
    
public oracle.toplink.essentials.descriptors.ClassDescriptorgetClassDescriptor()
INTERNAL: Return the referenced descriptor.

		return getDescriptor();
    
public oracle.toplink.essentials.queryframework.FetchGroupgetDefaultFetchGroup()
Return the descriptor-level default fetch group. All read object and read all queries would use the default fetch group if no fetch group is explicitly defined for the query, unless setShouldUseDefaultFetchGroup(false); is also called on the query. Default fetch group should be used carefully. It would be beneficial if most of the system queries are for the subset of the object, so un-needed attributes data would not have to be read, and the users do not have to setup every query for the given fetch group, as default one is always used. However, if queries on object are mostly use case specific and not systematic, using default fetch group could cause undesirable extra round-trip and performance degradation.

see
oracle.toplink.essentials.queryframework.ObjectLevelReadQuery#setShouldUseDefaultFetchGroup(boolean)

        return defaultFetchGroup;
    
public oracle.toplink.essentials.descriptors.ClassDescriptorgetDescriptor()
INTERNAL: Return the referenced descriptor.

        return descriptor;
    
public oracle.toplink.essentials.queryframework.FetchGroupgetFetchGroup(java.lang.String groupName)
Return a pre-defined named fetch group.

        return (FetchGroup)getFetchGroups().get(groupName);
    
public java.util.MapgetFetchGroups()
Return the fetch group map: keyed by the group name, valued by the fetch group object.

        if (fetchGroups == null) {
            //lazy initialized
            fetchGroups = new HashMap(2);
        }

        return fetchGroups;
    
public booleanisAttributeFetched(java.lang.Object object, java.lang.String attributeName)
Return true if the attribute of the object has already been fetched

        FetchGroup fetchgroup = ((FetchGroupTracker)object).getFetchGroup();
        return (fetchgroup == null) || (fetchgroup.getAttributes().contains(attributeName));
    
public booleanisObjectValidForFetchGroup(java.lang.Object object, oracle.toplink.essentials.queryframework.FetchGroup fetchGroup)
INTERNAL: Return if the cached object data is sufficiently valid against a fetch group

        FetchGroup groupInObject = ((FetchGroupTracker)object).getFetchGroup();
        return (groupInObject == null) || groupInObject.isSupersetOf(fetchGroup);
    
public booleanisPartialObject(java.lang.Object domainObject)
INTERNAL: Return true if the object is partially fetched and cached. It applies to the query with fetch group.

        if (domainObject != null) {
            FetchGroup fetchGroupInCache = ((FetchGroupTracker)domainObject).getFetchGroup();

            //if the fetch group reference is not null, it means the object is partial.
            return (fetchGroupInCache != null);
        }
        return false;
    
public voidprepareQueryWithFetchGroup(oracle.toplink.essentials.queryframework.ObjectLevelReadQuery query)
INTERNAL: Prepare the query with the fetch group to add group attributes to the query for partial reading.

        //initialize query's fetch group
        query.initializeFetchGroup();
        if ((query.getFetchGroup() == null) || query.getFetchGroup().hasFetchGroupAttributeExpressions()) {
            //simply return if fetch group is not defined; or if defined, it has been prepared already.
            return;
        } else {
            if (query.isReportQuery()) {
                //fetch group does not work with report query
                throw QueryException.fetchGroupNotSupportOnReportQuery();
            }
            if (query.hasPartialAttributeExpressions()) {
                //fetch group does not work with partial attribute reading
                throw QueryException.fetchGroupNotSupportOnPartialAttributeReading();
            }
        }
        Set attributes = query.getFetchGroup().getAttributes();
        ObjectBuilder builder = query.getDescriptor().getObjectBuilder();

        //First add all primary key attributes into the fetch group
        Iterator pkMappingIter = builder.getPrimaryKeyMappings().iterator();

        while (pkMappingIter.hasNext()) {
            DatabaseMapping pkMapping = (DatabaseMapping)pkMappingIter.next();
            DatabaseField pkField = pkMapping.getField();

            // Add pk attribute to the fetch group attributes list
            attributes.add(pkMapping.getAttributeName());
        }

        //second, add version/optimistic locking object attributes into the fetch group if applied.
        OptimisticLockingPolicy lockingPolicy = getDescriptor().getOptimisticLockingPolicy();
        if (query.shouldMaintainCache() && (lockingPolicy != null)) {
            lockingPolicy.prepareFetchGroupForReadQuery(query.getFetchGroup(), query);
        }

        //thrid, prepare all fetch group attributes 
        Iterator attrIter = attributes.iterator();
        while (attrIter.hasNext()) {
            String attrName = (String)attrIter.next();
            DatabaseMapping mapping = builder.getMappingForAttributeName(attrName);
            if (mapping == null) {
                //the attribute name defined in the fetch group is not mapped
                throw QueryException.fetchGroupAttributeNotMapped(attrName);
            }

            //partially fetch each fetch group attribute
            if (mapping.isCollectionMapping()) {
                query.getFetchGroup().addFetchGroupAttribute(query.getExpressionBuilder().anyOf(attrName));
            } else {
                query.getFetchGroup().addFetchGroupAttribute(query.getExpressionBuilder().get(attrName));
            }
        }
    
private voidrefreshFetchGroupIntoClones(java.lang.Object cachedObject, java.lang.Object workingClone, java.lang.Object backupClone, oracle.toplink.essentials.queryframework.FetchGroup fetchGroupInObject, oracle.toplink.essentials.queryframework.FetchGroup fetchGroupInClone, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
Refresh the fetch group data into the working and backup clones. This is called if refresh is enforced

        Vector mappings = descriptor.getMappings();
        boolean isObjectPartial = (fetchGroupInObject != null);
        Set fetchedAttributes = isObjectPartial ? fetchGroupInObject.getAttributes() : null;
        for (int index = 0; index < mappings.size(); index++) {
            DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
            if ((!isObjectPartial) || ((fetchedAttributes != null) && fetchedAttributes.contains(mapping.getAttributeName()))) {
                //only fill in the unfetched attributes into clones
                mapping.buildClone(cachedObject, workingClone, uow, null);
                mapping.buildClone(workingClone, backupClone, uow, null);
            }
        }
    
public voidreset(java.lang.Object source)
INTERNAL: Reset object attributes to the default values.

        ((FetchGroupTracker)source).resetFetchGroup();
    
private voidrevertDataIntoUnfetchedAttributesOfClones(java.lang.Object cachedObject, java.lang.Object workingClone, java.lang.Object backupClone, oracle.toplink.essentials.queryframework.FetchGroup fetchGroupInObject, oracle.toplink.essentials.queryframework.FetchGroup fetchGroupInClone, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
Revert the clones' unfetched attributes, and leave fetched ones intact.

        //if(fetchGroupInClone == null || fetchGroupInClone.isSupersetOf(fetchGroupInObject)) {
        if (isObjectValidForFetchGroup(workingClone, fetchGroupInObject)) {
            //if working clone is fully fetched or it's fetch group is superset of that of the cached object
            //no reversion is needed, so simply return
            return;
        }
        Vector mappings = descriptor.getMappings();

        //fetched attributes list in working clone
        Set fetchedAttributesClone = fetchGroupInClone.getAttributes();
        for (int index = 0; index < mappings.size(); index++) {
            DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);

            //only revert the attribute which is fetched by the cached object, but not fecthed by the clones.
            if (isAttributeFetched(cachedObject, mapping.getAttributeName()) && (!fetchedAttributesClone.contains(mapping.getAttributeName()))) {
                //only fill in the unfetched attributes into clones
                mapping.buildClone(cachedObject, workingClone, uow, null);
                mapping.buildClone(workingClone, backupClone, uow, null);
            }
        }
    
public voidsetDefaultFetchGroup(oracle.toplink.essentials.queryframework.FetchGroup newDefaultFetchGroup)
Set the descriptor-level default fetch group. All read object and read all queries would use the default fetch group if no fetch group is explicitly defined for the query, unless setShouldUseDefaultFetchGroup(false); is also called on the query. Default fetch group should be used carefully. It would be beneficial if most of the system queries are for the subset of the object, so un-needed attributes data would not have to be read, and the users do not have to setup every query for the given fetch group, as default one is always used. However, if queries on object are mostly use case specific and not systematic, using default fetch group could cause undesirable extra round-trip and performance degradation.

see
oracle.toplink.essentials.queryframework.ObjectLevelReadQuery#setShouldUseDefaultFetchGroup(boolean)

        defaultFetchGroup = newDefaultFetchGroup;
    
public voidsetDescriptor(oracle.toplink.essentials.descriptors.ClassDescriptor descriptor)
Set the referenced descriptor.

        this.descriptor = descriptor;
    
public voidsetObjectFetchGroup(java.lang.Object source, oracle.toplink.essentials.queryframework.FetchGroup fetchGroup)
INTERNAL: Reset object attributes to the default their values.

        if (descriptor.getFetchGroupManager() != null) {
            ((FetchGroupTracker)source).setFetchGroup(fetchGroup);
        }
    
public voidsetRefreshOnFetchGroupToObject(java.lang.Object source, boolean shouldRefreshOnFetchgroup)
INTERNAL: Set if the tracked object is fetched from executing a query with or without refresh.

        ((FetchGroupTracker)source).setShouldRefreshFetchGroup(shouldRefreshOnFetchgroup);
    
public booleanshouldWriteInto(java.lang.Object cachedObject, java.lang.Object clone)
INTERNAL: Return true if the cached object data should be written in clone. It is used in Fetch Group case when filling in the clone from the cached object.

        if (isPartialObject(clone)) {
            FetchGroup fetchGroupInSrc = ((FetchGroupTracker)cachedObject).getFetchGroup();
            FetchGroup fetchGroupInTarg = ((FetchGroupTracker)clone).getFetchGroup();

            //if the target fetch group is not null (i.e. fully fetched object) or if partially fetched, it's not a superset of that of the source, 
            //or if refresh is required, should always write (either refresh or revert) data from the cache to the clones.
            return (!((fetchGroupInTarg == null) || fetchGroupInTarg.isSupersetOf(fetchGroupInSrc)) || ((FetchGroupTracker)cachedObject).shouldRefreshFetchGroup());
        }
        return false;
    
public voidunionFetchGroupIntoObject(java.lang.Object source, oracle.toplink.essentials.queryframework.FetchGroup newFetchGroup)
INTERNAL: Union the fetch group of the domain object with the new fetch group.

        FetchGroupTracker tracker = (FetchGroupTracker)source;
        tracker.setFetchGroup(unionFetchGroups(tracker.getFetchGroup(), newFetchGroup));
    
public oracle.toplink.essentials.queryframework.FetchGroupunionFetchGroups(oracle.toplink.essentials.queryframework.FetchGroup first, oracle.toplink.essentials.queryframework.FetchGroup second)
INTERNAL: Union two fetch groups.

        if ((first == null) || (second == null)) {
            return null;
        }

        //return the superset if applied
        if (first.isSupersetOf(second)) {
            return first;
        } else if (second.isSupersetOf(first)) {
            return second;
        }

        //otherwise, union two fetch groups
        StringBuffer unionGroupName = new StringBuffer(first.getName());
        unionGroupName.append("_");
        unionGroupName.append(second.getName());
        FetchGroup unionFetchGroup = new FetchGroup(unionGroupName.toString());
        unionFetchGroup.addAttributes(first.getAttributes());
        unionFetchGroup.addAttributes(second.getAttributes());
        return unionFetchGroup;
    
public voidwritePartialIntoClones(java.lang.Object partialObject, java.lang.Object workingClone, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
INTERNAL: Write data of the partially fetched object into the working and backup clones

        FetchGroup fetchGroupInClone = ((FetchGroupTracker)workingClone).getFetchGroup();
        FetchGroup fetchGroupInObject = ((FetchGroupTracker)partialObject).getFetchGroup();
        Object backupClone = uow.getBackupClone(workingClone);

        //if refresh is set, force to fill in fecth group data
        if (((FetchGroupTracker)partialObject).shouldRefreshFetchGroup()) {
            //refresh and fill in the fecth group data
            refreshFetchGroupIntoClones(partialObject, workingClone, backupClone, fetchGroupInObject, fetchGroupInClone, uow);
        } else {//no refresh is enforced
            //revert the unfetched attributes of the clones.
            revertDataIntoUnfetchedAttributesOfClones(partialObject, workingClone, backupClone, fetchGroupInObject, fetchGroupInClone, uow);
        }

        //update fecth group in clone as the union of two
        fetchGroupInObject = unionFetchGroups(fetchGroupInObject, fetchGroupInClone);
        //finally, update clone's fetch group reference 
        setObjectFetchGroup(workingClone, fetchGroupInObject);
        setObjectFetchGroup(backupClone, fetchGroupInObject);