FileDocCategorySizeDatePackage
CollectionMapping.javaAPI DocGlassfish v2 API65322Tue May 22 16:54:46 BST 2007oracle.toplink.essentials.mappings

CollectionMapping

public abstract class CollectionMapping extends ForeignReferenceMapping implements ContainerMapping

Purpose: Abstract class for relationship mappings which store collection of objects

author
Sati
since
TOPLink/Java 1.0

Fields Summary
protected transient ModifyQuery
deleteAllQuery
Used for delete all in m-m, dc and delete all optimization in 1-m.
protected transient boolean
hasCustomDeleteAllQuery
protected ContainerPolicy
containerPolicy
protected transient boolean
hasOrderBy
Constructors Summary
public CollectionMapping()
PUBLIC: Default constructor.

        this.selectionQuery = new ReadAllQuery();
        this.hasCustomDeleteAllQuery = false;
        this.containerPolicy = ContainerPolicy.buildPolicyFor(ClassConstants.Vector_class);
        this.hasOrderBy = false;
    
Methods Summary
public voidaddAggregateOrderBy(java.lang.String aggregateName, java.lang.String queryKeyName, boolean isDescending)
PUBLIC: Provide order support for queryKeyName in ascending order. Called from the EJBAnnotationsProcessor when an @OrderBy on an aggregate is found.

        this.hasOrderBy = true;
        
        ReadAllQuery readAllQuery = (ReadAllQuery) getSelectionQuery();
        ExpressionBuilder builder = readAllQuery.getExpressionBuilder();
        Expression expression = builder.get(aggregateName).get(queryKeyName).toUpperCase();
        
        if (isDescending) {
            readAllQuery.addOrdering(expression.descending());
        } else {
            readAllQuery.addOrdering(expression.ascending());
        }
    
public voidaddAscendingOrdering(java.lang.String queryKeyName)
PUBLIC: Provide order support for queryKeyName in ascending order

        if (queryKeyName == null) {
            return;
        }
        
        ((ReadAllQuery)getSelectionQuery()).addAscendingOrdering(queryKeyName);
    
public voidaddDescendingOrdering(java.lang.String queryKeyName)
PUBLIC: Provide order support for queryKeyName in descending order.

        if (queryKeyName == null) {
            return;
        }
        
        ((ReadAllQuery)getSelectionQuery()).addDescendingOrdering(queryKeyName);
    
public voidaddOrderBy(java.lang.String queryKeyName, boolean isDescending)
PUBLIC: Provide order support for queryKeyName in descending or ascending order. Called from the EJBAnnotationsProcessor when an @OrderBy is found.

        this.hasOrderBy = true;
        
        if (isDescending) {
            addDescendingOrdering(queryKeyName);
        } else {
            addAscendingOrdering(queryKeyName);
        }
    
