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

oracle.toplink.essentials.queryframework.ObjectBuildingQuery Maven / Gradle / Ivy

The 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.queryframework;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.*;
import oracle.toplink.essentials.internal.expressions.*;
import oracle.toplink.essentials.internal.helper.Helper;
import oracle.toplink.essentials.mappings.DatabaseMapping;
import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
import oracle.toplink.essentials.internal.sessions.MergeManager;
import oracle.toplink.essentials.exceptions.ValidationException;
import oracle.toplink.essentials.internal.sessions.AbstractRecord;
import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.indirection.IndirectContainer;
import oracle.toplink.essentials.indirection.ValueHolderInterface;
import oracle.toplink.essentials.internal.queryframework.ContainerPolicy;
import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
import oracle.toplink.essentials.mappings.CollectionMapping;
import oracle.toplink.essentials.mappings.ForeignReferenceMapping;

/**
 * 

Purpose: * Abstract class for all read queries that build objects and potentially manipulate * the TopLink cache. * *

Description: * Contains common behavior for all read queries building objects. * * @author Gordon Yorke * @since TopLink Essentials */ public abstract class ObjectBuildingQuery extends ReadQuery { /** The class of the target objects to be read from the database. */ protected Class referenceClass; protected String referenceClassName; /** Allows for the resulting objects to be refresh with the data from the database. */ protected boolean shouldRefreshIdentityMapResult; protected boolean shouldRefreshRemoteIdentityMapResult; /** INTERNAL: for bug 2612601 allow ability not to register results in UOW. */ protected boolean shouldRegisterResultsInUnitOfWork = true; /** CMP only. Allow users to configure whether finder should be executed in a uow or not. */ protected boolean shouldProcessResultsInUnitOfWork = true; /** Used for pessimistic locking. */ protected ForUpdateClause lockingClause; public static final short NO_LOCK = 0; public static final short LOCK = 1; public static final short LOCK_NOWAIT = 2; // allow pessimistic locking policy to be used public static final short DEFAULT_LOCK_MODE = -1; protected boolean isPrePrepared; /** * Used to set the read time on objects that use this query. * Should be set to the time the query returned from the database. */ protected long executionTime = 0; /** * Added for Exclusive Connection (VPD) support see accessor for information */ protected boolean shouldUseExclusiveConnection = false; /** * INTERNAL: This is the key for accessing unregistered and locked result in the query's properties. * The uow and QueryBaseValueHolder use this property to record amd to retreive the result respectively. */ public static final String LOCK_RESULT_PROPERTY = "LOCK_RESULT"; /** PERF: Store if the query originally used the default lock mode. */ protected boolean wasDefaultLockMode = false; /** * INTERNAL: * Initialize the state of the query */ public ObjectBuildingQuery() { this.shouldRefreshIdentityMapResult = false; } /** * INTERNAL: * Convert all the class-name-based settings in this query 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); 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: * Return if this query originally used the default lock mode. */ protected boolean wasDefaultLockMode() { return wasDefaultLockMode; } /** * INTERNAL: * Set if this query originally used the default lock mode. */ protected void setWasDefaultLockMode(boolean wasDefaultLockMode) { this.wasDefaultLockMode = wasDefaultLockMode; } /** * PUBLIC: * When unset means perform read normally and dont do refresh. */ public void dontRefreshIdentityMapResult() { setShouldRefreshIdentityMapResult(false); } /** * PUBLIC: * When unset means perform read normally and dont do refresh. */ public void dontRefreshRemoteIdentityMapResult() { setShouldRefreshRemoteIdentityMapResult(false); } /** * Return the fetch group set in the query. * If a fetch group is not explicitly set in the query, default fetch group optionally defined in the decsiptor * would be used, unless the user explicitly calls query.setShouldUseDefaultFetchGroup(false). */ public FetchGroup getFetchGroup() { return null; } /** * PUBLIC: * Return the current locking mode. */ public short getLockMode() { if (lockingClause == null) { return DEFAULT_LOCK_MODE; } else { return lockingClause.getLockMode(); } } /** * INTERNAL: * Return all of the rows fetched by the query, used for 1-m joining. */ public List getDataResults() { return null; } /** * INTERNAL: * Return the time this query actually went to the database */ public long getExecutionTime() { return executionTime; } /** * PUBLIC: * Return the reference class of the query. */ public Class getReferenceClass() { return referenceClass; } /** * INTERNAL: * Return the reference class of the query. */ public String getReferenceClassName() { if ((referenceClassName == null) && (referenceClass != null)) { referenceClassName = referenceClass.getName(); } return referenceClassName; } /** * INTERNAL: * Return if partial attributes. */ public boolean hasPartialAttributeExpressions() { return false; } /** * PUBLIC: * Answers if the query lock mode is known to be LOCK or LOCK_NOWAIT. * * In the case of DEFAULT_LOCK_MODE and the query reference class being a CMP entity bean, * at execution time LOCK, LOCK_NOWAIT, or NO_LOCK will be decided. *

* If a single joined attribute was configured for pessimistic locking then * this will return true (after first execution) as the SQL contained a * FOR UPDATE OF clause. */ public boolean isLockQuery() { return getLockMode() > NO_LOCK; } /** * PUBLIC: * Return if this is an object building query. */ public boolean isObjectBuildingQuery() { return true; } /** * INTERNAL: * Answers if we are executing through a UnitOfWork and registering results. * This is only ever false if using the conforming without registering * feature. */ protected boolean isRegisteringResults() { return ((shouldRegisterResultsInUnitOfWork() && getDescriptor().shouldRegisterResultsInUnitOfWork()) || isLockQuery()); } /** * PUBLIC: * Refresh the attributes of the object(s) resulting from the query. * If cascading is used the private parts of the objects will also be refreshed. */ public void refreshIdentityMapResult() { setShouldRefreshIdentityMapResult(true); } /** * PUBLIC: * Refresh the attributes of the object(s) resulting from the query. * If cascading is used the private parts of the objects will also be refreshed. */ public void refreshRemoteIdentityMapResult() { setShouldRefreshRemoteIdentityMapResult(true); } /** * INTERNAL: * Constructs the final (registered) object for every individual object * queried via a UnitOfWork. *

* Called for every object in a read all, the object in a read object, and * every time the next or previous row is retrieved from a cursor. *

* The (conform) without registering feature is implemented here, and may * return an original non UnitOfWork registered result. *

* Pessimistically locked objects are tracked here. * * @param result may be an object (in the identity map of the parent session), * which becomes the original, or a raw database row if in transaction. * @param buildDirectlyFromRows true if must construct * a registered result from raw database rows. * * @return a refreshed UnitOfWork queried object, unwrapped. */ public Object registerIndividualResult(Object result, UnitOfWorkImpl unitOfWork, boolean buildDirectlyFromRows, JoinedAttributeManager joinManager) { Object clone = null; if (buildDirectlyFromRows) { // This method will either call back later with buildDirectlyFromRows == false, // or special code which builds/refreshes clones directly from the // row will be invoked. return buildObject((AbstractRecord)result); } // For bug 2612601 Conforming without registering in Unit Of Work. else if (!isRegisteringResults()) { clone = unitOfWork.getIdentityMapAccessorInstance().getIdentityMapManager().getFromIdentityMap(result); // If object not registered do not register it here! Simply return // the original to the user. // Avoid setting clone = original, in case revert(clone) is called. if (clone == null) { clone = result; } } else { // bug # 3183379 either the object comes from the shared cache and is existing, or //it is from a parent unit of work and this unit of work does not need to know if it is new //or not. It will query the parent unit of work to determine newness. clone = unitOfWork.registerExistingObject(result); } // Check for refreshing, require to revert in the unit of work to accomplish a refresh. if (shouldRefreshIdentityMapResult()) { // Revert only works in the object is in the parent cache, if it is not merge must be used. if (unitOfWork.getParent().getIdentityMapAccessor().containsObjectInIdentityMap(clone)) { if (shouldCascadeAllParts()) { unitOfWork.deepRevertObject(clone); } else if (shouldCascadePrivateParts()) { unitOfWork.revertObject(clone); } else if (shouldCascadeByMapping()) { unitOfWork.revertObject(clone, MergeManager.CASCADE_BY_MAPPING); } else if (!shouldCascadeParts()) { unitOfWork.shallowRevertObject(clone); } } else { if (shouldCascadeAllParts()) { unitOfWork.deepMergeClone(result); } else if (shouldCascadePrivateParts()) { unitOfWork.mergeClone(result); } else if (shouldCascadeByMapping()) { unitOfWork.mergeClone(result, MergeManager.CASCADE_BY_MAPPING); } else if (!shouldCascadeParts()) { unitOfWork.shallowMergeClone(result); } } } // record clone if referenced class has pessimistic locking policy recordCloneForPessimisticLocking(clone, unitOfWork); //BUG 3517: trigger indirection on the clone where required due to fetch joins on the query if (joinManager != null && joinManager.hasJoinedAttributeExpressions()) { triggerJoinExpressions(unitOfWork, joinManager, clone); } return clone; } /** * INTERNAL: * Fetch/trigger indirection on the clone passed in, based on join expressions in the joinManager. */ private void triggerJoinExpressions(UnitOfWorkImpl unitOfWork, JoinedAttributeManager joinManager, Object clone){ List joinExpressions = joinManager.getJoinedAttributeExpressions(); int size = joinExpressions.size(); if ( (size==0) || (clone==null) ){ return; } ClassDescriptor descriptor = unitOfWork.getDescriptor(clone); for (int index = 0; index < size; index++) { DatabaseMapping mapping = descriptor.getMappingForAttributeName((String)joinManager.getJoinedAttributes().get(index)); if (mapping !=null){ Object attributeValue = mapping.getRealAttributeValueFromObject(clone, unitOfWork); if (attributeValue != null){ if ( mapping.isForeignReferenceMapping() && (((ForeignReferenceMapping)mapping).getIndirectionPolicy().usesTransparentIndirection()) ) { ((IndirectContainer)attributeValue).getValueHolder().getValue(); } //recurse through the mapping if the expression's base isn't the base expressionBuilder QueryKeyExpression queryKeyExpression = (QueryKeyExpression)joinExpressions.get(index); if (!queryKeyExpression.getBaseExpression().isExpressionBuilder()){ ObjectLevelReadQuery nestedQuery =null; //if (joinManager.getJoinedMappingQueryClones()==null){ if (joinManager.getJoinedMappingQueries_()!=null){ nestedQuery = (ObjectLevelReadQuery)joinManager.getJoinedMappingQueries_().get(mapping); } /*}else{ nestedQuery = joinManager.getJoinedMappingQueryClones().get(mapping); }*/ if ( (nestedQuery!=null) && (nestedQuery.getJoinedAttributeManager()!=null)){ if (!mapping.isCollectionMapping()){ triggerJoinExpressions(unitOfWork, nestedQuery.getJoinedAttributeManager(), attributeValue); }else { ContainerPolicy cp = ((CollectionMapping)mapping).getContainerPolicy(); Object iterator = cp.iteratorFor(attributeValue); while (cp.hasNext(iterator)){ triggerJoinExpressions(unitOfWork, nestedQuery.getJoinedAttributeManager(), cp.next(iterator, unitOfWork)); } } } } } } } } /** * INTERNAL: * Set the the time this query went to the database. */ public void setExecutionTime(long executionTime) { this.executionTime = executionTime; } /** * PUBLIC: * Sets whether this is a pessimistically locking query. *

    *
  • ObjectBuildingQuery.LOCK: SELECT .... FOR UPDATE issued. *
  • ObjectBuildingQuery.LOCK_NOWAIT: SELECT .... FOR UPDATE NO WAIT issued. *
  • ObjectBuildingQuery.NO_LOCK: no pessimistic locking. *
  • ObjectBuildingQuery.DEFAULT_LOCK_MODE (default) and you have a CMP descriptor: * fine grained locking will occur. *
*

Fine Grained Locking: On execution the reference class * and those of all joined attributes will be checked. If any of these have a * PessimisticLockingPolicy set on their descriptor, they will be locked in a * SELECT ... FOR UPDATE OF ... {NO WAIT}. Issues fewer locks * and avoids setting the lock mode on each query. *

Example:readAllQuery.setSelectionCriteria(employee.get("address").equal("Ottawa")); *

  • LOCK: all employees in Ottawa and all referenced Ottawa addresses will be locked. *
  • DEFAULT_LOCK_MODE: if address is a joined attribute, and only address has a pessimistic * locking policy, only referenced Ottawa addresses will be locked. *
* @see oracle.toplink.essentials.descriptors.PessimisticLockingPolicy */ public void setLockMode(short lockMode) { lockingClause = ForUpdateClause.newInstance(lockMode); } /** * REQUIRED: * Set the reference class for the query. */ public void setReferenceClass(Class aClass) { referenceClass = aClass; setIsPrepared(false); } /** * INTERNAL: * Set the reference class for the query. */ public void setReferenceClassName(String aClass) { referenceClassName = aClass; setIsPrepared(false); } /** * PUBLIC: * Set if the attributes of the object(s) resulting from the query should be refreshed. * If cascading is used the private parts of the objects will also be refreshed. */ public void setShouldRefreshIdentityMapResult(boolean shouldRefreshIdentityMapResult) { this.shouldRefreshIdentityMapResult = shouldRefreshIdentityMapResult; if (shouldRefreshIdentityMapResult) { setShouldRefreshRemoteIdentityMapResult(true); } } /** * PUBLIC: * Set if the attributes of the object(s) resulting from the query should be refreshed. * If cascading is used the private parts of the objects will also be refreshed. */ public void setShouldRefreshRemoteIdentityMapResult(boolean shouldRefreshIdentityMapResult) { this.shouldRefreshRemoteIdentityMapResult = shouldRefreshIdentityMapResult; } /** * INTERNAL: * Set to false to have queries conform to a UnitOfWork without registering * any additional objects not already in that UnitOfWork. * @see #shouldRegisterResultsInUnitOfWork * @bug 2612601 */ public void setShouldRegisterResultsInUnitOfWork(boolean shouldRegisterResultsInUnitOfWork) { this.shouldRegisterResultsInUnitOfWork = shouldRegisterResultsInUnitOfWork; } /** * INTERNAL: * Allows one to do conforming in a UnitOfWork without registering. * Queries executed on a UnitOfWork will only return working copies for objects * that have already been registered. *

Extreme care should be taken in using this feature, for a user will * get back a mix of registered and original (unregistered) objects. *

Best used with a WrapperPolicy where invoking on an object will trigger * its registration (CMP). Without a WrapperPolicy {@link oracle.toplink.essentials.sessions.UnitOfWork#registerExistingObject registerExistingObject} * should be called on any object that you intend to change. * @return true by default. * @see #setShouldRegisterResultsInUnitOfWork * @see oracle.toplink.essentials.publicinterface.Descriptor#shouldRegisterResulstInUnitOfWork * @bug 2612601 */ public boolean shouldRegisterResultsInUnitOfWork() { return shouldRegisterResultsInUnitOfWork; } /** * INTERNAL: * Return if this is a full object query, not partial nor fetch group. */ public boolean shouldReadAllMappings() { return true; } /** * INTERNAL: * Check if the mapping is part of the partial attributes. */ public boolean shouldReadMapping(DatabaseMapping mapping) { return true; } /** * PUBLIC: * Set to a boolean. When set means refresh the instance * variables of referenceObject from the database. */ public boolean shouldRefreshIdentityMapResult() { return shouldRefreshIdentityMapResult; } /** * PUBLIC: * Set to a boolean. When set means refresh the instance * variables of referenceObject from the database. */ public boolean shouldRefreshRemoteIdentityMapResult() { return shouldRefreshRemoteIdentityMapResult; } public String toString() { if (getReferenceClass() == null) { return super.toString(); } return Helper.getShortClassName(getClass()) + "(" + getReferenceClass().getName() + ")"; } /** * ADVANCED: * Used for CMP only. This allows users to indicate whether cmp finders executed * at the beginning of a transaction should always be run against a UnitOfWork. * Defaults to true. *

* If set to false, then UnitOfWork allocation will be deferred until a business * method (including creates/removes) or finder with shouldProcessResultsInUnitOfWork == true * is invoked. Any finder executed before such a time, will do so against the * underlying ServerSession. Forcing finder execution to always go through a * UnitOfWork means the results will be cloned and cached in the UnitOfWork up * front. This is desired when the results will be accessed in the same transaction. *

* Note that finders executed with an unspecified transaction context will never * be executed against a UnitOfWork, even if this setting is true. This case may happen * with the NotSupported, Never, and Supports attributes. */ public void setShouldProcessResultsInUnitOfWork(boolean processResultsInUnitOfWork) { this.shouldProcessResultsInUnitOfWork = processResultsInUnitOfWork; } /** * ADVANCED: * Used for CMP only. Indicates whether cmp finders executed at the beginning * of a transaction should always be run against a UnitOfWork. * Defaults to true. *

* If set to false, then UnitOfWork allocation will be deferred until a business * method (including creates/removes) or finder with shouldProcessResultsInUnitOfWork == true * is invoked. Any finder executed before such a time, will do so against the * underlying ServerSession. Forcing finder execution to always go through a * UnitOfWork means the results will be cloned and cached in the UnitOfWork up * front. This is desired when the results will be accessed in the same transaction. *

* Note that finders executed with an unspecified transaction context will never * be executed against a UnitOfWork, even if this setting is true. This case may happen * with the NotSupported, Never, and Supports attributes. */ public boolean shouldProcessResultsInUnitOfWork() { return this.shouldProcessResultsInUnitOfWork; } /** * INTERNAL: * Return if the attribute is specified for joining. */ public boolean isAttributeJoined(ClassDescriptor mappingDescriptor, String attributeName) { return false; } /** * INTERNAL: * Helper method that checks if clone has been locked with uow. */ public boolean isClonePessimisticLocked(Object clone, UnitOfWorkImpl uow) { return false; } /** * INTERNAL: * Helper method that records clone with uow if query is pessimistic locking. */ public void recordCloneForPessimisticLocking(Object clone, UnitOfWorkImpl uow) { if ((isLockQuery()) && lockingClause.isReferenceClassLocked()) { uow.addPessimisticLockedClone(clone); } } /** * INTERNAL: Helper method to determine the default mode. If true and quey has a pessimistic locking policy, * locking will be configured according to the pessimistic locking policy. */ public boolean isDefaultLock() { return (lockingClause == null); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy