All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.toplink.essentials.mappings.ForeignReferenceMapping Maven / Gradle / Ivy

There is a newer version: 2.1-60f
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.mappings;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.*;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.expressions.*;
import oracle.toplink.essentials.indirection.*;
import oracle.toplink.essentials.internal.descriptors.*;
import oracle.toplink.essentials.internal.expressions.*;
import oracle.toplink.essentials.internal.indirection.*;
import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
import oracle.toplink.essentials.internal.sessions.*;
import oracle.toplink.essentials.queryframework.*;
import oracle.toplink.essentials.internal.sessions.AbstractRecord;
import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
import oracle.toplink.essentials.internal.sessions.AbstractSession;
import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.internal.helper.Helper;
import oracle.toplink.essentials.sessions.DatabaseRecord;
import oracle.toplink.essentials.sessions.Session;

/**
 * Purpose: Abstract class for relationship mappings
 */
public abstract class ForeignReferenceMapping extends DatabaseMapping {

    /** This is used only in descriptor proxy in remote session */
    protected Class referenceClass;
    protected String referenceClassName;

    /** The session is temporarily used for initialization. Once used, it is set to null */
    protected transient AbstractSession tempInitSession;

    /** The descriptor of the reference class. */
    protected transient ClassDescriptor referenceDescriptor;

    /** This query is used to read referenced objects for this mapping. */
    protected transient ReadQuery selectionQuery;

    /** Indicates whether the referenced object is privately owned or not. */
    protected boolean isPrivateOwned;

    /** Implements indirection behavior */
    protected IndirectionPolicy indirectionPolicy;

    /** Indicates whether the selection query is TopLink generated or defined by the user. */
    protected transient boolean hasCustomSelectionQuery;

    /** Used to reference the other half of a bi-directional relationship. */
    protected DatabaseMapping relationshipPartner;

    /** Set by users, used to retreive the backpointer for this mapping */
    protected String relationshipPartnerAttributeName;

    /** Cascading flags used by the EntityManager */
    protected boolean cascadePersist;
    protected boolean cascadeMerge;
    protected boolean cascadeRefresh;
    protected boolean cascadeRemove;

    protected ForeignReferenceMapping() {
        this.isPrivateOwned = false;
        this.hasCustomSelectionQuery = false;
        this.useBasicIndirection();
        this.cascadePersist = false;
        this.cascadeMerge = false;
        this.cascadeRefresh = false;
        this.cascadeRemove = false;
    }

    /**
     * INTERNAL:
     * Clone the attribute from the clone and assign it to the backup.
     */
    public void buildBackupClone(Object clone, Object backup, UnitOfWorkImpl unitOfWork) {
        Object attributeValue = getAttributeValueFromObject(clone);
        Object clonedAttributeValue = getIndirectionPolicy().backupCloneAttribute(attributeValue, clone, backup, unitOfWork);
        setAttributeValueInObject(backup, clonedAttributeValue);
    }

    /**
     * INTERNAL:
     * Used during building the backup shallow copy to copy the
     * target object without re-registering it.
     */
    public abstract Object buildBackupCloneForPartObject(Object attributeValue, Object clone, Object backup, UnitOfWorkImpl unitOfWork);

    /**
     * INTERNAL:
     * Clone the attribute from the original and assign it to the clone.
     */
    public void buildClone(Object original, Object clone, UnitOfWorkImpl unitOfWork, JoinedAttributeManager joinedAttributeManager) {
        Object attributeValue = getAttributeValueFromObject(original);
        Object clonedAttributeValue = getIndirectionPolicy().cloneAttribute(attributeValue, original, clone, unitOfWork, false);// building clone from an original not a row.
        //GFBug#404  Trigger the value holder of the joined attribute if shouldCascadeCloneToJoinedRelationship (joinedAttributeManager != null)
        if ((clonedAttributeValue != null) && (joinedAttributeManager != null)) {
            if (joinedAttributeManager.hasJoinedAttributes()) {
                if (joinedAttributeManager.getJoinedAttributes().contains(getAttributeName())) {
                     if (IndirectContainer.class.isAssignableFrom(clonedAttributeValue.getClass())) {
                         ((IndirectContainer)clonedAttributeValue).getValueHolder().getValue();
                     } else if (ValueHolderInterface.class.isAssignableFrom(clonedAttributeValue.getClass())) {
                         ((ValueHolderInterface)clonedAttributeValue).getValue();
                     }
                }
            }
        }
        setAttributeValueInObject(clone, clonedAttributeValue);
    }

    /**
     * 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. */ public void buildCloneFromRow(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object clone, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) { Object attributeValue = valueFromRow(databaseRow, joinManager, sourceQuery, executionSession); Object clonedAttributeValue = getIndirectionPolicy().cloneAttribute(attributeValue, null,// no original clone, unitOfWork, true);// building clone directly from row. setAttributeValueInObject(clone, clonedAttributeValue); } /** * INTERNAL: * Require for cloning, the part must be cloned. */ public abstract Object buildCloneForPartObject(Object attributeValue, Object original, Object clone, UnitOfWorkImpl unitOfWork, boolean isExisting); /** * INTERNAL: * The mapping clones itself to create deep copy. */ public Object clone() { ForeignReferenceMapping clone = (ForeignReferenceMapping)super.clone(); clone.setIndirectionPolicy((IndirectionPolicy)indirectionPolicy.clone()); clone.setSelectionQuery((ReadQuery)getSelectionQuery().clone()); return clone; } /** * INTERNAL: * Compare the attributes belonging to this mapping for the objects. */ public boolean compareObjects(Object firstObject, Object secondObject, AbstractSession session) { if (isPrivateOwned()) { return compareObjectsWithPrivateOwned(firstObject, secondObject, session); } else { return compareObjectsWithoutPrivateOwned(firstObject, secondObject, session); } } /** * Compare two objects if their parts are not private owned */ protected abstract boolean compareObjectsWithoutPrivateOwned(Object first, Object second, AbstractSession session); /** * Compare two objects if their parts are private owned */ protected abstract boolean compareObjectsWithPrivateOwned(Object first, Object second, AbstractSession session); /** * INTERNAL: * Convert all the class-name-based settings in this mapping to actual class-based * settings. This method is used when converting a project that has been built * with class names to a project with classes. * @param classLoader */ public void convertClassNamesToClasses(ClassLoader classLoader){ super.convertClassNamesToClasses(classLoader); // DirectCollection mappings don't require a reference class. if (getReferenceClassName() != null) { Class referenceClass = null; try{ if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { referenceClass = (Class)AccessController.doPrivileged(new PrivilegedClassForName(getReferenceClassName(), true, classLoader)); } catch (PrivilegedActionException exception) { throw ValidationException.classNotFoundWhileConvertingClassNames(getReferenceClassName(), exception.getException()); } } else { referenceClass = oracle.toplink.essentials.internal.security.PrivilegedAccessHelper.getClassForName(getReferenceClassName(), true, classLoader); } } catch (ClassNotFoundException exc){ throw ValidationException.classNotFoundWhileConvertingClassNames(getReferenceClassName(), exc); } setReferenceClass(referenceClass); } }; /** * INTERNAL: * Builder the unit of work value holder. * Ignore the original object. * @param buildDirectlyFromRow indicates that we are building the clone directly * from a row as opposed to building the original from the row, putting it in * the shared cache, and then cloning the original. */ public UnitOfWorkValueHolder createUnitOfWorkValueHolder(ValueHolderInterface attributeValue, Object original, Object clone, AbstractRecord row, UnitOfWorkImpl unitOfWork, boolean buildDirectlyFromRow) { return new UnitOfWorkQueryValueHolder(attributeValue, clone, this, row, unitOfWork); } /** * INTERNAL: * Return true if the merge should be bypassed. This would be the case for several reasons, depending on * the kind of merge taking place. */ protected boolean dontDoMerge(Object target, Object source, MergeManager mergeManager) { if (!shouldMergeCascadeReference(mergeManager)) { return true; } if (mergeManager.shouldMergeOriginalIntoWorkingCopy()) { // For reverts we are more concerned about the target than the source. if (!isAttributeValueInstantiated(target)) { return true; } } else { if (mergeManager.shouldRefreshRemoteObject() && shouldMergeCascadeParts(mergeManager) && usesIndirection()) { return true; } else { if (!isAttributeValueInstantiated(source)) { return true; } } } return false; } /** * PUBLIC: * Indirection means that a ValueHolder will be put in-between the attribute and the real object. * This allows for the reading of the target from the database to be delayed until accessed. * This defaults to true and is strongly suggested as it give a huge performance gain. */ public void dontUseIndirection() { setIndirectionPolicy(new NoIndirectionPolicy()); } /** * INTERNAL: * Clone and prepare the JoinedAttributeManager nested JoinedAttributeManager. * This is used for nested joining as the JoinedAttributeManager passed to the joined build object. */ public ObjectLevelReadQuery prepareNestedJoins(JoinedAttributeManager joinManager, AbstractSession session) { // A nested query must be built to pass to the descriptor that looks like the real query execution would. ObjectLevelReadQuery nestedQuery = (ObjectLevelReadQuery)((ObjectLevelReadQuery)getSelectionQuery()).deepClone(); //set session to be the session on which the query is being executed nestedQuery.setSession(session); // Recompute the joined indexes based on the nested join expressions. nestedQuery.getJoinedAttributeManager().setJoinedMappingIndexes_(null); nestedQuery.getJoinedAttributeManager().setJoinedMappingExpressions_(new ArrayList(1)); nestedQuery.getJoinedAttributeManager().setJoinedAttributeExpressions_(extractNestedExpressions(joinManager.getJoinedAttributeExpressions(), nestedQuery.getExpressionBuilder(), false)); // the next line sets isToManyJoinQuery flag nestedQuery.getJoinedAttributeManager().prepareJoinExpressions(session); nestedQuery.getJoinedAttributeManager().computeJoiningMappingIndexes(true, session, 0); if (joinManager.getBaseQuery().isLockQuery()) { ObjectLevelReadQuery baseQuery = ((ObjectLevelReadQuery)joinManager.getBaseQuery()); if (baseQuery.getLockingClause().isForUpdateOfClause()) { ForUpdateOfClause clause = (ForUpdateOfClause)baseQuery.getLockingClause().clone(); clause.setLockedExpressions(extractNestedExpressions(clause.getLockedExpressions(), nestedQuery.getExpressionBuilder(), true)); nestedQuery.setLockingClause(clause); } else { nestedQuery.setLockingClause(baseQuery.getLockingClause()); } } nestedQuery.setShouldMaintainCache(joinManager.getBaseQuery().shouldMaintainCache()); nestedQuery.setShouldRefreshIdentityMapResult(joinManager.getBaseQuery().shouldRefreshIdentityMapResult()); nestedQuery.setCascadePolicy(joinManager.getBaseQuery().getCascadePolicy()); nestedQuery.setSession(null); return nestedQuery; } /** * INTERNAL: * Return the value of an attribute which this mapping represents for an object. */ public Object getAttributeValueFromObject(Object object) throws DescriptorException { Object attributeValue = super.getAttributeValueFromObject(object); Object indirectionValue = getIndirectionPolicy().validateAttributeOfInstantiatedObject(attributeValue); // PERF: Allow the indirection policy to initialize null attribute values, // this allows the indirection objects to not be initialized in the constructor. if (indirectionValue != attributeValue) { setAttributeValueInObject(object, indirectionValue); attributeValue = indirectionValue; } return attributeValue; } /** * INTERNAL: * Returns the attribute value from the reference object. * If the attribute is using indirection the value of the value-holder is returned. * If the value holder is not instantiated then it is instantiated. */ public Object getAttributeValueWithClonedValueHolders(Object object) { Object attributeValue = getAttributeValueFromObject(object); if (attributeValue instanceof DatabaseValueHolder){ return ((DatabaseValueHolder)attributeValue).clone(); } else if (attributeValue instanceof ValueHolder){ return ((ValueHolder)attributeValue).clone(); } return attributeValue; } /** * INTERNAL: * Return the mapping's indirection policy. */ public IndirectionPolicy getIndirectionPolicy() { return indirectionPolicy; } /** * INTERNAL: * Returns the join criteria stored in the mapping selection query. This criteria * is used to read reference objects across the tables from the database. */ public Expression getJoinCriteria(QueryKeyExpression exp) { Expression selectionCriteria = getSelectionCriteria(); return exp.getBaseExpression().twist(selectionCriteria, exp); } /** * INTERNAL: * Returns the attribute value from the reference object. * If the attribute is using indirection the value of the value-holder is returned. * If the value holder is not instantiated then it is instantiated. */ public Object getRealAttributeValueFromObject(Object object, AbstractSession session) { return getIndirectionPolicy().getRealAttributeValueFromObject(object, getAttributeValueFromObject(object)); } /** * PUBLIC: * Returns the reference class. */ public Class getReferenceClass() { return referenceClass; } /** * INTERNAL: * Returns the reference class name. */ public String getReferenceClassName() { if ((referenceClassName == null) && (referenceClass != null)) { referenceClassName = referenceClass.getName(); } return referenceClassName; } /** * INTERNAL: * Return the referenceDescriptor. This is a descriptor which is associated with * the reference class. */ public ClassDescriptor getReferenceDescriptor() { if (referenceDescriptor == null) { if (getTempSession() == null) { return null; } else { referenceDescriptor = getTempSession().getDescriptor(getReferenceClass()); } } return referenceDescriptor; } /** * INTERNAL: * Return the relationshipPartner mapping for this bi-directional mapping. If the relationshipPartner is null then * this is a uni-directional mapping. */ public DatabaseMapping getRelationshipPartner() { if ((this.relationshipPartner == null) && (this.relationshipPartnerAttributeName != null)) { setRelationshipPartner(getReferenceDescriptor().getMappingForAttributeName(getRelationshipPartnerAttributeName())); } return this.relationshipPartner; } /** * PUBLIC: * Use this method retreive the relationship partner attribute name of this bidirectional Mapping. */ public String getRelationshipPartnerAttributeName() { return this.relationshipPartnerAttributeName; } /** * INTERNAL: * Returns the selection criteria stored in the mapping selection query. This criteria * is used to read reference objects from the database. */ public Expression getSelectionCriteria() { return getSelectionQuery().getSelectionCriteria(); } /** * INTERNAL: * Returns the read query assoicated with the mapping. */ public ReadQuery getSelectionQuery() { return selectionQuery; } protected AbstractSession getTempSession() { return tempInitSession; } /** * INTERNAL: * Indicates whether the selection query is TopLink generated or defined by * the user. */ public boolean hasCustomSelectionQuery() { return hasCustomSelectionQuery; } /** * INTERNAL: * Initialize the state of mapping. */ public void initialize(AbstractSession session) throws DescriptorException { super.initialize(session); initializeReferenceDescriptor(session); initializeSelectionQuery(session); getIndirectionPolicy().initialize(); } /** * Initialize and set the descriptor for the referenced class in this mapping. */ protected void initializeReferenceDescriptor(AbstractSession session) throws DescriptorException { if (getReferenceClass() == null) { throw DescriptorException.referenceClassNotSpecified(this); } ClassDescriptor refDescriptor = session.getDescriptor(getReferenceClass()); if (refDescriptor == null) { throw DescriptorException.descriptorIsMissing(getReferenceClass().getName(), this); } if (refDescriptor.isAggregateDescriptor() && (!isAggregateCollectionMapping())) { throw DescriptorException.referenceDescriptorCannotBeAggregate(this); } // can not be isolated if it is null. Seems that only aggregates do not set // the owning descriptor on the mapping. if ((!((this.getDescriptor() != null) && this.getDescriptor().isIsolated())) && refDescriptor.isIsolated()) { throw DescriptorException.isolateDescriptorReferencedBySharedDescriptor(refDescriptor.getJavaClassName(), this.getDescriptor().getJavaClassName(), this); } setReferenceDescriptor(refDescriptor); } /** * A subclass should implement this method if it wants non default behaviour. */ protected void initializeSelectionQuery(AbstractSession session) throws DescriptorException { if (((ObjectLevelReadQuery)getSelectionQuery()).getReferenceClass() == null) { throw DescriptorException.referenceClassNotSpecified(this); } getSelectionQuery().setDescriptor(getReferenceDescriptor()); } /** * INTERNAL: * The referenced object is checked if it is instantiated or not */ public boolean isAttributeValueInstantiated(Object object) { return getIndirectionPolicy().objectIsInstantiated(getAttributeValueFromObject(object)); } /** * PUBLIC: * Check cascading value for the CREATE operation. */ public boolean isCascadePersist() { return this.cascadePersist; } /** * PUBLIC: * Check cascading value for the MERGE operation. */ public boolean isCascadeMerge() { return this.cascadeMerge; } /** * PUBLIC: * Check cascading value for the REFRESH operation. */ public boolean isCascadeRefresh() { return this.cascadeRefresh; } /** * PUBLIC: * Check cascading value for the REMOVE operation. */ public boolean isCascadeRemove() { return this.cascadeRemove; } /** * INTERNAL: */ public boolean isForeignReferenceMapping() { return true; } /** * INTERNAL: * Return true if referenced objects are provately owned else false. */ public boolean isPrivateOwned() { return isPrivateOwned; } /** * INTERNAL: * Iterate on the iterator's current object's attribute defined by this mapping. * The iterator's settings for cascading and value holders determine how the * iteration continues from here. */ public void iterate(DescriptorIterator iterator) { Object attributeValue = this.getAttributeValueFromObject(iterator.getVisitedParent()); this.getIndirectionPolicy().iterateOnAttributeValue(iterator, attributeValue); } /** * INTERNAL: * Iterate on the attribute value. * The value holder has already been processed. */ public abstract void iterateOnRealAttributeValue(DescriptorIterator iterator, Object realAttributeValue); /** * PUBLIC: * Sets the reference object to be a private owned. The default behaviour is non * private owned. */ public void privateOwnedRelationship() { setIsPrivateOwned(true); } /** * PUBLIC: * Sets the cascading for all operations. */ public void setCascadeAll(boolean value) { setCascadePersist(value); setCascadeMerge(value); setCascadeRefresh(value); setCascadeRemove(value); } /** * PUBLIC: * Sets the cascading for the CREATE operation. */ public void setCascadePersist(boolean value) { this.cascadePersist = value; } /** * PUBLIC: * Sets the cascading for the MERGE operation. */ public void setCascadeMerge(boolean value) { this.cascadeMerge = value; } /** * PUBLIC: * Sets the cascading for the REFRESH operation. */ public void setCascadeRefresh(boolean value) { this.cascadeRefresh = value; } /** * PUBLIC: * Sets the cascading for the REMOVE operation. */ public void setCascadeRemove(boolean value) { this.cascadeRemove = value; } /** * PUBLIC: * Relationship mappings creates a read query to read reference objects. If this default * query needs to be customize then user can specify its own read query to do the reading * of reference objects. One must instance of ReadQuery or subclasses of the ReadQuery. */ public void setCustomSelectionQuery(ReadQuery query) { setSelectionQuery(query); setHasCustomSelectionQuery(true); } protected void setHasCustomSelectionQuery(boolean bool) { hasCustomSelectionQuery = bool; } /** * ADVANCED: * Set the indirection policy. */ public void setIndirectionPolicy(IndirectionPolicy indirectionPolicy) { this.indirectionPolicy = indirectionPolicy; indirectionPolicy.setMapping(this); } /** * INTERNAL: * Used by Gromit */ public void setIsPrivateOwned(boolean isPrivateOwned) { this.isPrivateOwned = isPrivateOwned; } /** * INTERNAL: * Set the value of the attribute mapped by this mapping, * placing it inside a value holder if necessary. * If the value holder is not instantiated then it is instantiated. */ public void setRealAttributeValueInObject(Object object, Object value) throws DescriptorException { this.getIndirectionPolicy().setRealAttributeValueInObject(object, value); } /** * PUBLIC: * Set the referenced class. */ public void setReferenceClass(Class referenceClass) { this.referenceClass = referenceClass; if (referenceClass != null) { setReferenceClassName(referenceClass.getName()); // Make sure the reference class of the selectionQuery is set. setSelectionQuery(getSelectionQuery()); } } /** * INTERNAL: * Used by MW. */ public void setReferenceClassName(String referenceClassName) { this.referenceClassName = referenceClassName; } /** * Set the referenceDescriptor. This is a descriptor which is associated with * the reference class. */ protected void setReferenceDescriptor(ClassDescriptor aDescriptor) { referenceDescriptor = aDescriptor; } /** * INTERNAL: * Sets the relationshipPartner mapping for this bi-directional mapping. If the relationshipPartner is null then * this is a uni-directional mapping. */ public void setRelationshipPartner(DatabaseMapping mapping) { this.relationshipPartner = mapping; } /** * PUBLIC: * Use this method to specify the relationship partner attribute name of a bidirectional Mapping. * TopLink will use the attribute name to find the back pointer mapping to maintain referential integrity of * the bi-directional mappings. */ public void setRelationshipPartnerAttributeName(String attributeName) { this.relationshipPartnerAttributeName = attributeName; } /** * PUBLIC: * Sets the selection criteria to be used as a where clause to read * reference objects. This criteria is automatically generated by the * TopLink if not explicitly specified by the user. */ public void setSelectionCriteria(Expression anExpression) { getSelectionQuery().setSelectionCriteria(anExpression); } /** * Sets the query */ protected void setSelectionQuery(ReadQuery aQuery) { selectionQuery = aQuery; // Make sure the reference class of the selectionQuery is set. if ((selectionQuery != null) && selectionQuery.isObjectLevelReadQuery() && (selectionQuery.getReferenceClassName() == null)) { ((ObjectLevelReadQuery)selectionQuery).setReferenceClass(getReferenceClass()); } } /** * PUBLIC: * This is a property on the mapping which will allow custom SQL to be * substituted for reading a reference object. */ public void setSelectionSQLString(String sqlString) { getSelectionQuery().setSQLString(sqlString); setCustomSelectionQuery(getSelectionQuery()); } /** * PUBLIC: * This is a property on the mapping which will allow custom call to be * substituted for reading a reference object. */ public void setSelectionCall(Call call) { getSelectionQuery().setCall(call); setCustomSelectionQuery(getSelectionQuery()); } protected void setTempSession(AbstractSession session) { this.tempInitSession = session; } /** * PUBLIC: * Indirection means that a ValueHolder will be put in-between the attribute and the real object. * This allows for the reading of the target from the database to be delayed until accessed. * This defaults to true and is strongly suggested as it give a huge performance gain. * @see #useBasicIndirection() * @see #dontUseIndirection() */ public void setUsesIndirection(boolean usesIndirection) { if (usesIndirection) { useBasicIndirection(); } else { dontUseIndirection(); } } protected boolean shouldInitializeSelectionCriteria() { if (hasCustomSelectionQuery()) { return false; } if (getSelectionCriteria() == null) { return true; } return false; } /** * INTERNAL: * Returns true if the merge should cascade to the mappings reference's parts. */ public boolean shouldMergeCascadeParts(MergeManager mergeManager) { return ((mergeManager.shouldCascadeByMapping() && this.isCascadeMerge()) || (mergeManager.shouldCascadeAllParts()) || (mergeManager.shouldCascadePrivateParts() && isPrivateOwned())); } /** * Returns true if the merge should cascade to the mappings reference. */ protected boolean shouldMergeCascadeReference(MergeManager mergeManager) { if (mergeManager.shouldCascadeReferences()) { return true; } // P2.0.1.3: Was merging references on non-privately owned parts // Same logic in: return shouldMergeCascadeParts(mergeManager); } /** * INTERNAL: * Returns true only if the object is scheduled for deletion. */ protected boolean shouldObjectDeleteCascadeToPart(WriteObjectQuery query, Object object) { return (isPrivateOwned() || query.shouldCascadeAllParts() || query.shouldDependentObjectBeDeleted(object)); } /** * Returns true if any process leading to object modification should also affect its parts * Usually used by write, insert and update. */ protected boolean shouldObjectModifyCascadeToParts(ObjectLevelModifyQuery query) { if (isReadOnly()) { return false; } if (isPrivateOwned()) { return true; } // Only cascade dependents writes in uow. if (query.shouldCascadeOnlyDependentParts()) { return hasConstraintDependency(); } return query.shouldCascadeAllParts(); } /** * INTERNAL: * Return whether any process leading to object deletion should also affect its parts. * Used in preDelete. Note that foreign key dependencies are reversed for deletes: * The relationship side having the foreign key must be deleted before the other side is removed. */ protected boolean shouldObjectModifyCascadeToPartsForPreDelete(ObjectLevelModifyQuery query) { if (isReadOnly()) { return false; } // Always cascade for privately-owned parts if (isPrivateOwned()) { return true; } // Foreign key dependencies are reversed for deletes if (query.shouldCascadeOnlyDependentParts()) { return !hasConstraintDependency(); } return query.shouldCascadeAllParts(); } /** * PUBLIC: * Indirection means that a ValueHolder will be put in-between the attribute and the real object. * This allows for the reading of the target from the database to be delayed until accessed. * This defaults to true and is strongly suggested as it give a huge performance gain. */ public void useBasicIndirection() { setIndirectionPolicy(new BasicIndirectionPolicy()); } public void useWeavedIndirection(String setMethodName){ setIndirectionPolicy(new WeavedObjectBasicIndirectionPolicy(setMethodName)); } /** * PUBLIC: * Indirection means that some sort of indirection object will be put in-between the attribute and the real object. * This allows for the reading of the target from the database to be delayed until accessed. * This defaults to true and is strongly suggested as it give a huge performance gain. */ public boolean usesIndirection() { return getIndirectionPolicy().usesIndirection(); } /** * INTERNAL: * To validate mappings decleration */ public void validateBeforeInitialization(AbstractSession session) throws DescriptorException { super.validateBeforeInitialization(session); if (getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) { Class attributeType = ((InstanceVariableAttributeAccessor)getAttributeAccessor()).getAttributeType(); getIndirectionPolicy().validateDeclaredAttributeType(attributeType, session.getIntegrityChecker()); } else if (getAttributeAccessor() instanceof MethodAttributeAccessor) { Class returnType = ((MethodAttributeAccessor)getAttributeAccessor()).getGetMethodReturnType(); getIndirectionPolicy().validateGetMethodReturnType(returnType, session.getIntegrityChecker()); Class parameterType = ((MethodAttributeAccessor)getAttributeAccessor()).getSetMethodParameterType(); getIndirectionPolicy().validateSetMethodParameterType(parameterType, session.getIntegrityChecker()); } } /** * INTERNAL: * Return the value of the reference attribute or a value holder. * Check whether the mapping's attribute should be optimized through batch and joining. */ public Object valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery query, AbstractSession executionSession) throws DatabaseException { if(shouldUseValueFromRowWithJoin(joinManager)) { return valueFromRowInternalWithJoin(row, joinManager, executionSession); } else { return valueFromRowInternal(row, joinManager, executionSession); } } /** * INTERNAL: * Indicates whether valueFromRow should call valueFromRowInternalWithJoin (true) * or valueFromRowInternal (false) */ protected boolean shouldUseValueFromRowWithJoin(JoinedAttributeManager joinManager) { return isJoiningSupported() && ( joinManager.isAttributeJoined(getDescriptor(), getAttributeName()) || joinManager.getBaseQuery().hasPartialAttributeExpressions()); // only true on OLRQ and above } /** * INTERNAL: * If the query used joining or partial attributes, build the target object directly. * If isJoiningSupported()==true then this method must be overridden. * Currently only 1-1, 1-m and m-m support joining. * Potentially agg-col, dc, dm may support it too (agg-col and dc by just implementing * isJoiningSupported(){return true;} - but that should be tested). */ protected Object valueFromRowInternalWithJoin(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) throws DatabaseException { throw ValidationException.mappingDoesNotOverrideValueFromRowInternalWithJoin(Helper.getShortClassName(this.getClass())); } /** * INTERNAL: * Return the value of the reference attribute or a value holder. * Check whether the mapping's attribute should be optimized through batch and joining. */ protected Object valueFromRowInternal(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) throws DatabaseException { // PERF: Direct variable access. ReadQuery targetQuery = this.selectionQuery; //CR #4365, 3610825 - moved up from the block below, needs to be set with // indirection off. Clone the query and set its id. if (!this.indirectionPolicy.usesIndirection()) { targetQuery = (ReadQuery)targetQuery.clone(); targetQuery.setQueryId(joinManager.getBaseQuery().getQueryId()); } // if the source query is cascading then the target query must use the same settings if (targetQuery.isObjectLevelReadQuery() && (joinManager.getBaseQuery().shouldCascadeAllParts() || (this.isPrivateOwned && joinManager.getBaseQuery().shouldCascadePrivateParts()) || (this.cascadeRefresh && joinManager.getBaseQuery().shouldCascadeByMapping()))) { // If the target query has already been cloned (we're refreshing) avoid // re-cloning the query again. if (targetQuery == this.selectionQuery) { targetQuery = (ObjectLevelReadQuery)targetQuery.clone(); } ((ObjectLevelReadQuery)targetQuery).setShouldRefreshIdentityMapResult(joinManager.getBaseQuery().shouldRefreshIdentityMapResult()); targetQuery.setCascadePolicy(joinManager.getBaseQuery().getCascadePolicy()); // For queries that have turned caching off, such as aggregate collection, leave it off. if (targetQuery.shouldMaintainCache()) { targetQuery.setShouldMaintainCache(joinManager.getBaseQuery().shouldMaintainCache()); } } if (joinManager.getBaseQuery().isObjectLevelReadQuery()){ targetQuery = prepareHistoricalQuery(targetQuery, (ObjectLevelReadQuery)joinManager.getBaseQuery(), executionSession); } return this.indirectionPolicy.valueFromQuery(targetQuery, row, executionSession); } /** * INTERNAL: * Allow for the mapping to perform any historical query additions. * Return the new target query. */ protected ReadQuery prepareHistoricalQuery(ReadQuery targetQuery, ObjectLevelReadQuery sourceQuery, AbstractSession executionSession) { return targetQuery; } /** * INTERNAL: */ public AbstractRecord trimRowForJoin(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) { // 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. if (joinManager.getJoinedMappingIndexes_() != null) { Object value = joinManager.getJoinedMappingIndexes_().get(this); if(value != null) { return trimRowForJoin(row, value, executionSession); } } return row; } /** * INTERNAL: */ public AbstractRecord trimRowForJoin(AbstractRecord row, Object value, AbstractSession executionSession) { // 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. int fieldStartIndex; if(value instanceof Integer) { fieldStartIndex = ((Integer)value).intValue(); } else { // must be Map of classes to Integers Map map = (Map)value; Class cls; if (getDescriptor().hasInheritance() && getDescriptor().getInheritancePolicy().shouldReadSubclasses()) { cls = getDescriptor().getInheritancePolicy().classFromRow(row, executionSession); } else { cls = getDescriptor().getJavaClass(); } fieldStartIndex = ((Integer)map.get(cls)).intValue(); } Vector trimedFields = Helper.copyVector(row.getFields(), fieldStartIndex, row.size()); Vector trimedValues = Helper.copyVector(row.getValues(), fieldStartIndex, row.size()); return new DatabaseRecord(trimedFields, trimedValues); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy