FileDocCategorySizeDatePackage
AbstractDirectMapping.javaAPI DocGlassfish v2 API29608Tue May 22 16:54:46 BST 2007oracle.toplink.essentials.mappings.foundation

AbstractDirectMapping

public abstract class AbstractDirectMapping extends oracle.toplink.essentials.mappings.DatabaseMapping
Purpose: Maps an attribute to the corresponding database field type. The list of field types that are supported by TopLink's direct to field mapping is dependent on the relational database being used. A converter can be used to convert between the object and data type if they do not match.
see
Converter
see
ObjectTypeConverter
see
TypeConversionConverter
see
SerializedObjectConverter
author
Sati
since
TopLink/Java 1.0

Fields Summary
protected DatabaseField
field
DatabaseField which this mapping represents.
protected transient Class
attributeClassification
To specify the conversion type
protected transient String
attributeClassificationName
protected transient Class
attributeObjectClassification
PERF: Also store object class of attribute in case of primitive.
protected Converter
converter
Allows user defined conversion between the object attribute value and the database value.
protected transient Object
nullValue
Support specification of the value to use for null.
protected Boolean
isMutable
PERF: Indicates if this mapping's attribute is a simple atomic value and cannot be modified, only replaced. This is a tri-state to allow user to set to true or false, as default is false but some data-types such as Calendar or byte[] or converter types may be desired to be used as mutable.
Constructors Summary
public AbstractDirectMapping()
Default constructor.

        super();
        this.setWeight(WEIGHT_1);
    
Methods Summary
public voidbuildBackupClone(java.lang.Object clone, java.lang.Object backup, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl unitOfWork)
INTERNAL: Clone the attribute from the clone and assign it to the backup.

        buildClone(clone, backup, unitOfWork, null);
    
public ChangeRecordbuildChangeRecord(java.lang.Object clone, ObjectChangeSet owner, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Directly build a change record without comparison

        return internalBuildChangeRecord(getAttributeValueFromObject(clone), owner);
    
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.

        buildCloneValue(original, clone, unitOfWork);
    
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: 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.

        // Even though the correct value may exist on the original, we can't
        // make that assumption.  It is easy to just build it again from the
        // row even if copy policy already copied it.
        // That optimization is lost.
        Object attributeValue = valueFromRow(databaseRow, joinManager, sourceQuery, executionSession);

        setAttributeValueInObject(clone, attributeValue);
    
public voidbuildCloneValue(java.lang.Object original, java.lang.Object clone, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Clone the attribute from the original and assign it to the clone.

        // Optimized for clone copy policy, setting of the value is not required.
        if (isCloningRequired()) {
            Object attributeValue = getAttributeValueFromObject(original);
            if (isMutable()) {
                attributeValue = getAttributeValue(getFieldValue(attributeValue, session), session);
            }
            setAttributeValueInObject(clone, attributeValue);
        }
    
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.

        buildCloneValue(original, copy, policy.getSession());
    
public voidbuildShallowOriginalFromRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, java.lang.Object original, ObjectBuildingQuery query, 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).

parameter
original later the input to buildCloneFromRow

        readFromRowIntoObject(databaseRow, null, original, query, executionSession);
    
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, this is a no-op.
    
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

        //objects referenced by this mapping are not registered as they have
        // no identity, this is a no-op.
    
public java.lang.Objectclone()
INTERNAL: The mapping clones itself to create deep copy.

        AbstractDirectMapping clone = (AbstractDirectMapping)super.clone();

        // Field must be cloned so aggregates do not share fields.
        clone.setField((DatabaseField)getField().clone());

        return clone;
    
protected java.util.VectorcollectFields()
Returns the field this mapping represents.

        Vector databaseField = new Vector(1);

        databaseField.addElement(getField());
        return databaseField;
    
public ChangeRecordcompareForChange(java.lang.Object clone, java.lang.Object backUp, ObjectChangeSet owner, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Compare the clone and backup clone values and return a change record if the value changed.

        // same code as write from object into row for update
        if ((owner.isNew()) || (!compareObjects(backUp, clone, session))) {
            return buildChangeRecord(clone, owner, session);
        }
        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 one = getAttributeValueFromObject(firstObject);
        Object two = getAttributeValueFromObject(secondObject);

        // PERF: Check identity before conversion.
        if (one == two) {
            return true;
        }

        // CR2114 - following two lines modified; getFieldValue() needs class as an argument
        one = getFieldValue(one, session);
        two = getFieldValue(two, session);
        // PERF:  Check identity/nulls before special type comparison.
        if (one == two) {
            return true;
        }

        if ((one == null) || (two == null)) {
            return false;
        }

        // Arrays must be checked for equality because default does identity
        if ((one.getClass() == ClassConstants.APBYTE) && (two.getClass() == ClassConstants.APBYTE)) {
            return Helper.compareByteArrays((byte[])one, (byte[])two);
        }
        if ((one.getClass() == ClassConstants.APCHAR) && (two.getClass() == ClassConstants.APCHAR)) {
            return Helper.compareCharArrays((char[])one, (char[])two);
        }
        if ((one.getClass().isArray()) && (two.getClass().isArray())) {
            return Helper.compareArrays((Object[])one, (Object[])two);
        }

        // BigDecimals equals does not consider the precision correctly
        if (one instanceof java.math.BigDecimal && two instanceof java.math.BigDecimal) {
            return Helper.compareBigDecimals((java.math.BigDecimal)one, (java.math.BigDecimal)two);
        }

        return one.equals(two);
    
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);
        
        if (converter != null) {
            if (converter instanceof TypeConversionConverter) {
                ((TypeConversionConverter)converter).convertClassNamesToClasses(classLoader);
            } else if (converter instanceof ObjectTypeConverter) {
                // To avoid 1.5 dependencies with the EnumTypeConverter check
                // against ObjectTypeConverter.
                ((ObjectTypeConverter) converter).convertClassNamesToClasses(classLoader);
            }
        } 
    
public java.lang.ClassgetAttributeClassification()
PUBLIC: Some databases do not properly support all of the base data types. For these databases, the base data type must be explicitly specified in the mapping to tell TopLink to force the instance variable value to that data type

        return attributeClassification;
    
public java.lang.StringgetAttributeClassificationName()

        return attributeClassificationName;
    
public java.lang.ObjectgetAttributeValue(java.lang.Object fieldValue, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Allows for subclasses to convert the attribute value.

        // PERF: Direct variable access.
        Object attributeValue = fieldValue;
        if ((fieldValue == null) && (getNullValue() != null)) {// Translate default null value
            return this.nullValue;
        }

        // Allow for user defined conversion to the object value.
        if (this.converter != null) {
            attributeValue = this.converter.convertDataValueToObjectValue(attributeValue, session);
        } else {
            // PERF: Avoid conversion check when not required.
            if ((attributeValue == null) || (attributeValue.getClass() != this.attributeObjectClassification)) {
                try {
                    attributeValue = session.getDatasourcePlatform().convertObject(attributeValue, this.attributeClassification);
                } catch (ConversionException e) {
                    throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
                }
            }
        }
        if (attributeValue == null) {// Translate default null value, conversion may have produced null.
            attributeValue = this.nullValue;
        }

        return attributeValue;
    
public ConvertergetConverter()
PUBLIC: Return the converter on the mapping. A converter can be used to convert between the object's value and database value of the attribute.

        return converter;
    
public DatabaseFieldgetField()
INTERNAL: Returns the field which this mapping represents.

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

        // PERF: This method is a major performance code point,
        // so has been micro optimized and uses direct variable access.
        if (fieldToClassify.type != null) {
            return fieldToClassify.type;
        } else {
            if (this.converter != null) {
                return null;
            } else {
                return this.attributeClassification;
            }
        }
    
public java.lang.ClassgetFieldClassification()
ADVANCED: Return the class type of the field value. This can be used if field value differs from the object value, has specific typing requirements such as usage of java.sql.Blob or NChar.

        if (getField() == null) {
            return null;
        }
        return getField().getType();
    
public java.lang.StringgetFieldName()
PUBLIC: Name of the field this mapping represents.

        return getField().getQualifiedName();
    
public java.lang.ObjectgetFieldValue(java.lang.Object attributeValue, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Convert the attribute value to a field value. Process any converter if defined, and check for null values.

        // PERF: This method is a major performance code point,
        // so has been micro optimized and uses direct variable access.
        Object fieldValue = attributeValue;
        if ((this.nullValue != null) && (this.nullValue.equals(fieldValue))) {
            return null;
        }

        // Allow for user defined conversion to the object value.		
        if (this.converter != null) {
            fieldValue = this.converter.convertObjectValueToDataValue(fieldValue, session);
        }
        Class fieldClassification = getFieldClassification(getField());
        // PERF: Avoid conversion if not required.
        if ((fieldValue == null) || (fieldClassification != fieldValue.getClass())) {
            try {
                fieldValue = session.getPlatform(getDescriptor().getJavaClass()).convertObject(fieldValue, fieldClassification);
            } catch (ConversionException exception) {
                throw ConversionException.couldNotBeConverted(this, getDescriptor(), exception);
            }
        }
        return fieldValue;
    
public java.lang.ObjectgetNullValue()
PUBLIC: Allow for the value used for null to be specified. This can be used to convert database null values to application specific values, when null values are not allowed by the application (such as in primitives). Note: the default value for NULL is used on reads, writes, and query SQL generation

        return nullValue;
    
public java.lang.IntegergetWeight()
INTERNAL: Return the weight of the mapping, used to sort mappings to ensure that DirectToField Mappings get merged first

        return this.weight;
    
public voidinitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: The mapping is initialized with the given session. This mapping is fully initialized after this.

        super.initialize(session);

        // Initialize isMutable if not specified, default is false (assumes atomic).
        if (this.isMutable == null) {
            if (getConverter() != null) {
                setIsMutable(getConverter().isMutable());
            } else {
                setIsMutable(false);
            }
        }

        if (getField() == null) {
            session.getIntegrityChecker().handleError(DescriptorException.fieldNameNotSetInMapping(this));
        }

        getDescriptor().buildField(getField());
        setFields(collectFields());

        if (getConverter() != null) {
            getConverter().initialize(this, session);
        }
    
public ChangeRecordinternalBuildChangeRecord(java.lang.Object newValue, ObjectChangeSet owner)
INTERNAL: Build a change record

        DirectToFieldChangeRecord changeRecord = new DirectToFieldChangeRecord(owner);
        changeRecord.setAttribute(getAttributeName());
        changeRecord.setMapping(this);
        changeRecord.setNewValue(newValue);
        return changeRecord;
    
public booleanisAbstractDirectMapping()
INTERNAL:

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

        return !isMutable();
    
public booleanisCloningRequired()
INTERNAL: Return if this mapping requires its attribute value to be cloned.

        return isMutable() || getDescriptor().getCopyPolicy().buildsNewInstance();
    
public booleanisDirectToFieldMapping()
INTERNAL:

        return true;
    
public booleanisMutable()
PUBLIC: Return true if the attribute for this mapping is a simple atomic value that cannot be modified, only replaced. This is false by default unless a mutable converter is used such as the SerializedObjectConverter. This can be set to false in this case, or if a Calendar or byte[] is desired to be used as a mutable value it can be set to true.

        if (isMutable == null) {
            return false;
        }
        return isMutable.booleanValue();
    
public voiditerate(DescriptorIterator iterator)
INTERNAL: Iterate on the appropriate attribute.

        // PERF: Only iterate when required.
        if (iterator.shouldIterateOnPrimitives()) {
            iterator.iteratePrimitiveForMapping(getAttributeValueFromObject(iterator.getVisitedParent()), this);
        }
    
public voidmergeChangesIntoObject(java.lang.Object target, ChangeRecord changeRecord, java.lang.Object source, MergeManager mergeManager)
INTERNAL: Merge changes from the source to the target object.

        setAttributeValueInObject(target, ((DirectToFieldChangeRecord)changeRecord).getNewValue());
    
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

        Object attributeValue = getAttributeValueFromObject(source);
        if ( (this.getDescriptor().getObjectChangePolicy().isObjectChangeTrackingPolicy()) && (!compareObjects(target, source, mergeManager.getSession())) ) {
            // if it didn't change then there will be no event
            Object targetAttribute = getAttributeValueFromObject(target);
            setAttributeValueInObject(target, attributeValue);
            //set the value first, if the owner is new ( or aggregate) the change set may be created directly
            //from the target.
            this.getDescriptor().getObjectChangePolicy().raiseInternalPropertyChangeEvent(target, getAttributeName(), targetAttribute, attributeValue);
        }else{
            //just set the value and continue
            setAttributeValueInObject(target, attributeValue);
        }
    
public voidpreInitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Initialize the attribute classification.

        super.preInitialize(session);
        this.attributeClassification = getAttributeAccessor().getAttributeClass();
        this.attributeObjectClassification = Helper.getObjectClass(this.attributeClassification);
    
public voidsetAttributeClassification(java.lang.Class attributeClassification)
PUBLIC: Some databases do not properly support all of the base data types. For these databases, the base data type must be explicitly specified in the mapping to tell TopLink to force the instance variable value to that data type

        this.attributeClassification = attributeClassification;
    
public voidsetAttributeClassificationName(java.lang.String attributeClassificationName)
INTERNAL: Set the name of the class for MW usage.

        this.attributeClassificationName = attributeClassificationName;
    
public voidsetConverter(Converter converter)
PUBLIC: Set the converter on the mapping. A converter can be used to convert between the object's value and database value of the attribute.

        this.converter = converter;
    
public voidsetField(DatabaseField theField)
ADVANCED: Set the field in the mapping. This can be used for advanced field types, such as XML nodes, or to set the field type.

        field = theField;
    
public voidsetFieldClassification(java.lang.Class fieldType)
ADVANCED: Set the class type of the field value. This can be used if field value differs from the object value, has specific typing requirements such as usage of java.sql.Blob or NChar. This must be called after the field name has been set.

        getField().setType(fieldType);
    
public voidsetFieldType(int jdbcType)
ADVANCED: Set the JDBC type of the field value. This can be used if field type does not corespond directly to a Java class type, such as MONEY. This is used for binding.

        getField().setSqlType(jdbcType);
    
public voidsetIsMutable(boolean isMutable)
PUBLIC: Return true if the attribute for this mapping is a simple atomic value that cannot be modified, only replaced. This is false by default unless a mutable converter is used such as the SerializedObjectConverter. This can be set to false in this case, or if a Calendar or byte[] is desired to be used as a mutable value it can be set to true.

        if (isMutable == true) {
            this.isMutable = Boolean.TRUE;
        } else {
            this.isMutable = Boolean.FALSE;
        }
    
public voidsetNullValue(java.lang.Object nullValue)
PUBLIC: Allow for the value used for null to be specified. This can be used to convert database null values to application specific values, when null values are not allowed by the application (such as in primitives). Note: the default value for NULL is used on reads, writes, and query SQL generation

        this.nullValue = nullValue;
    
public java.lang.StringtoString()
INTERNAL:

        return getClass().getName() + "[" + getAttributeName() + "-->" + getField() + "]";
    
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.

        DirectToFieldChangeRecord changeRecord = (DirectToFieldChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
        if (changeRecord == null) {
            objectChangeSet.addChange(internalBuildChangeRecord(newValue, objectChangeSet));
        } else {
            changeRecord.setNewValue(newValue);
        }
    
public voidvalidateBeforeInitialization(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Allow for subclasses to perform validation.

        if ((getFieldName() == null) || (getFieldName().length() == 0)) {
            session.getIntegrityChecker().handleError(DescriptorException.noFieldNameForMapping(this));
        }
    
public java.lang.ObjectvalueFromObject(java.lang.Object object, DatabaseField field, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Get the value from the object for this mapping.

        return getFieldValue(getAttributeValueFromObject(object), session);
    
public java.lang.ObjectvalueFromRow(oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager joinManager, ObjectBuildingQuery query, oracle.toplink.essentials.internal.sessions.AbstractSession executionSession)
INTERNAL: In the case of building a UnitOfWork clone directly from a row, the session set in the query will not know which database platform to use for converting the value. Allows the correct session to be passed in.

param
row
param
query
param
executionSession
return

        // PERF: Direct variable access.
        Object fieldValue = row.get(this.field);
        Object attributeValue = getAttributeValue(fieldValue, executionSession);

        return attributeValue;
    
public voidwriteFromObjectIntoRow(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Get a value from the object and set that in the respective field of the row.

        if (isReadOnly()) {
            return;
        }

        Object attributeValue = getAttributeValueFromObject(object);
        Object fieldValue = getFieldValue(attributeValue, session);

        writeValueIntoRow(row, getField(), fieldValue);

    
public voidwriteFromObjectIntoRowForUpdate(WriteObjectQuery query, oracle.toplink.essentials.internal.sessions.AbstractRecord aDatabaseRow)
INTERNAL: Write the attribute value from the object to the row for update.

        if (query.getSession().isUnitOfWork()) {
            if (compareObjects(query.getBackupClone(), query.getObject(), query.getSession())) {
                return;
            }
        }

        super.writeFromObjectIntoRowForUpdate(query, aDatabaseRow);
    
public voidwriteFromObjectIntoRowWithChangeRecord(ChangeRecord changeRecord, oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Get a value from the object and set that in the respective field of the row. Validation preventing primary key updates is implemented here.

        if (isReadOnly()) {
            return;
        }

        if (isPrimaryKeyMapping() && !changeRecord.getOwner().isNew()) {
           throw ValidationException.primaryKeyUpdateDisallowed(changeRecord.getOwner().getClassName(), changeRecord.getAttribute());
        }
        
        Object attributeValue = ((DirectToFieldChangeRecord)changeRecord).getNewValue();
        Object fieldValue = getFieldValue(attributeValue, session);

        row.add(getField(), fieldValue);
    
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;
        }

        databaseRow.add(getField(), null);
    
protected abstract voidwriteValueIntoRow(oracle.toplink.essentials.internal.sessions.AbstractRecord row, DatabaseField field, java.lang.Object value)