public voidaddToCollectionChangeRecord(java.lang.Object newKey, java.lang.Object newValue, ObjectChangeSet objectChangeSet, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
INTERNAL: Add a new value and its change set to the collection change record. This is used by attribute change tracking.

        if (newValue != null) {
            ClassDescriptor descriptor;
            //PERF: Use referenceDescriptor if it does not have inheritance
            if (!getReferenceDescriptor().hasInheritance()) {
                descriptor = getReferenceDescriptor();
            } else {
                descriptor = uow.getDescriptor(newValue);
            }
            newValue = descriptor.getObjectBuilder().unwrapObject(newValue, uow);
            ObjectChangeSet newSet = descriptor.getObjectBuilder().createObjectChangeSet(newValue, (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet(), uow);
            simpleAddToCollectionChangeRecord(newKey, newSet, objectChangeSet, uow);
        }
    
public java.lang.ObjectbuildBackupCloneForPartObject(java.lang.Object attributeValue, java.lang.Object clone, java.lang.Object backup, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork)
INTERNAL: Used during building the backup shallow copy to copy the vector without re-registering the target objects.

        // Check for null
        if (attributeValue == null) {
            return getContainerPolicy().containerInstance(1);
        } else {
            return getContainerPolicy().cloneFor(attributeValue);
        }
    
public ChangeRecordbuildChangeRecord(java.lang.Object clone, ObjectChangeSet owner, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Directly build a change record without comparison

        Object cloneAttribute = null;
        cloneAttribute = getAttributeValueFromObject(clone);
        if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
            return null;
        }

        // 2612538 - the default size of IdentityHashtable (32) is appropriate
        IdentityHashMap cloneKeyValues = new IdentityHashMap();
        ContainerPolicy cp = getContainerPolicy();
        Object cloneObjectCollection = null;
        if (cloneAttribute != null) {
            cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
        } else {
            cloneObjectCollection = cp.containerInstance(1);
        }
        Object cloneIter = cp.iteratorFor(cloneObjectCollection);

        while (cp.hasNext(cloneIter)) {
            Object firstObject = cp.next(cloneIter, session);
            if (firstObject != null) {
                cloneKeyValues.put(firstObject, firstObject);
            }
        }

        CollectionChangeRecord changeRecord = new CollectionChangeRecord(owner);
        changeRecord.setAttribute(getAttributeName());
        changeRecord.setMapping(this);
        changeRecord.addAdditionChange(cloneKeyValues, (UnitOfWorkChangeSet)owner.getUOWChangeSet(), session);
        if (changeRecord.hasChanges()) {
            return changeRecord;
        }
        return null;
    
public java.lang.ObjectbuildCloneForPartObject(java.lang.Object attributeValue, java.lang.Object original, java.lang.Object clone, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork, boolean isExisting)
INTERNAL: Require for cloning, the part must be cloned. Ignore the objects, use the attribute value.

        ContainerPolicy containerPolicy = getContainerPolicy();
        if (attributeValue == null) {
            Object container = containerPolicy.containerInstance(1);
            return container;
        }
        Object clonedAttributeValue = containerPolicy.containerInstance(containerPolicy.sizeFor(attributeValue));

        // I need to synchronize here to prevent the collection from changing while I am cloning it.
        // This will occur when I am merging into the cache and I am instantiating a UOW valueHolder at the same time
        // I can not synchronize around the clone, as this will cause deadlocks, so I will need to copy the collection then create the clones
        // I will use a temporary collection to help speed up the process
        Object temporaryCollection = null;
        synchronized (attributeValue) {
            temporaryCollection = containerPolicy.cloneFor(attributeValue);
        }
        for (Object valuesIterator = containerPolicy.iteratorFor(temporaryCollection);
                 containerPolicy.hasNext(valuesIterator);) {
            Object cloneValue = buildElementClone(containerPolicy.next(valuesIterator, unitOfWork), unitOfWork, isExisting);
            containerPolicy.addInto(cloneValue, clonedAttributeValue, unitOfWork);
        }
        return clonedAttributeValue;
    
public voidbuildCopy(java.lang.Object copy, java.lang.Object original, oracle.toplink.essentials.sessions.ObjectCopyingPolicy policy)
INTERNAL: Copy of the attribute of the object. This is NOT used for unit of work but for templatizing an object.

        Object attributeValue = getRealCollectionAttributeValueFromObject(original, policy.getSession());
        Object valuesIterator = getContainerPolicy().iteratorFor(attributeValue);
        attributeValue = getContainerPolicy().containerInstance(getContainerPolicy().sizeFor(attributeValue));
        while (getContainerPolicy().hasNext(valuesIterator)) {
            Object originalValue = getContainerPolicy().next(valuesIterator, policy.getSession());
            Object copyValue = originalValue;
            if (policy.shouldCascadeAllParts() || (policy.shouldCascadePrivateParts() && isPrivateOwned())) {
                copyValue = policy.getSession().copyObject(originalValue, policy);
            } else {
                // Check for backrefs to copies.
                copyValue = policy.getCopies().get(originalValue);
                if (copyValue == null) {
                    copyValue = originalValue;
                }
            }
            getContainerPolicy().addInto(copyValue, attributeValue, policy.getSession());
        }
        setRealAttributeValueInObject(copy, attributeValue);
    
protected java.lang.ObjectbuildElementClone(java.lang.Object element, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork, boolean isExisting)
INTERNAL: Clone the element, if necessary.

        // optimize registration to knowledge of existence
        if (isExisting) {
            return unitOfWork.registerExistingObject(element);
        } else {// not known whether existing or not
            return unitOfWork.registerObject(element);
        }
    
public voidcalculateDeferredChanges(ChangeRecord changeRecord, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Used by AttributeLevelChangeTracking to update a changeRecord with calculated changes as apposed to detected changes. If an attribute can not be change tracked it's changes can be detected through this process.

        CollectionChangeRecord collectionRecord = (CollectionChangeRecord) changeRecord;
        //clear incase events were fired since the set of the collection
//        collectionRecord.getAddObjectList().clear();
//        collectionRecord.getRemoveObjectList().clear();
        compareCollectionsForChange(collectionRecord.getOriginalCollection(), collectionRecord.getLatestCollection(), collectionRecord, session);
    
public voidcascadeMerge(java.lang.Object sourceElement, MergeManager mergeManager)
INTERNAL: Cascade the merge to the component object, if appropriate.

        if (shouldMergeCascadeParts(mergeManager)) {
            mergeManager.mergeChanges(mergeManager.getObjectToMerge(sourceElement), null);
        }
    
public voidcascadePerformRemoveIfRequired(java.lang.Object object, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow, IdentityHashtable visitedObjects)
INTERNAL: Cascade perform delete through mappings that require the cascade

        Object cloneAttribute = null;
        cloneAttribute = getAttributeValueFromObject(object);
        if ((cloneAttribute == null) || (!this.isCascadeRemove())) {
            return;
        }

        ContainerPolicy cp = getContainerPolicy();
        Object cloneObjectCollection = null;
        cloneObjectCollection = getRealCollectionAttributeValueFromObject(object, uow);
        Object cloneIter = cp.iteratorFor(cloneObjectCollection);
        while (cp.hasNext(cloneIter)) {
            Object nextObject = cp.next(cloneIter, uow);
            if (nextObject != null && (! visitedObjects.contains(nextObject)) ){
                visitedObjects.put(nextObject, nextObject);
                uow.performRemove(nextObject, visitedObjects);
            }
        }
    
public voidcascadeRegisterNewIfRequired(java.lang.Object object, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow, IdentityHashtable visitedObjects)
INTERNAL: Cascade registerNew for Create through mappings that require the cascade

        Object cloneAttribute = null;
        cloneAttribute = getAttributeValueFromObject(object);
        if ((cloneAttribute == null) || (!this.isCascadePersist()) || (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
            return;
        }

        ContainerPolicy cp = getContainerPolicy();
        Object cloneObjectCollection = null;
        cloneObjectCollection = getRealCollectionAttributeValueFromObject(object, uow);
        Object cloneIter = cp.iteratorFor(cloneObjectCollection);
        while (cp.hasNext(cloneIter)) {
            Object nextObject = cp.next(cloneIter, uow);
            if (nextObject != null && (! visitedObjects.contains(nextObject)) ){
                visitedObjects.put(nextObject, nextObject);
                uow.registerNewObjectForPersist(nextObject, visitedObjects);
            }
        }
    
private voidcheckMapClass(java.lang.Class concreteClass)
INTERNAL: Common validation for a collection mapping using a Map class.

        // the reference class has to be specified before coming here
        if (getReferenceClass() == null) {
            throw DescriptorException.referenceClassNotSpecified(this);
        }
        
        if (! Helper.classImplementsInterface(concreteClass, ClassConstants.Map_Class)) {
            throw ValidationException.illegalContainerClass(concreteClass);
        }
    
public voidcompareCollectionsForChange(java.lang.Object oldCollection, java.lang.Object newCollection, ChangeRecord changeRecord, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: This method is used to calculate the differences between two collections. It is passed to the container policy to calculate the changes.

        getContainerPolicy().compareCollectionsForChange(oldCollection, newCollection, (CollectionChangeRecord) changeRecord, session, getReferenceDescriptor());
    
public ChangeRecordcompareForChange(java.lang.Object clone, java.lang.Object backUp, ObjectChangeSet owner, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: This method is used to create a change record from comparing two collections

return
prototype.changeset.ChangeRecord

        Object cloneAttribute = null;
        Object backUpAttribute = null;

        Object backUpObjectCollection = null;

        cloneAttribute = getAttributeValueFromObject(clone);

        if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
            return null;
        }


        if (!owner.isNew()) {// if the changeSet is for a new object then we must record all off the attributes
            backUpAttribute = getAttributeValueFromObject(backUp);

            if ((cloneAttribute == null) && (backUpAttribute == null)) {
                return null;
            }

            backUpObjectCollection = getRealCollectionAttributeValueFromObject(backUp, session);
       }

        Object cloneObjectCollection = null;
        if (cloneAttribute != null) {
            cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
        } else {
            cloneObjectCollection = getContainerPolicy().containerInstance(1);
        }

        CollectionChangeRecord changeRecord = new CollectionChangeRecord(owner);
        changeRecord.setAttribute(getAttributeName());
        changeRecord.setMapping(this);
        compareCollectionsForChange(backUpObjectCollection, cloneObjectCollection, changeRecord, session);
        if (changeRecord.hasChanges()) {
            return changeRecord;
        }
        return null;
    
public booleancompareObjects(java.lang.Object firstObject, java.lang.Object secondObject, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Compare the attributes belonging to this mapping for the objects.

        Object firstObjectCollection = getRealCollectionAttributeValueFromObject(firstObject, session);
        Object secondObjectCollection = getRealCollectionAttributeValueFromObject(secondObject, session);

        return super.compareObjects(firstObjectCollection, secondObjectCollection, session);
    
protected voidcompareObjectsAndWrite(java.lang.Object previousObjects, java.lang.Object currentObjects, WriteObjectQuery query)
INTERNAL: The memory objects are compared and only the changes are written to the database

        ContainerPolicy cp = getContainerPolicy();
        
        // If it is for an aggregate collection let it continue so that all of 
        // the correct values are deleted and then re-added  This could be 
        // changed to make AggregateCollection changes smarter.
        if ((query.getObjectChangeSet() != null) && !this.isAggregateCollectionMapping()) {
            ObjectChangeSet changeSet = query.getObjectChangeSet();
            CollectionChangeRecord record = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
            
            if (record != null) {
                ObjectChangeSet removedChangeSet = null;
                ObjectChangeSet addedChangeSet = null;
                UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)changeSet.getUOWChangeSet();
                Enumeration removedObjects = record.getRemoveObjectList().elements();
                
                while (removedObjects.hasMoreElements()) {
                    removedChangeSet = (ObjectChangeSet)removedObjects.nextElement();
                    objectRemovedDuringUpdate(query, removedChangeSet.getUnitOfWorkClone());
                }

                Enumeration addedObjects = record.getAddObjectList().elements();
                
                while (addedObjects.hasMoreElements()) {
                    addedChangeSet = (ObjectChangeSet)addedObjects.nextElement();
                    objectAddedDuringUpdate(query, addedChangeSet.getUnitOfWorkClone(), addedChangeSet);
                }
            }
            
            return;
        }

        Hashtable previousObjectsByKey = new Hashtable(cp.sizeFor(previousObjects) + 2);// Read from db or from backup in uow.
        Hashtable currentObjectsByKey = new Hashtable(cp.sizeFor(currentObjects) + 2);// Current value of object's attribute (clone in uow).

        IdentityHashtable cacheKeysOfCurrentObjects = new IdentityHashtable(cp.sizeFor(currentObjects) + 1);

        // First index the current objects by their primary key.
        for (Object currentObjectsIter = cp.iteratorFor(currentObjects);
                 cp.hasNext(currentObjectsIter);) {
            Object currentObject = cp.next(currentObjectsIter, query.getSession());
            try {
                Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
                CacheKey key = new CacheKey(primaryKey);
                currentObjectsByKey.put(key, currentObject);
                cacheKeysOfCurrentObjects.put(currentObject, key);
            } catch (NullPointerException e) {
                // For CR#2646 quietly discard nulls added to a collection mapping.
                // This try-catch is essentially a null check on currentObject, for
                // ideally the customer should check for these themselves.
                if (currentObject != null) {
                    throw e;
                }
            }
        }

        // Next index the previous objects (read from db or from backup in uow)
        // and process the difference to current (optimized in same loop).
        for (Object previousObjectsIter = cp.iteratorFor(previousObjects);
                 cp.hasNext(previousObjectsIter);) {
            Object previousObject = cp.next(previousObjectsIter, query.getSession());
            Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
            CacheKey key = new CacheKey(primaryKey);
            previousObjectsByKey.put(key, previousObject);

            // Delete must occur first, incase object with same pk is removed and added,
            // (technically should not happen, but same applies to unquie constainsts)
            if (!currentObjectsByKey.containsKey(key)) {
                objectRemovedDuringUpdate(query, previousObject);
            }
        }

        for (Object currentObjectsIter = cp.iteratorFor(currentObjects);
                 cp.hasNext(currentObjectsIter);) {
            Object currentObject = cp.next(currentObjectsIter, query.getSession());
            try {
                CacheKey cacheKey = (CacheKey)cacheKeysOfCurrentObjects.get(currentObject);

                if (!(previousObjectsByKey.containsKey(cacheKey))) {
                    objectAddedDuringUpdate(query, currentObject, null);
                } else {
                    objectUnchangedDuringUpdate(query, currentObject, previousObjectsByKey, cacheKey);
                }
            } catch (NullPointerException e) {
                // For CR#2646 skip currentObject if it is null.
                if (currentObject != null) {
                    throw e;
                }
            }
        }
    
protected booleancompareObjectsWithPrivateOwned(java.lang.Object firstCollection, java.lang.Object secondCollection, oracle.toplink.essentials.internal.sessions.AbstractSession session)
Compare two objects if their parts are private owned

        ContainerPolicy cp = getContainerPolicy();
        if (cp.sizeFor(firstCollection) != cp.sizeFor(secondCollection)) {
            return false;
        }

        Object firstIter = cp.iteratorFor(firstCollection);
        Object secondIter = cp.iteratorFor(secondCollection);

        Hashtable keyValueToObject = new Hashtable(cp.sizeFor(firstCollection) + 2);
        CacheKey cacheKey;

        while (cp.hasNext(secondIter)) {
            Object secondObject = cp.next(secondIter, session);
            Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(secondObject, session);
            keyValueToObject.put(new CacheKey(primaryKey), secondObject);
        }

        while (cp.hasNext(firstIter)) {
            Object firstObject = cp.next(firstIter, session);
            Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(firstObject, session);
            cacheKey = new CacheKey(primaryKey);

            if (keyValueToObject.containsKey(cacheKey)) {
                Object object = keyValueToObject.get(cacheKey);

                if (!session.compareObjects(firstObject, object)) {
                    return false;
                }
            } else {
                return false;
            }
        }

        return true;
    
protected booleancompareObjectsWithoutPrivateOwned(java.lang.Object firstCollection, java.lang.Object secondCollection, oracle.toplink.essentials.internal.sessions.AbstractSession session)
Compare two objects if their parts are not private owned

        ContainerPolicy cp = getContainerPolicy();
        if (cp.sizeFor(firstCollection) != cp.sizeFor(secondCollection)) {
            return false;
        }

        Object firstIter = cp.iteratorFor(firstCollection);
        Object secondIter = cp.iteratorFor(secondCollection);

        Vector keyValue = new Vector();

        while (cp.hasNext(secondIter)) {
            Object secondObject = cp.next(secondIter, session);
            Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(secondObject, session);
            keyValue.addElement(new CacheKey(primaryKey));
        }

        while (cp.hasNext(firstIter)) {
            Object firstObject = cp.next(firstIter, session);
            Vector primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(firstObject, session);

            if (!keyValue.contains(new CacheKey(primaryKey))) {
                return false;
            }
        }
        return true;
    
public voidconvertClassNamesToClasses(java.lang.ClassLoader classLoader)
INTERNAL: Convert all the class-name-based settings in this mapping to actual class-based settings This method is implemented by subclasses as necessary.

param
classLoader

        super.convertClassNamesToClasses(classLoader);
        containerPolicy.convertClassNamesToClasses(classLoader);
    
public ContainerPolicygetContainerPolicy()
INTERNAL: Returns the receiver's containerPolicy.

        return containerPolicy;
    
protected ModifyQuerygetDeleteAllQuery()

        if (deleteAllQuery == null) {
            deleteAllQuery = new DataModifyQuery();
        }
        return deleteAllQuery;
    
public java.lang.ObjectgetRealAttributeValueFromObject(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Return the value of an attribute, unwrapping value holders if necessary. Also check to ensure the collection is a vector.

        Object value = super.getRealAttributeValueFromObject(object, session);
        if (value != null) {
            if (!getContainerPolicy().isValidContainer(value)) {
                throw DescriptorException.attributeTypeNotValid(this);
            }
        }
        return value;
    
public java.lang.ObjectgetRealCollectionAttributeValueFromObject(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractSession session)
Convenience method. Return the value of an attribute, unwrapping value holders if necessary. If the value is null, build a new container.

        Object value = this.getRealAttributeValueFromObject(object, session);
        if (value == null) {
            value = this.getContainerPolicy().containerInstance(1);
        }
        return value;
    
protected booleanhasCustomDeleteAllQuery()

        return hasCustomDeleteAllQuery;
    
public booleanhasOrderBy()
INTERNAL: Return true if ascending or descending ordering has been set on this mapping via the @OrderBy annotation.

        return hasOrderBy;
    
public voidinitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Initialize the state of mapping.

        super.initialize(session);
        setFields(collectFields());
        getContainerPolicy().prepare(getSelectionQuery(), session);

        // Check that the container policy is correct for the collection type.
        if ((!usesIndirection()) && (!getAttributeAccessor().getAttributeClass().isAssignableFrom(getContainerPolicy().getContainerClass()))) {
            throw DescriptorException.incorrectCollectionPolicy(this, getAttributeAccessor().getAttributeClass(), getContainerPolicy().getContainerClass());
        }
    
public booleanisChangeTrackingSupported()
INTERNAL: Return if this mapping supports change tracking.

        return true;
    
public booleanisCollectionMapping()
INTERNAL:

        return true;
    
public voiditerateOnElement(DescriptorIterator iterator, java.lang.Object element)
INTERNAL: Iterate on the specified element.

        iterator.iterateReferenceObjectForMapping(element, this);
    
public voiditerateOnRealAttributeValue(DescriptorIterator iterator, java.lang.Object realAttributeValue)
INTERNAL: Iterate on the attribute value. The value holder has already been processed.

        if (realAttributeValue == null) {
            return;
        }
        ContainerPolicy cp = getContainerPolicy();
        for (Object iter = cp.iteratorFor(realAttributeValue); cp.hasNext(iter);) {
            iterateOnElement(iterator, cp.next(iter, iterator.getSession()));
        }
    
public voidmergeChangesIntoObject(java.lang.Object target, ChangeRecord chgRecord, java.lang.Object source, MergeManager mergeManager)
INTERNAL: Merge changes from the source to the target object. Because this is a collection mapping, values are added to or removed from the collection based on the change set.

        Object valueOfTarget = null;
        Object valueOfSource = null;
        AbstractSession parentSession = null;
        ContainerPolicy containerPolicy = getContainerPolicy();
        CollectionChangeRecord changeRecord = (CollectionChangeRecord) chgRecord;
        UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)changeRecord.getOwner().getUOWChangeSet();

        // Collect the changes into a vector. Check to see if the target has an instantiated 
        // collection, if it does then iterate over the changes and merge the collections.
        if (isAttributeValueInstantiated(target)) {
            // If it is new will need a new collection.
            if (changeRecord.getOwner().isNew()) {
                valueOfTarget = containerPolicy.containerInstance(changeRecord.getAddObjectList().size());
            } else {
                valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeManager.getSession());
            }

            // Remove must happen before add to allow for changes in hash keys.
            // This is required to return the appropriate object from the parent when unwrapping.
            if (mergeManager.getSession().isUnitOfWork() && !mergeManager.shouldMergeWorkingCopyIntoBackup()) {
                parentSession = ((UnitOfWorkImpl)mergeManager.getSession()).getParent();
            } else {
                parentSession = mergeManager.getSession();
            }
            
            containerPolicy.mergeChanges(changeRecord, valueOfTarget, shouldMergeCascadeParts(mergeManager), mergeManager, parentSession);
        } else { 
            // The valueholder has not been instantiated
            if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
                return; // do nothing
            }

            // If I'm not merging on another server then create instance of the collection.
            valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
            Object iterator = containerPolicy.iteratorFor(valueOfSource);
            valueOfTarget = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
            
            while (containerPolicy.hasNext(iterator)) {
                // CR2195 - Problem with merging Collection mapping in unit of work and inheritance.
                Object objectToMerge = containerPolicy.next(iterator, mergeManager.getSession());
                
                ObjectChangeSet changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(objectToMerge);
                if (shouldMergeCascadeParts(mergeManager) && (valueOfSource != null)) {
                    mergeManager.mergeChanges(objectToMerge, changeSet);
                }

                // Let the mergemanager get it because I don't have the change for the object.
                // CR2188 - Problem with merging Collection mapping in unit of work and transparent indirection.
                containerPolicy.addInto(mergeManager.getTargetVersionOfSourceObject(objectToMerge), valueOfTarget, mergeManager.getSession());
            }
        }
        
        if (valueOfTarget == null) {
            valueOfTarget = containerPolicy.containerInstance();
        }
        
        setRealAttributeValueInObject(target, valueOfTarget);
    
public voidmergeIntoObject(java.lang.Object target, boolean isTargetUnInitialized, java.lang.Object source, MergeManager mergeManager)
INTERNAL: Merge changes from the source to the target object. This merge is only called when a changeSet for the target does not exist or the target is uninitialized

        if (isTargetUnInitialized) {
            // This will happen if the target object was removed from the cache before the commit was attempted
            if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!isAttributeValueInstantiated(source))) {
                setAttributeValueInObject(target, getIndirectionPolicy().getOriginalIndirectionObject(getAttributeValueFromObject(source), mergeManager.getSession()));
                return;
            }
        }
        if (!shouldMergeCascadeReference(mergeManager)) {
            // This is only going to happen on mergeClone, and we should not attempt to merge the reference
            return;
        }
        if (mergeManager.shouldMergeOriginalIntoWorkingCopy()) {
            if (!isAttributeValueInstantiated(target)) {
                // This will occur when the clone's value has not been instantiated yet and we do not need
                // the refresh that attribute
                return;
            }
        } else if (!isAttributeValueInstantiated(source)) {
            // I am merging from a clone into an original.  No need to do merge if the attribute was never
            // modified
            return;
        }

        Object valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());

        // There is a very special case when merging into the shared cache that the original
        // has been refreshed and now has non-instantiated indirection objects.
        // Force instantiation is not necessary and can cause problem with JTS drivers.
        AbstractSession mergeSession = mergeManager.getSession();
        Object valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeSession);
        ContainerPolicy containerPolicy = getContainerPolicy();
        boolean fireChangeEvents = false;
        if (! mergeManager.shouldMergeOriginalIntoWorkingCopy()){
   	        // if we are copying from original to clone then the source will be     
            // instantiated anyway and we must continue to use the UnitOfWork 
            // valueholder in the case of transparent indirection
            Object newContainer = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
            valueOfTarget = newContainer;
        }else{
            //bug 3953038 - set a new collection in the object until merge completes, this
            //              prevents rel-maint. from adding duplicates.
            setRealAttributeValueInObject(target, containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource)));
            containerPolicy.clear(valueOfTarget);
        }

        synchronized(valueOfSource){
            Object sourceIterator = containerPolicy.iteratorFor(valueOfSource);
            while (containerPolicy.hasNext(sourceIterator)) {
                Object object = containerPolicy.next(sourceIterator, mergeManager.getSession());
                if (object == null){
                    continue; // skip the null
                }
                if (shouldMergeCascadeParts(mergeManager)) {
                    if ((mergeManager.getSession().isUnitOfWork()) && (((UnitOfWorkImpl)mergeManager.getSession()).getUnitOfWorkChangeSet() != null)) {
                        // If it is a unit of work, we have to check if I have a change Set fot this object
                        mergeManager.mergeChanges(mergeManager.getObjectToMerge(object), (ObjectChangeSet)((UnitOfWorkImpl)mergeManager.getSession()).getUnitOfWorkChangeSet().getObjectChangeSetForClone(object));
                    } else {
                        mergeManager.mergeChanges(mergeManager.getObjectToMerge(object), null);
                    }
                }
                object = getReferenceDescriptor().getObjectBuilder().wrapObject(mergeManager.getTargetVersionOfSourceObject(object), mergeManager.getSession());
                synchronized (valueOfTarget){
                    containerPolicy.addInto(object, valueOfTarget, mergeManager.getSession());
                }
            }
        }
        // Must re-set variable to allow for set method to re-morph changes if the collection is not being stored directly.
        setRealAttributeValueInObject(target, valueOfTarget);
    
protected voidobjectAddedDuringUpdate(ObjectLevelModifyQuery query, java.lang.Object objectAdded, ObjectChangeSet changeSet)
INTERNAL: An object was added to the collection during an update, insert it if private.

        if (!shouldObjectModifyCascadeToParts(query)) {// Called always for M-M
            return;
        }

        // Only cascade dependents writes in uow.
        if (query.shouldCascadeOnlyDependentParts()) {
            return;
        }

        // Insert must not be done for uow or cascaded queries and we must cascade to cascade policy.
        // We should distiguish between insert and write (optimization/paraniod).
        if (isPrivateOwned()) {
            InsertObjectQuery insertQuery = new InsertObjectQuery();
            insertQuery.setObject(objectAdded);
            insertQuery.setCascadePolicy(query.getCascadePolicy());
            query.getSession().executeQuery(insertQuery);
        } else {
            // Always write for updates, either private or in uow if calling this method.
            UnitOfWorkChangeSet uowChangeSet = null;
            if ((changeSet == null) && query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
                uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
                changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(query.getObject());
            }
            WriteObjectQuery writeQuery = new WriteObjectQuery();
            writeQuery.setObject(objectAdded);
            writeQuery.setObjectChangeSet(changeSet);
            writeQuery.setCascadePolicy(query.getCascadePolicy());
            query.getSession().executeQuery(writeQuery);
        }
    
