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

AggregateObjectMapping

public class AggregateObjectMapping extends AggregateMapping implements RelationalMapping

Purpose:Two objects can be considered to be related by aggregation if there is a strict 1:1 relationship between the objects. This means that if the source (parent) object exists, then the target (child or owned) object must exist. In TopLink, it also means the data for the owned object is in the same table as the parent.

author
Sati
since
TOPLink/Java 1.0

Fields Summary
protected boolean
isNullAllowed
If all the fields in the database row for the aggregate object are NULL, then, by default, TopLink will place a null in the appropriate source object (as opposed to an aggregate object filled with nulls). To change this behavior, set the value of this variable to false. Then TopLink will build a new instance of the aggregate object that is filled with nulls and place it in the source object.
protected transient Map
aggregateToSourceFieldNames
Map the name of a field in the aggregate descriptor to a field in the source table.
Constructors Summary
public AggregateObjectMapping()
Default constructor.

        aggregateToSourceFieldNames = new HashMap(5);
        isNullAllowed = true;
    
Methods Summary
public voidaddFieldNameTranslation(java.lang.String sourceFieldName, java.lang.String aggregateFieldName)
PUBLIC: Add a field name translation that maps from a field name in the source table to a field name in the aggregate descriptor.

        String unQualifiedAggregateFieldName = aggregateFieldName.substring(aggregateFieldName.lastIndexOf('.") + 1);// -1 is returned for no ".".
        getAggregateToSourceFieldNames().put(unQualifiedAggregateFieldName, sourceFieldName);
    
public voidaddPrimaryKeyJoinField(DatabaseField primaryKeyField, DatabaseField secondaryField)
INTERNAL: Add a primary key join column (secondary field). If this contain primary keys and the descriptor(or its subclass) has multiple tables (secondary tables or joined inheritance strategy), this should also know the primary key join columns to handle some cases properly.

        // now it doesn't need to manage this as a separate table here,
        // it's enough just to add the mapping to ObjectBuilder.mappingsByField 
        ObjectBuilder builder = getReferenceDescriptor().getObjectBuilder();
        DatabaseMapping mapping = builder.getMappingForField(primaryKeyField);
        if (mapping != null) {
            builder.getMappingsByField().put(secondaryField, mapping); 
        }
    
protected booleanallAggregateFieldsAreNull(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow)
INTERNAL: Return whether all the aggregate fields in the specified row are NULL.

        for (Enumeration fieldsEnum = getReferenceFields().elements();
                 fieldsEnum.hasMoreElements();) {
            DatabaseField field = (DatabaseField)fieldsEnum.nextElement();
            Object value = databaseRow.get(field);
            if (value != null) {
                return false;
            }
        }
        return true;
    
public voidallowNull()
PUBLIC: If all the fields in the database row for the aggregate object are NULL, then, by default, TopLink will place a null in the appropriate source object (as opposed to an aggregate object filled with nulls). This behavior can be explicitly set by calling #allowNull(). To change this behavior, call #dontAllowNull(). Then TopLink will build a new instance of the aggregate object that is filled with nulls and place it in the source object. In either situation, when writing, TopLink will place a NULL in all the fields in the database row for the aggregate object.

        setIsNullAllowed(true);
    
protected booleanbackupAttributeValueIsNull(WriteObjectQuery query)
INTERNAL: Return whether the query's backup object has an attribute value of null.

        if (query.getSession().isUnitOfWork()) {
            Object backupAttributeValue = getAttributeValueFromObject(query.getBackupClone());
            if (backupAttributeValue == null) {
                return true;
            }
        }
        return false;
    
protected java.lang.ObjectbuildAggregateFromRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, java.lang.Object targetObject, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinManager, boolean buildShallowOriginal, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Build and return an aggregate object from the specified row. If a null value is allowed and all the appropriate fields in the row are NULL, return a null. Otherwise, simply create a new aggregate object and return it.

        // check for all NULLs
        if (isNullAllowed() && allAggregateFieldsAreNull(databaseRow)) {
            return null;
        }

        // If refreshing, maintain object identity;
        // otherwise construct a new aggregate object.
        Object aggregate = null;
        ClassDescriptor descriptor = getReferenceDescriptor();
        boolean refreshing = true;
        if (descriptor.hasInheritance()) {
            Class newAggregateClass = descriptor.getInheritancePolicy().classFromRow(databaseRow, session);
            descriptor = getReferenceDescriptor(newAggregateClass, session);

            if (joinManager.getBaseQuery().shouldRefreshIdentityMapResult()) {
                aggregate = getMatchingAttributeValueFromObject(databaseRow, targetObject, session, descriptor);
                if ((aggregate != null) && (aggregate.getClass() != newAggregateClass)) {
                    // if the class has changed out from underneath us, we cannot preserve object identity
                    // build a new instance of the *new* class
                    aggregate = descriptor.getObjectBuilder().buildNewInstance();
                    refreshing = false;
                }
            }
        } else {
            if (joinManager.getBaseQuery().shouldRefreshIdentityMapResult()) {
                aggregate = getMatchingAttributeValueFromObject(databaseRow, targetObject, session, descriptor);
            }
        }

        if (aggregate == null) {
            aggregate = descriptor.getObjectBuilder().buildNewInstance();
            refreshing = false;
        }

        ObjectBuildingQuery nestedQuery = joinManager.getBaseQuery();
        nestedQuery.setSession(session); //ensure the correct session is set on the query.
        if (joinManager.getBaseQuery().isObjectLevelReadQuery()){
            if (joinManager.isAttributeJoined(getDescriptor(), getAttributeName()) ){
                // A nested query must be built to pass to the descriptor that looks like the real query execution would.
                nestedQuery = (ObjectLevelReadQuery)((ObjectLevelReadQuery)joinManager.getBaseQuery()).deepClone();
                // Must cascade the nested partial/join expression and filter the nested ones.
                ((ObjectLevelReadQuery)nestedQuery).getJoinedAttributeManager().setJoinedAttributeExpressions_(extractNestedExpressions(joinManager.getJoinedAttributeExpressions(), joinManager.getBaseExpressionBuilder(), false));
                nestedQuery.setDescriptor(descriptor);
            }
        }
        if (buildShallowOriginal) {
            descriptor.getObjectBuilder().buildAttributesIntoShallowObject(aggregate, databaseRow, nestedQuery);
        } else if (session.isUnitOfWork()) {
            descriptor.getObjectBuilder().buildAttributesIntoWorkingCopyClone(aggregate, nestedQuery, joinManager, databaseRow, (UnitOfWorkImpl)session, refreshing);
        } else {
            descriptor.getObjectBuilder().buildAttributesIntoObject(aggregate, databaseRow, nestedQuery, joinManager, refreshing);
        }
        return aggregate;
    
public voidbuildClone(java.lang.Object original, java.lang.Object clone, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinedAttributeManager)
INTERNAL: Clone the attribute from the original and assign it to the clone.

        Object attributeValue = getAttributeValueFromObject(original);
        Object aggregateClone = buildClonePart(original, attributeValue, unitOfWork);

        if (aggregateClone != null) {
            ClassDescriptor descriptor = getReferenceDescriptor(aggregateClone, unitOfWork);
            descriptor.getObjectChangePolicy().setAggregateChangeListener(clone, aggregateClone, unitOfWork, descriptor, getAttributeName());
        }

        setAttributeValueInObject(clone, aggregateClone);
    
public voidbuildCloneFromRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinManager, java.lang.Object clone, ObjectBuildingQuery sourceQuery, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork, oracle.toplink.essentials.internal.sessions.AbstractSession executionSession)
INTERNAL: A combination of readFromRowIntoObject and buildClone.

buildClone assumes the attribute value exists on the original and can simply be copied.

readFromRowIntoObject assumes that one is building an original.

Both of the above assumptions are false in this method, and actually attempts to do both at the same time.

Extract value from the row and set the attribute to this value in the working copy clone. In order to bypass the shared cache when in transaction a UnitOfWork must be able to populate working copies directly from the row.

        // This method is a combination of buildggregateFromRow and
        // buildClonePart on the super class.
        // none of buildClonePart used, as not an orignal new object, nor
        // do we worry about creating heavy clones for aggregate objects.
        Object clonedAttributeValue = buildAggregateFromRow(databaseRow, clone, joinManager, false, executionSession);
        ClassDescriptor descriptor = getReferenceDescriptor(clonedAttributeValue, unitOfWork);
        descriptor.getObjectChangePolicy().setAggregateChangeListener(clone, clonedAttributeValue, unitOfWork, descriptor, getAttributeName());
        setAttributeValueInObject(clone, clonedAttributeValue);
        return;
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildNullReferenceRow()
INTERNAL: Build and return a database row with all the reference fields set to nulls.

        AbstractRecord result = new DatabaseRecord(getReferenceFields().size());
        for (Enumeration stream = getReferenceFields().elements(); stream.hasMoreElements();) {
            result.put((DatabaseField)stream.nextElement(), null);
        }
        return result;
    
public ExpressionbuildObjectJoinExpression(Expression expression, Expression argument, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Used to allow object level comparisons.

        Expression attributeByAttributeComparison = null;

        //Enumeration mappingsEnum = getSourceToTargetKeyFields().elements();
        Enumeration mappingsEnum = getReferenceDescriptor().getMappings().elements();
        for (; mappingsEnum.hasMoreElements();) {
            DatabaseMapping mapping = (DatabaseMapping)mappingsEnum.nextElement();
            String attributeName = mapping.getAttributeName();
            Expression join = expression.get(attributeName).equal(argument.get(attributeName));
            if (attributeByAttributeComparison == null) {
                attributeByAttributeComparison = join;
            } else {
                attributeByAttributeComparison = attributeByAttributeComparison.and(join);
            }
        }
        return attributeByAttributeComparison;
    
public ExpressionbuildObjectJoinExpression(Expression expression, java.lang.Object value, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Used to allow object level comparisons. In the case of an Aggregate which has no primary key must do an attribute by attribute comparison.

        Expression attributeByAttributeComparison = null;
        Expression join = null;
        Object attributeValue = null;

        // value need not be unwrapped as it is an aggregate, nor should it
        // influence a call to getReferenceDescriptor.
        ClassDescriptor referenceDescriptor = getReferenceDescriptor();
        if ((value != null) && !referenceDescriptor.getJavaClass().isInstance(value)) {
            throw QueryException.incorrectClassForObjectComparison(expression, value, this);
        }
        Enumeration mappings = referenceDescriptor.getMappings().elements();
        for (; mappings.hasMoreElements();) {
            DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
            if (value == null) {
                attributeValue = null;
            } else {
                attributeValue = mapping.getAttributeValueFromObject(value);
            }
            join = expression.get(mapping.getAttributeName()).equal(attributeValue);
            if (attributeByAttributeComparison == null) {
                attributeByAttributeComparison = join;
            } else {
                attributeByAttributeComparison = attributeByAttributeComparison.and(join);
            }
        }
        return attributeByAttributeComparison;
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildRowFromAggregate(java.lang.Object object, java.lang.Object attributeValue, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Build and return a database row built with the values from the specified attribute value.

        return buildRowFromAggregate(object, attributeValue, session, false);
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildRowFromAggregate(java.lang.Object object, java.lang.Object attributeValue, oracle.toplink.essentials.internal.sessions.AbstractSession session, boolean forceWriteOfReadOnlyClasses)
INTERNAL: Build and return a database row built with the values from the specified attribute value.

        if (attributeValue == null) {
            if (isNullAllowed()) {
                return buildNullReferenceRow();
            } else {
                throw DescriptorException.nullForNonNullAggregate(object, this);
            }
        } else {
            if ((!forceWriteOfReadOnlyClasses) && (session.isClassReadOnly(attributeValue.getClass()))) {
                return new DatabaseRecord(1);
            } else {
                return getObjectBuilder(attributeValue, session).buildRow(attributeValue, session);
            }
        }
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildRowFromAggregateForUpdate(WriteObjectQuery query, java.lang.Object attributeValue)
INTERNAL: Build and return a database row built with the changed values from the specified attribute value.

        if (attributeValue == null) {
            if (isNullAllowed()) {
                if (backupAttributeValueIsNull(query)) {
                    return new DatabaseRecord(1);// both attributes are null - no update required
                } else {
                    return buildNullReferenceRow();
                }
            } else {
                throw DescriptorException.nullForNonNullAggregate(query.getObject(), this);
            }
        } else if ((query.getBackupClone() != null) && ((getMatchingBackupAttributeValue(query, attributeValue) == null) || !(attributeValue.getClass().equals(getMatchingBackupAttributeValue(query, attributeValue).getClass())))) {
            return getObjectBuilder(attributeValue, query.getSession()).buildRow(attributeValue, query.getSession());
        } else {
            if (query.getSession().isClassReadOnly(attributeValue.getClass())) {
                return new DatabaseRecord(1);
            }
            WriteObjectQuery clonedQuery = (WriteObjectQuery)query.clone();
            clonedQuery.setObject(attributeValue);
            if (query.getSession().isUnitOfWork()) {
                Object backupAttributeValue = getMatchingBackupAttributeValue(query, attributeValue);
                if (backupAttributeValue == null) {
                    backupAttributeValue = getObjectBuilder(attributeValue, query.getSession()).buildNewInstance();
                }
                clonedQuery.setBackupClone(backupAttributeValue);
            }
            return getObjectBuilder(attributeValue, query.getSession()).buildRowForUpdate(clonedQuery);
        }
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildRowFromAggregateWithChangeRecord(ChangeRecord changeRecord, ObjectChangeSet objectChangeSet, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Build and return a database row built with the values from the specified attribute value.

        return buildRowFromAggregateWithChangeRecord(changeRecord, objectChangeSet, session, false);
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildRowFromAggregateWithChangeRecord(ChangeRecord changeRecord, ObjectChangeSet objectChangeSet, oracle.toplink.essentials.internal.sessions.AbstractSession session, boolean forceWriteOfReadOnlyClasses)
INTERNAL: Build and return a database row built with the values from the specified attribute value.

        if (objectChangeSet == null) {
            if (isNullAllowed()) {
                return buildNullReferenceRow();
            } else {
                Object object = ((ObjectChangeSet)changeRecord.getOwner()).getUnitOfWorkClone();
                throw DescriptorException.nullForNonNullAggregate(object, this);
            }
        } else {
            if ((!forceWriteOfReadOnlyClasses) && (session.isClassReadOnly(objectChangeSet.getClassType(session)))) {
                return new DatabaseRecord(1);
            } else {
                return getReferenceDescriptor(objectChangeSet.getClassType(session), session).getObjectBuilder().buildRowWithChangeSet(objectChangeSet, session);
            }
        }
    
public voidbuildShallowOriginalFromRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, java.lang.Object original, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinManager, oracle.toplink.essentials.internal.sessions.AbstractSession executionSession)
INTERNAL: Builds a shallow original object. Only direct attributes and primary keys are populated. In this way the minimum original required for instantiating a working copy clone can be built without placing it in the shared cache (no concern over cycles).

        Object aggregate = buildAggregateFromRow(databaseRow, original, joinManager, true, executionSession);// shallow only.
        setAttributeValueInObject(original, aggregate);
    
protected oracle.toplink.essentials.internal.sessions.AbstractRecordbuildTemplateInsertRow(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Build and return a "template" database row with all the fields set to null.

        AbstractRecord result = getReferenceDescriptor().getObjectBuilder().buildTemplateInsertRow(session);
        List processedMappings = (List)getReferenceDescriptor().getMappings().clone();
        if (getReferenceDescriptor().hasInheritance()) {
            Enumeration children = getReferenceDescriptor().getInheritancePolicy().getChildDescriptors().elements();
            while (children.hasMoreElements()) {
                Enumeration mappings = ((ClassDescriptor)children.nextElement()).getMappings().elements();
                while (mappings.hasMoreElements()) {
                    DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();

                    // Only write mappings once.
                    if (!processedMappings.contains(mapping)) {
                        mapping.writeInsertFieldsIntoRow(result, session);
                        processedMappings.add(mapping);
                    }
                }
            }
        }
        return result;
    
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

        //objects referenced by this mapping are not registered as they have
        // no identity, however mappings from the referenced object may need cascading.
        Object objectReferenced = getRealAttributeValueFromObject(object, uow);
        if ((objectReferenced == null)){
            return ;
        }
        if ( ! visitedObjects.contains(objectReferenced)){
            visitedObjects.put(objectReferenced, objectReferenced);
            ObjectBuilder builder = getReferenceDescriptor(objectReferenced.getClass(), uow).getObjectBuilder();
            builder.cascadePerformRemove(objectReferenced, uow, 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

        //aggregate objects are not registered but their mappings should be.
        Object objectReferenced = getRealAttributeValueFromObject(object, uow);
        if ( (objectReferenced == null) ){
            return ;
        }
        if ( ! visitedObjects.contains(objectReferenced)){
            visitedObjects.put(objectReferenced, objectReferenced);
            ObjectBuilder builder = getReferenceDescriptor(objectReferenced.getClass(), uow).getObjectBuilder();
            builder.cascadeRegisterNewForCreate(objectReferenced, uow, visitedObjects);
        }
    
protected java.util.VectorcollectFields()
INTERNAL: Return the fields handled by the mapping.

        return getReferenceFields();
    
public voiddontAllowNull()
PUBLIC: If all the fields in the database row for the aggregate object are NULL, then, by default, TopLink will place a null in the appropriate source object (as opposed to an aggregate object filled with nulls). This behavior can be explicitly set by calling #allowNull(). To change this behavior, call #dontAllowNull(). Then TopLink will build a new instance of the aggregate object that is filled with nulls and place it in the source object. In either situation, when writing, TopLink will place a NULL in all the fields in the database row for the aggregate object.

        setIsNullAllowed(false);
    
public java.util.VectorgetAggregateToSourceFieldNameAssociations()
INTERNAL: Return a collection of the aggregate to source field name associations.

        Vector<Association> associations = new Vector(getAggregateToSourceFieldNames().size());
        Iterator aggregateEnum = getAggregateToSourceFieldNames().keySet().iterator();
        Iterator sourceEnum = getAggregateToSourceFieldNames().values().iterator();
        while (aggregateEnum.hasNext()) {
            associations.addElement(new Association(aggregateEnum.next(), sourceEnum.next()));
        }

        return associations;
    
public java.util.MapgetAggregateToSourceFieldNames()
INTERNAL: Return the hashtable that stores aggregate field name to source field name.

        return aggregateToSourceFieldNames;
    
public java.lang.ClassgetFieldClassification(DatabaseField fieldToClassify)
INTERNAL: Return the classification for the field contained in the mapping. This is used to convert the row value to a consistent Java value.

        DatabaseMapping mapping = getReferenceDescriptor().getObjectBuilder().getMappingForField(fieldToClassify);
        if (mapping == null) {
            return null;// Means that the mapping is read-only
        }
        return mapping.getFieldClassification(fieldToClassify);
    
protected java.lang.ObjectgetMatchingAttributeValueFromObject(oracle.toplink.essentials.internal.sessions.AbstractRecord row, java.lang.Object targetObject, oracle.toplink.essentials.internal.sessions.AbstractSession session, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor)
INTERNAL: This is used to preserve object identity during a refreshObject() query. Return the object corresponding to the specified database row. The default is to simply return the attribute value.

        return getAttributeValueFromObject(targetObject);
    
protected java.lang.ObjectgetMatchingBackupAttributeValue(WriteObjectQuery query, java.lang.Object attributeValue)
INTERNAL: This is used to match up objects during an update in a UOW. Return the object corresponding to the specified attribute value. The default is to simply return the backup attribute value.

        return getAttributeValueFromObject(query.getBackupClone());
    
protected oracle.toplink.essentials.descriptors.ClassDescriptorgetReferenceDescriptor(java.lang.Class theClass, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Since aggregate object mappings clone their descriptors, for inheritance the correct child clone must be found.

        if (getReferenceDescriptor().getJavaClass().equals(theClass)) {
            return getReferenceDescriptor();
        }

        ClassDescriptor subclassDescriptor = getReferenceDescriptor().getInheritancePolicy().getSubclassDescriptor(theClass);
        if (subclassDescriptor == null) {
            throw DescriptorException.noSubClassMatch(theClass, this);
        } else {
            return subclassDescriptor;
        }
    
protected java.util.VectorgetReferenceFields()
INTERNAL: Return the fields used to build the aggregate object.

        return getReferenceDescriptor().getAllFields();
    
public booleanhasDependency()
INTERNAL: Return if the mapping has any ownership or other dependency over its target object(s).

        return getReferenceDescriptor().hasDependencyOnParts();
    
public voidinitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: For an aggregate mapping the reference descriptor is cloned. The cloned descriptor is then assigned primary keys and table names before initialize. Once the cloned descriptor is initialized it is assigned as reference descriptor in the aggregate mapping. This is a very specific behaviour for aggregate mappings. The original descriptor is used only for creating clones and after that the aggregate mapping never uses it. Some initialization is done in postInitialize to ensure the target descriptor's references are initialized.

        super.initialize(session);

        ClassDescriptor clonedDescriptor = (ClassDescriptor)getReferenceDescriptor().clone();
        if (clonedDescriptor.isChildDescriptor()) {
            ClassDescriptor parentDescriptor = session.getDescriptor(clonedDescriptor.getInheritancePolicy().getParentClass());
            initializeParentInheritance(parentDescriptor, clonedDescriptor, session);
        }
        setReferenceDescriptor(clonedDescriptor);

        initializeReferenceDescriptor(clonedDescriptor);
        clonedDescriptor.preInitialize(session);
        clonedDescriptor.initialize(session);
        translateFields(clonedDescriptor, session);

        if (clonedDescriptor.hasInheritance() && clonedDescriptor.getInheritancePolicy().hasChildren()) {
            //clone child descriptors
            initializeChildInheritance(clonedDescriptor, session);
        }

        setFields(collectFields());
    
public voidinitializeChildInheritance(oracle.toplink.essentials.descriptors.ClassDescriptor parentDescriptor, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: For an aggregate mapping the reference descriptor is cloned. If the reference descriptor is involved in an inheritance tree, all the parent and child descriptors are cloned also. The cloned descriptors are then assigned primary keys and table names before initialize. This is a very specific behaviour for aggregate mappings.

        //recursive call to the further childern descriptors
        if (parentDescriptor.getInheritancePolicy().hasChildren()) {
            //setFields(clonedChildDescriptor.getFields());		
            Vector childDescriptors = parentDescriptor.getInheritancePolicy().getChildDescriptors();
            Vector cloneChildDescriptors = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
            for (Enumeration enumtr = childDescriptors.elements(); enumtr.hasMoreElements();) {
                ClassDescriptor clonedChildDescriptor = (ClassDescriptor)((ClassDescriptor)enumtr.nextElement()).clone();
                clonedChildDescriptor.getInheritancePolicy().setParentDescriptor(parentDescriptor);
                initializeReferenceDescriptor(clonedChildDescriptor);
                clonedChildDescriptor.preInitialize(session);
                clonedChildDescriptor.initialize(session);
                translateFields(clonedChildDescriptor, session);
                cloneChildDescriptors.addElement(clonedChildDescriptor);
                initializeChildInheritance(clonedChildDescriptor, session);
            }
            parentDescriptor.getInheritancePolicy().setChildDescriptors(cloneChildDescriptors);
        }
    
public voidinitializeParentInheritance(oracle.toplink.essentials.descriptors.ClassDescriptor parentDescriptor, oracle.toplink.essentials.descriptors.ClassDescriptor childDescriptor, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: For an aggregate mapping the reference descriptor is cloned. If the reference descriptor is involved in an inheritance tree, all the parent and child descriptors are cloned also. The cloned descriptors are then assigned primary keys and table names before initialize. This is a very specific behaviour for aggregate mappings.

        ClassDescriptor clonedParentDescriptor = (ClassDescriptor)parentDescriptor.clone();

        //recursive call to the further parent descriptors
        if (clonedParentDescriptor.getInheritancePolicy().isChildDescriptor()) {
            ClassDescriptor parentToParentDescriptor = session.getDescriptor(clonedParentDescriptor.getJavaClass());
            initializeParentInheritance(parentToParentDescriptor, parentDescriptor, session);
        }

        initializeReferenceDescriptor(clonedParentDescriptor);
        Vector children = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
        children.addElement(childDescriptor);
        clonedParentDescriptor.getInheritancePolicy().setChildDescriptors(children);
        clonedParentDescriptor.preInitialize(session);
        clonedParentDescriptor.initialize(session);
        translateFields(clonedParentDescriptor, session);
    
protected voidinitializeReferenceDescriptor(oracle.toplink.essentials.descriptors.ClassDescriptor clonedDescriptor)
INTERNAL: Initialize the cloned reference descriptor with table names and primary keys

        // Must ensure default tables remains the same.
        clonedDescriptor.setDefaultTable(getDescriptor().getDefaultTable());
        clonedDescriptor.setTables(getDescriptor().getTables());
        clonedDescriptor.setPrimaryKeyFields(getDescriptor().getPrimaryKeyFields());
    
public booleanisAggregateObjectMapping()
INTERNAL: Related mapping should implement this method to return true.

        return true;
    
public booleanisCascadedLockingSupported()
INTERNAL Return true if this mapping supports cascaded version optimistic locking.

        return true;
    
public booleanisChangeTrackingSupported()
INTERNAL: Return if this mapping supports change tracking.

        return false;
    
public booleanisNullAllowed()
INTERNAL: Return setting.

        return isNullAllowed;
    
public booleanisRelationalMapping()
INTERNAL:

        return true;
    
public voidpostInitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: For an aggregate mapping the reference descriptor is cloned. The cloned descriptor is then assigned primary keys and table names before initialize. Once the cloned descriptor is initialized it is assigned as reference descriptor in the aggregate mapping. This is a very specific behaviour for aggregate mappings. The original descriptor is used only for creating clones and after that the aggregate mapping never uses it. Some initialization is done in postInitialize to ensure the target descriptor's references are initialized.

        super.postInitialize(session);

        if (getReferenceDescriptor() != null) {
            getReferenceDescriptor().postInitialize(session);
        }
    
public java.lang.ObjectreadFromReturnRowIntoObject(oracle.toplink.essentials.internal.sessions.AbstractRecord row, java.lang.Object targetObject, ReadObjectQuery query, java.util.Collection handledMappings)
INTERNAL: Build an aggregate object from the specified return row and put it in the specified target object. Return row is merged into object after execution of insert or update call accordiing to ReturningPolicy.

        Object aggregate = getAttributeValueFromObject(targetObject);
        if (aggregate == null) {
            aggregate = readFromRowIntoObject(row, null, targetObject, query);
            handledMappings.add(this);
            return aggregate;
        }

        for (int i = 0; i < getReferenceFields().size(); i++) {
            DatabaseField field = (DatabaseField)getReferenceFields().elementAt(i);
            if (row.containsKey(field)) {
                getObjectBuilder(aggregate, query.getSession()).assignReturnValueForField(aggregate, query, row, field, handledMappings);
            }
        }

        if (isNullAllowed()) {
            boolean allAttributesNull = true;
            for (int i = 0; (i < getReferenceFields().size()) && allAttributesNull; i++) {
                DatabaseField field = (DatabaseField)fields.elementAt(i);
                if (row.containsKey(field)) {
                    allAttributesNull = row.get(field) == null;
                } else {
                    Object fieldValue = valueFromObject(targetObject, field, query.getSession());
                    if (fieldValue == null) {
                        Object baseValue = getDescriptor().getObjectBuilder().getBaseValueForField(field, targetObject);
                        if (baseValue != null) {
                            DatabaseMapping baseMapping = getDescriptor().getObjectBuilder().getBaseMappingForField(field);
                            if (baseMapping.isForeignReferenceMapping()) {
                                ForeignReferenceMapping refMapping = (ForeignReferenceMapping)baseMapping;
                                if (refMapping.usesIndirection()) {
                                    allAttributesNull = refMapping.getIndirectionPolicy().objectIsInstantiated(baseValue);
                                }
                            }
                        }
                    } else {
                        allAttributesNull = false;
                    }
                }
            }
            if (allAttributesNull) {
                aggregate = null;
                setAttributeValueInObject(targetObject, aggregate);
            }
        }
        handledMappings.add(this);
        return aggregate;
    
public java.lang.ObjectreadFromRowIntoObject(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinManager, java.lang.Object targetObject, ObjectBuildingQuery sourceQuery, oracle.toplink.essentials.internal.sessions.AbstractSession executionSession)
INTERNAL: Build an aggregate object from the specified row and put it in the specified target object.

        Object aggregate = buildAggregateFromRow(databaseRow, targetObject, joinManager, false, executionSession);// don't just build a shallow original
        setAttributeValueInObject(targetObject, aggregate);
        return aggregate;
    
public voidrehashFieldDependancies(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Rehash any hashtables based on fields. This is used to clone descriptors for aggregates, which hammer field names.

        getReferenceDescriptor().rehashFieldDependancies(session);
    
public voidsetAggregateToSourceFieldNameAssociations(java.util.Vector fieldAssociations)
INTERNAL: Set a collection of the aggregate to source field name associations.

        Hashtable fieldNames = new Hashtable(fieldAssociations.size() + 1);
        for (Enumeration associationsEnum = fieldAssociations.elements();
                 associationsEnum.hasMoreElements();) {
            Association association = (Association)associationsEnum.nextElement();
            fieldNames.put(association.getKey(), association.getValue());
        }

        setAggregateToSourceFieldNames(fieldNames);
    
protected voidsetAggregateToSourceFieldNames(java.util.Map aggregateToSource)
INTERNAL: Set the hashtable that stores target field name to the source field name.

        aggregateToSourceFieldNames = aggregateToSource;
    
public voidsetIsNullAllowed(boolean aBoolean)
INTERNAL: Will be used by Gromit only.

        isNullAllowed = aBoolean;
    
protected voidtranslateFields(oracle.toplink.essentials.descriptors.ClassDescriptor clonedDescriptor, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: If field names are different in the source and aggregate objects then the translation is done here. The aggregate field name is converted to source field name from the field name mappings stored.

        for (Enumeration entry = clonedDescriptor.getFields().elements(); entry.hasMoreElements();) {
            DatabaseField field = (DatabaseField)entry.nextElement();
            String nameInAggregate = field.getName();
            String nameInSource = (String)getAggregateToSourceFieldNames().get(nameInAggregate);

            // Do not modify non-translated fields.
            if (nameInSource != null) {
                DatabaseField fieldInSource = new DatabaseField(nameInSource);

                // Check if the translated field specified a table qualifier.
                if (fieldInSource.getName().equals(nameInSource)) {
                    // No table so just set the field name.
                    field.setName(nameInSource);
                } else {
                    // There is a table, so set the name and table.
                    field.setName(fieldInSource.getName());
                    field.setTable(clonedDescriptor.getTable(fieldInSource.getTable().getName()));
                }
            }
        }

        clonedDescriptor.rehashFieldDependancies(session);
    
public java.lang.ObjectvalueFromObject(java.lang.Object object, DatabaseField field, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Extract value of the field from the object

        Object attributeValue = getAttributeValueFromObject(object);
        if (attributeValue == null) {
            if (isNullAllowed()) {
                return null;
            } else {
                throw DescriptorException.nullForNonNullAggregate(object, this);
            }
        } else {
            return getObjectBuilder(attributeValue, session).extractValueFromObjectForField(attributeValue, field, session);
        }
    
public voidwriteFromAttributeIntoRow(java.lang.Object attribute, oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: A subclass should implement this method if it wants different behaviour. Write the foreign key values from the attribute to the row.

          AbstractRecord targetRow = buildRowFromAggregate(null, attribute, session);
          for (Enumeration stream = targetRow.keys(); stream.hasMoreElements(); ) {
                  DatabaseField field = (DatabaseField) stream.nextElement();
                  Object value = targetRow.get(field);
                  row.put(field, value);
          }
    
public voidwriteFromObjectIntoRow(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Get the attribute value from the object and add the appropriate values to the specified database row.

        if (isReadOnly()) {
            return;
        }
        AbstractRecord targetRow = buildRowFromAggregate(object, getAttributeValueFromObject(object), session);
        for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
            DatabaseField field = (DatabaseField)stream.nextElement();
            Object value = targetRow.get(field);
            databaseRow.add(field, value);
        }
    
public voidwriteFromObjectIntoRowForUpdate(WriteObjectQuery query, oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow)
INTERNAL: Get the attribute value from the object and add the changed values to the specified database row.

        if (isReadOnly()) {
            return;
        }
        AbstractRecord targetRow = buildRowFromAggregateForUpdate(query, getAttributeValueFromObject(query.getObject()));
        for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
            DatabaseField field = (DatabaseField)stream.nextElement();
            Object value = targetRow.get(field);
            databaseRow.add(field, value);
        }
    
public voidwriteFromObjectIntoRowWithChangeRecord(ChangeRecord changeRecord, oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Get the attribute value from the object and add the appropriate values to the specified database row.

        if (isReadOnly()) {
            return;
        }
        AbstractRecord targetRow = buildRowFromAggregateWithChangeRecord(changeRecord, (ObjectChangeSet)((AggregateChangeRecord)changeRecord).getChangedObject(), session);
        for (Enumeration stream = targetRow.keys(); stream.hasMoreElements();) {
            DatabaseField field = (DatabaseField)stream.nextElement();
            Object value = targetRow.get(field);
            databaseRow.add(field, value);
        }
    
public voidwriteInsertFieldsIntoRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Write fields needed for insert into the template for with null values.

        if (isReadOnly()) {
            return;
        }

        AbstractRecord targetRow = buildTemplateInsertRow(session);
        for (Enumeration keyEnum = targetRow.keys(); keyEnum.hasMoreElements();) {
            DatabaseField field = (DatabaseField)keyEnum.nextElement();
            Object value = targetRow.get(field);

            //CR-3286097 - Should use add not put, to avoid linear search.
            databaseRow.add(field, value);
        }