protected voidobjectRemovedDuringUpdate(ObjectLevelModifyQuery query, java.lang.Object objectDeleted)
INTERNAL: An object was removed to the collection during an update, delete it if private.

        if (isPrivateOwned()) {// Must check ownership for uow and cascading.
            if (query.shouldCascadeOnlyDependentParts()) {
                // If the session is a unit of work
                if (query.getSession().isUnitOfWork()) {
                    // ...and the object has not been explictly deleted in the unit of work
                    if (!(((UnitOfWorkImpl)query.getSession()).getDeletedObjects().containsKey(objectDeleted))) {
                        query.getSession().getCommitManager().addObjectToDelete(objectDeleted);
                    }
                } else {
                    query.getSession().getCommitManager().addObjectToDelete(objectDeleted);
                }
            } else {
                query.getSession().deleteObject(objectDeleted);
            }
        }
    
protected voidobjectUnchangedDuringUpdate(ObjectLevelModifyQuery query, java.lang.Object object)
INTERNAL: An object is still in the collection, update it as it may have changed.

        if (!shouldObjectModifyCascadeToParts(query)) {// Called always for M-M
            return;
        }

        // Only cascade dependents writes in uow.
        if (query.shouldCascadeOnlyDependentParts()) {
            return;
        }

        // Always write for updates, either private or in uow if calling this method.
        WriteObjectQuery writeQuery = new WriteObjectQuery();
        writeQuery.setObject(object);
        writeQuery.setCascadePolicy(query.getCascadePolicy());
        query.getSession().executeQuery(writeQuery);
    
protected voidobjectUnchangedDuringUpdate(ObjectLevelModifyQuery query, java.lang.Object object, java.util.Hashtable backupclones, CacheKey keys)
INTERNAL: An object is still in the collection, update it as it may have changed.

        objectUnchangedDuringUpdate(query, object);
    
protected voidprepareTranslationRow(oracle.toplink.essentials.internal.sessions.AbstractRecord translationRow, java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: copies the non primary key information into the row currently used only in ManyToMany

        //Do nothing for the generic Collection Mapping
    
protected java.lang.ObjectreadPrivateOwnedForObject(ObjectLevelModifyQuery modifyQuery)
INTERNAL: All the privately owned parts are read

        if (modifyQuery.getSession().isUnitOfWork()) {
            return getRealCollectionAttributeValueFromObject(modifyQuery.getBackupClone(), modifyQuery.getSession());
        } else {
            // cr 3819
            prepareTranslationRow(modifyQuery.getTranslationRow(), modifyQuery.getObject(), modifyQuery.getSession());
            return modifyQuery.getSession().executeQuery(getSelectionQuery(), modifyQuery.getTranslationRow());
        }
    
public voidremoveFromCollectionChangeRecord(java.lang.Object newKey, java.lang.Object newValue, ObjectChangeSet objectChangeSet, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
INTERNAL: Remove a value and its change set from the collection change record. This is used by attribute change tracking.

        if (newValue != null) {
            ClassDescriptor descriptor;

            //PERF: Use referenceDescriptor if it does not have inheritance
            if (!getReferenceDescriptor().hasInheritance()) {
                descriptor = getReferenceDescriptor();
            } else {
                descriptor = uow.getDescriptor(newValue);
            }
            newValue = descriptor.getObjectBuilder().unwrapObject(newValue, uow);
            ObjectChangeSet newSet = descriptor.getObjectBuilder().createObjectChangeSet(newValue, (UnitOfWorkChangeSet)objectChangeSet.getUOWChangeSet(), uow);
            simpleRemoveFromCollectionChangeRecord(newKey, newSet, objectChangeSet, uow);
        }
    
public voidsetContainerPolicy(ContainerPolicy containerPolicy)
ADVANCED: Configure the mapping to use a container policy. The policy manages the access to the collection.

        this.containerPolicy = containerPolicy;
        ((ReadAllQuery)getSelectionQuery()).setContainerPolicy(containerPolicy);
    
public voidsetCustomDeleteAllQuery(ModifyQuery query)
PUBLIC: The default delete all query for mapping can be overridden by specifying the new query. This query is responsible for doing the deletion required by the mapping, such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M.

        setDeleteAllQuery(query);
        setHasCustomDeleteAllQuery(true);
    
public voidsetDeleteAllCall(Call call)
PUBLIC: Set the receiver's delete all call. This allows the user to override the SQL generated by TopLink, with there own SQL or procedure call. The arguments are translated from the fields of the source row. This call is responsible for doing the deletion required by the mapping, such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M. Example, 'new SQLCall("delete from PROJ_EMP where EMP_ID = #EMP_ID")'.

        DataModifyQuery query = new DataModifyQuery();
        query.setCall(call);
        setCustomDeleteAllQuery(query);
    
protected voidsetDeleteAllQuery(ModifyQuery query)

        deleteAllQuery = query;
    
public voidsetDeleteAllSQLString(java.lang.String sqlString)
PUBLIC: Set the receiver's delete all SQL string. This allows the user to override the SQL generated by TopLink, with there own SQL or procedure call. The arguments are translated from the fields of the source row, through replacing the field names marked by '#' with the values for those fields. This SQL is responsible for doing the deletion required by the mapping, such as deletion of all the rows from join table for M-M, or optimized delete all of target objects for 1-M. Example, 'delete from PROJ_EMP where EMP_ID = #EMP_ID'.

        DataModifyQuery query = new DataModifyQuery();
        query.setSQLString(sqlString);
        setCustomDeleteAllQuery(query);
    
protected voidsetHasCustomDeleteAllQuery(boolean bool)

        hasCustomDeleteAllQuery = bool;
    
public voidsetSessionName(java.lang.String name)
PUBLIC: Set the name of the session to execute the mapping's queries under. This can be used by the session broker to override the default session to be used for the target class.

        getDeleteAllQuery().setSessionName(name);
        getSelectionQuery().setSessionName(name);
    
protected booleanshouldUseValueFromRowWithJoin(JoinedAttributeManager joinManager)
INTERNAL: Indicates whether valueFromRow should call valueFromRowInternalWithJoin (true) or valueFromRowInternal (false)

        return joinManager.getDataResults_()!=null && super.shouldUseValueFromRowWithJoin(joinManager);
    
public voidsimpleAddToCollectionChangeRecord(java.lang.Object referenceKey, java.lang.Object changeSetToAdd, ObjectChangeSet changeSet, oracle.toplink.essentials.internal.sessions.AbstractSession session)
ADVANCED: This method is used to have an object add to a collection once the changeSet is applied. The referenceKey parameter should only be used for direct Maps.

        CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
        if (collectionChangeRecord == null) {
            collectionChangeRecord = new CollectionChangeRecord(changeSet);
            collectionChangeRecord.setAttribute(getAttributeName());
            collectionChangeRecord.setMapping(this);
            collectionChangeRecord.getAddObjectList().put(changeSetToAdd, changeSetToAdd);
            collectionChangeRecord.getOrderedAddObjects().add(changeSetToAdd);
            changeSet.addChange(collectionChangeRecord);
        } else {
            getContainerPolicy().recordAddToCollectionInChangeRecord((ObjectChangeSet)changeSetToAdd, collectionChangeRecord);
        }
        if (referenceKey != null){
            ((ObjectChangeSet)changeSetToAdd).setNewKey(referenceKey);
        }
    
public voidsimpleRemoveFromCollectionChangeRecord(java.lang.Object referenceKey, java.lang.Object changeSetToRemove, ObjectChangeSet changeSet, oracle.toplink.essentials.internal.sessions.AbstractSession session)
ADVANCED: This method is used to have an object removed from a collection once the changeSet is applied. The referenceKey parameter should only be used for direct Maps.

        CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
        if (collectionChangeRecord == null) {
            collectionChangeRecord = new CollectionChangeRecord(changeSet);
            collectionChangeRecord.setAttribute(getAttributeName());
            collectionChangeRecord.setMapping(this);
            collectionChangeRecord.getRemoveObjectList().put(changeSetToRemove, changeSetToRemove);
            changeSet.addChange(collectionChangeRecord);
        } else {
            getContainerPolicy().recordRemoveFromCollectionInChangeRecord((ObjectChangeSet)changeSetToRemove, collectionChangeRecord);
        }
        if (referenceKey != null){
            ((ObjectChangeSet)changeSetToRemove).setOldKey(referenceKey);
        }
    
public voidupdateChangeRecord(java.lang.Object clone, java.lang.Object newValue, java.lang.Object oldValue, ObjectChangeSet objectChangeSet, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow)
INTERNAL: Either create a new change record or update with the new value. This is used by attribute change tracking. Specifically in a collection mapping this will be called when the customer Set a new collection. In this case we will need to mark the change record with the new and the old versions of the collection. And mark the ObjectChangeSet with the attribute name then when the changes are calculated force a compare on the collections to determine changes.

        CollectionChangeRecord collectionChangeRecord = (CollectionChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
        if (collectionChangeRecord == null) {
            collectionChangeRecord = new CollectionChangeRecord(objectChangeSet);
            collectionChangeRecord.setAttribute(getAttributeName());
            collectionChangeRecord.setMapping(this);
            objectChangeSet.addChange(collectionChangeRecord);
        }
        if (collectionChangeRecord.getOriginalCollection() == null){
            collectionChangeRecord.setOriginalCollection(oldValue);
        }
        collectionChangeRecord.setLatestCollection(newValue);
        
        objectChangeSet.deferredDetectionRequiredOn(getAttributeName());
    
public voiduseCollectionClass(java.lang.Class concreteClass)
PUBLIC: Configure the mapping to use an instance of the specified container class to hold the target objects.

The container class must implement (directly or indirectly) the java.util.Collection interface.

        ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass, hasOrderBy());
        setContainerPolicy(policy);
    
public voiduseMapClass(java.lang.Class concreteClass, java.lang.String keyName)
PUBLIC: Configure the mapping to use an instance of the specified container clas to hold the target objects. The key used to index a value in the Map is the value returned by either a call to a specified zero-argument method or the value of a field.

To facilitate resolving the keyName to a method or field, the mapping's referenceClass must set before calling this method.

Note: If the keyName is for a method, that method must be implemented by the class (or a superclass) of any value to be inserted into the Map.

The container class must implement (directly or indirectly) the java.util.Map interface.

        // the reference class has to be specified before coming here
        if (getReferenceClassName() == null) {
            throw DescriptorException.referenceClassNotSpecified(this);
        }
        
        ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass);
        policy.setKeyName(keyName, getReferenceClassName());
        setContainerPolicy(policy);
    
public voiduseMapClass(java.lang.Class concreteClass)
PUBLIC: Configure the mapping to use an instance of the specified container class to hold the target objects. The key used to index a value in the Map is an instance of the composite primary key class.

To facilitate resolving the primary key class, the mapping's referenceClass must set before calling this method.

The container class must implement (directly or indirectly) the java.util.Map interface.

        useMapClass(concreteClass, null);
    
public voiduseSortedSetClass(java.lang.Class concreteClass, java.util.Comparator comparator)
PUBLIC: Configure the mapping to use an instance of the specified container class to hold the target objects.

The container class must implement (directly or indirectly) the java.util.SortedSet interface.

        try {
            SortedCollectionContainerPolicy policy = (SortedCollectionContainerPolicy)ContainerPolicy.buildPolicyFor(concreteClass);
            policy.setComparator(comparator);
            setContainerPolicy(policy);
        } catch (ClassCastException e) {
            useCollectionClass(concreteClass);
        }
    
public voiduseTransparentCollection()
PUBLIC: If transparent indirection is used, a special collection will be placed in the source object's attribute. Fetching of the contents of the collection from the database will be delayed until absolutely necessary. (Any message sent to the collection will cause the contents to be faulted in from the database.) This can result in rather significant performance gains, without having to change the source object's attribute from Collection (or List or Vector) to ValueHolderInterface.

        setIndirectionPolicy(new TransparentIndirectionPolicy());
        useCollectionClass(ClassConstants.IndirectList_Class);
    
public voiduseTransparentList()
PUBLIC: If transparent indirection is used, a special collection will be placed in the source object's attribute. Fetching of the contents of the collection from the database will be delayed until absolutely necessary. (Any message sent to the collection will cause the contents to be faulted in from the database.) This can result in rather significant performance gains, without having to change the source object's attribute from List to ValueHolderInterface.

        setIndirectionPolicy(new TransparentIndirectionPolicy());
        useCollectionClass(ClassConstants.IndirectList_Class);
    
public voiduseTransparentMap(java.lang.String methodName)
PUBLIC: If transparent indirection is used, a special map will be placed in the source object's attribute. Fetching of the contents of the map from the database will be delayed until absolutely necessary. (Any message sent to the map will cause the contents to be faulted in from the database.) This can result in rather significant performance gains, without having to change the source object's attribute from Map (or Dictionary or Hashtable) to ValueHolderInterface.

The key used in the Map is the value returned by a call to the zero parameter method named methodName. The method should be a zero argument method implemented (or inherited) by the value to be inserted into the Map.

        setIndirectionPolicy(new TransparentIndirectionPolicy());
        useMapClass(ClassConstants.IndirectMap_Class, methodName);
    
public voiduseTransparentSet()
PUBLIC: If transparent indirection is used, a special collection will be placed in the source object's attribute. Fetching of the contents of the collection from the database will be delayed until absolutely necessary. (Any message sent to the collection will cause the contents to be faulted in from the database.) This can result in rather significant performance gains, without having to change the source object's attribute from Set to ValueHolderInterface.

        setIndirectionPolicy(new TransparentIndirectionPolicy());
        useCollectionClass(IndirectSet.class);
    
public voidvalidateBeforeInitialization(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: To validate mappings declaration

        super.validateBeforeInitialization(session);

        getIndirectionPolicy().validateContainerPolicy(session.getIntegrityChecker());

        if (getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) {
            Class attributeType = ((InstanceVariableAttributeAccessor)getAttributeAccessor()).getAttributeType();
            getIndirectionPolicy().validateDeclaredAttributeTypeForCollection(attributeType, session.getIntegrityChecker());
        } else if (getAttributeAccessor() instanceof MethodAttributeAccessor) {
            Class returnType = ((MethodAttributeAccessor)getAttributeAccessor()).getGetMethodReturnType();
            getIndirectionPolicy().validateGetMethodReturnTypeForCollection(returnType, session.getIntegrityChecker());

            Class parameterType = ((MethodAttributeAccessor)getAttributeAccessor()).getSetMethodParameterType();
            getIndirectionPolicy().validateSetMethodParameterTypeForCollection(parameterType, session.getIntegrityChecker());
        }
    
protected java.lang.ObjectvalueFromRowInternalWithJoin(oracle.toplink.essentials.internal.sessions.AbstractRecord row, JoinedAttributeManager joinManager, oracle.toplink.essentials.internal.sessions.AbstractSession executionSession)
INTERNAL: Return the value of the field from the row or a value holder on the query to obtain the object. To get here the mapping's isJoiningSupported() should return true, currently that's the case for only 1-m and m-m.

        // If the query was using joining, all of the result rows will have been set.
        List rows = joinManager.getDataResults_();
        Object value = getContainerPolicy().containerInstance();
        
        // A nested query must be built to pass to the descriptor that looks like the real query execution would,
        // these should be cached on the query during prepare.
        ObjectLevelReadQuery nestedQuery = null;
        if (joinManager.getJoinedMappingQueries_() != null) {
            nestedQuery = (ObjectLevelReadQuery) joinManager.getJoinedMappingQueries_().get(this);
        } else {
            nestedQuery = prepareNestedJoins(joinManager, executionSession);
        }
        nestedQuery.setSession(executionSession);                
        //CR #4365 - used to prevent infinite recursion on refresh object cascade all
        nestedQuery.setQueryId(joinManager.getBaseQuery().getQueryId());

        // Extract the primary key of the source object, to filter only the joined rows for that object.
        Vector sourceKey = getDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(row, executionSession);
        CacheKey sourceCacheKey = new CacheKey(sourceKey);
        
        // A set of target cache keys must be maintained to avoid duplicates from multiple 1-m joins.
        Set targetCacheKeys = new HashSet();

        // For each rows, extract the target row and build the target object and add to the collection.
        for (int index = 0; index < rows.size(); index++) {
            AbstractRecord sourceRow = (AbstractRecord)rows.get(index);
            AbstractRecord targetRow = sourceRow;
            
            // Row will be set to null if part of another object's join already processed.
            if (targetRow != null) {
                // CR #... the field for many objects may be in the row,
                // so build the subpartion of the row through the computed values in the query,
                // this also helps the field indexing match.
                targetRow = trimRowForJoin(targetRow, joinManager, executionSession);
                AbstractRecord pkRow = trimRowForJoin(sourceRow, new Integer(joinManager.getParentResultIndex()), executionSession);
                nestedQuery.setTranslationRow(targetRow);

                // Extract the primary key of the row to filter only the joined rows for the source object.
                Vector rowSourceKey = getDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(pkRow, executionSession);
                if(rowSourceKey != null) {
                    CacheKey rowSourceCacheKey = new CacheKey(rowSourceKey);
                    
                    // Only build/add the object if the join row is for the object.
                    if (sourceCacheKey.equals(rowSourceCacheKey)) {
                        // Partial object queries must select the primary key of the source and related objects.
                        // If the target joined rows in null (outerjoin) means an empty collection.
                        Vector targetKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromRow(targetRow, executionSession);
                        if (targetKey == null) {
                            // A null primary key means an empty collection returned as nulls from an outerjoin.
                            return getIndirectionPolicy().valueFromRow(value);
                        }
                        CacheKey targetCacheKey = new CacheKey(targetKey);
                        
                        // Only build/add the taregt object once, skip duplicates from multiple 1-m joins.
                        if (!targetCacheKeys.contains(targetCacheKey)) {
                            targetCacheKeys.add(targetCacheKey);
                            Object targetObject = getReferenceDescriptor().getObjectBuilder().buildObject(nestedQuery, targetRow, nestedQuery.getJoinedAttributeManager());
                            nestedQuery.setTranslationRow(null);
                            getContainerPolicy().addInto(targetObject, value, executionSession);
                        }
                    }
                } else {
                    // Clear an empty row
                    rows.set(index, null);
                }
            }
        }
        return getIndirectionPolicy().valueFromRow(value);
    
public booleanverifyDelete(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Checks if object is deleted from the database or not.

        // Row is built for translation
        if (isReadOnly()) {
            return true;
        }

        if (isPrivateOwned()) {
            Object objects = getRealCollectionAttributeValueFromObject(object, session);

            ContainerPolicy containerPolicy = getContainerPolicy();
            for (Object iter = containerPolicy.iteratorFor(objects); containerPolicy.hasNext(iter);) {
                if (!session.verifyDelete(containerPolicy.next(iter, session))) {
                    return false;
                }
            }
        }

        AbstractRecord row = getDescriptor().getObjectBuilder().buildRowForTranslation(object, session);

        //cr 3819 added the line below to fix the translationtable to ensure that it
        // contains the required values
        prepareTranslationRow(row, object, session);
        Object value = session.executeQuery(getSelectionQuery(), row);

        return getContainerPolicy().isEmpty(value);