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

org.eclipse.persistence.queries.ReadObjectQuery Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     04/01/2011-2.3 Guy Pelletier
//       - 337323: Multi-tenant with shared schema support (part 2)
//     09/09/2011-2.3.1 Guy Pelletier
//       - 356197: Add new VPD type to MultitenantType
//     01/06/2011-2.3 Guy Pelletier
//       - 371453: JPA Multi-Tenancy in Bidirectional OneToOne Relation throws ArrayIndexOutOfBoundsException
package org.eclipse.persistence.queries;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.databaseaccess.Accessor;
import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.helper.InvalidObject;
import org.eclipse.persistence.internal.identitymaps.CacheId;
import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy;
import org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ResultSetRecord;
import org.eclipse.persistence.internal.sessions.SimpleResultSetRecord;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.internal.sessions.remote.RemoteSessionController;
import org.eclipse.persistence.internal.sessions.remote.Transporter;
import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.sessions.remote.DistributedSession;
import org.eclipse.persistence.tools.profiler.QueryMonitor;

/**
 * 

Purpose: * Concrete class for all read queries involving a single object. * *

Responsibilities: * Return a single object for the query. * Implements the inheritance feature when dealing with abstract descriptors. * * @author Yvon Lavoie * @since TOPLink/Java 1.0 */ public class ReadObjectQuery extends ObjectLevelReadQuery { /** Object that can be used in place of a selection criteria. */ protected transient Object selectionObject; /** Key that can be used in place of a selection criteria. */ protected Object selectionId; /** Can be used to refresh a specific non-cached instance from the database. */ protected boolean shouldLoadResultIntoSelectionObject = false; /** * PUBLIC: * Return a new read object query. * A reference class must be specified before execution. * It is better to provide the class and expression builder on construction to esnure a single expression builder is used. * If no selection criteria is specified this will reads the first object found in the database. */ public ReadObjectQuery() { super(); } /** * PUBLIC: * Return a new read object query. * By default, the query has no selection criteria. Executing this query without * selection criteria will always result in a database access to read the first * instance of the specified Class found in the database. This is true no * matter how cache usage is configured and even if an instance of the * specified Class exists in the cache. * Executing a query with selection criteria allows you to avoid a database * access if the selected instance is in the cache. For this reason, you may whish to use a ReadObjectQuery constructor that takes selection criteria, such as: {@link #ReadObjectQuery(Class, Call)}, {@link #ReadObjectQuery(Class, Expression)}, {@link #ReadObjectQuery(Class, ExpressionBuilder)}, {@link #ReadObjectQuery(ExpressionBuilder)}, {@link #ReadObjectQuery(Object)}, or {@link #ReadObjectQuery(Object, QueryByExamplePolicy)}. */ public ReadObjectQuery(Class classToRead) { this(); this.referenceClass = classToRead; } /** * PUBLIC: * Return a new read object query for the class and the selection criteria. */ public ReadObjectQuery(Class classToRead, Expression selectionCriteria) { this(); this.referenceClass = classToRead; setSelectionCriteria(selectionCriteria); } /** * PUBLIC: * Return a new read object query for the class. * The expression builder must be used for all associated expressions used with the query. */ public ReadObjectQuery(Class classToRead, ExpressionBuilder builder) { this(); this.defaultBuilder = builder; this.referenceClass = classToRead; } /** * PUBLIC: * Return a new read object query. * The call represents a database interaction such as SQL, Stored Procedure. */ public ReadObjectQuery(Class classToRead, Call call) { this(); this.referenceClass = classToRead; setCall(call); } /** * PUBLIC: * Return a new read object query. * The call represents a database interaction such as SQL, Stored Procedure. */ public ReadObjectQuery(Call call) { this(); setCall(call); } /** * PUBLIC: * Return a query to read the object with the same primary key as the provided object. * Note: This is not a query by example object, only the primary key will be used for the selection criteria. */ public ReadObjectQuery(Object objectToRead) { this(); setSelectionObject(objectToRead); } /** * PUBLIC: * Return a query by example query to find an object matching the attributes of the example object. */ public ReadObjectQuery(Object exampleObject, QueryByExamplePolicy policy) { this(); setExampleObject(exampleObject); setQueryByExamplePolicy(policy); } /** * PUBLIC: * The expression builder should be provide on creation to ensure only one is used. */ public ReadObjectQuery(ExpressionBuilder builder) { this(); this.defaultBuilder = builder; } /** * INTERNAL: *

This method is called by the object builder when building an original. * It will cause the original to be cached in the query results if the query * is set to do so. */ @Override public void cacheResult(Object object) { Object cachableObject = object; if (object == null) { this.temporaryCachedQueryResults = InvalidObject.instance(); } else { if (this.shouldUseWrapperPolicy) { cachableObject = this.session.wrapObject(object); } this.temporaryCachedQueryResults = cachableObject; } } /** * PUBLIC: * The cache will be checked only if the query contains exactly the primary key. * Queries can be configured to use the cache at several levels. * Other caching option are available. * @see #setCacheUsage(int) */ public void checkCacheByExactPrimaryKey() { setCacheUsage(CheckCacheByExactPrimaryKey); } /** * PUBLIC: * This is the default, the cache will be checked only if the query contains the primary key. * Queries can be configured to use the cache at several levels. * Other caching option are available. * @see #setCacheUsage(int) */ public void checkCacheByPrimaryKey() { setCacheUsage(CheckCacheByPrimaryKey); } /** * PUBLIC: * The cache will be checked completely, then if the object is not found or the query too complex the database will be queried. * Queries can be configured to use the cache at several levels. * Other caching option are available. * @see #setCacheUsage(int) */ public void checkCacheThenDatabase() { setCacheUsage(CheckCacheThenDatabase); } /** * INTERNAL: * Ensure that the descriptor has been set. */ @Override public void checkDescriptor(AbstractSession session) throws QueryException { if (this.descriptor == null) { if (getReferenceClass() == null) { throw QueryException.referenceClassMissing(this); } ClassDescriptor referenceDescriptor; //Bug#3947714 In case getSelectionObject() is proxy if (getSelectionObject() != null && session.getProject().hasProxyIndirection()) { referenceDescriptor = session.getDescriptor(getSelectionObject()); } else { referenceDescriptor = session.getDescriptor(getReferenceClass()); } if (referenceDescriptor == null) { throw QueryException.descriptorIsMissing(getReferenceClass(), this); } setDescriptor(referenceDescriptor); } } /** * INTERNAL: * The cache check is done before the prepare as a hit will not require the work to be done. */ @Override protected Object checkEarlyReturnLocal(AbstractSession session, AbstractRecord translationRow) { if (shouldCheckCache() && shouldMaintainCache() && (!shouldRefreshIdentityMapResult() && (!shouldRetrieveBypassCache())) && (!(session.isRemoteSession() && (shouldRefreshRemoteIdentityMapResult() || this.descriptor.shouldDisableCacheHitsOnRemote()))) && (!(shouldCheckDescriptorForCacheUsage() && this.descriptor.shouldDisableCacheHits())) && (!this.descriptor.isDescriptorForInterface())) { Object cachedObject = getQueryMechanism().checkCacheForObject(translationRow, session); this.isCacheCheckComplete = true; // Optimization: If find deleted object by exact primary // key expression or selection object/key just abort. if (cachedObject == InvalidObject.instance) { return cachedObject; } if (cachedObject != null) { if (shouldLoadResultIntoSelectionObject()) { ObjectBuilder builder = this.descriptor.getObjectBuilder(); builder.copyInto(cachedObject, getSelectionObject()); //put this object into the cache. This may cause some loss of identity session.getIdentityMapAccessorInstance().putInIdentityMap(getSelectionObject()); cachedObject = getSelectionObject(); } // check locking. If clone has not been locked, do not early return cached object if (isLockQuery() && (session.isUnitOfWork() && !((UnitOfWorkImpl)session).isPessimisticLocked(cachedObject))) { return null; } if (QueryMonitor.shouldMonitor()) { QueryMonitor.incrementReadObjectHits(this); } session.incrementProfile(SessionProfiler.CacheHits, this); } else { if (!session.isUnitOfWork()) { if (QueryMonitor.shouldMonitor()) { QueryMonitor.incrementReadObjectMisses(this); } session.incrementProfile(SessionProfiler.CacheMisses, this); } } if (shouldUseWrapperPolicy()) { cachedObject = this.descriptor.getObjectBuilder().wrapObject(cachedObject, session); } return cachedObject; } else { if (!session.isUnitOfWork()) { if (QueryMonitor.shouldMonitor()) { QueryMonitor.incrementReadObjectMisses(this); } session.incrementProfile(SessionProfiler.CacheMisses, this); } return null; } } /** * INTERNAL: * Check and return custom query flag. Custom query flag value is initialized when stored value is {@code null}. * Called from {@link #checkForCustomQuery(AbstractSession, AbstractRecord)} to retrieve custom query flag. * @param session Current session. * @param translationRow Database record. * @return Current custom query flag. Value will never be {@code null}. */ @Override protected Boolean checkCustomQueryFlag(final AbstractSession session, final AbstractRecord translationRow) { // #436871 - Use local copy to avoid NPE from concurrent modification. Boolean useCustomQuery = isCustomQueryUsed; if (useCustomQuery != null) { return useCustomQuery; } // Check if user defined a custom query in the query manager. if (!this.isUserDefined) { if (!isCallQuery() // By default all descriptors have a custom ("static") read-object query. // This allows the read-object query and SQL to be prepare once. && this.descriptor.getQueryManager().hasReadObjectQuery()) { // If the query require special SQL generation or execution do not use the static read object query. // PERF: the read-object query should always be static to ensure no regeneration of SQL. if ((!hasJoining() || !this.joinedAttributeManager.hasJoinedAttributeExpressions()) && (!hasPartialAttributeExpressions()) && (redirector == null) && !doNotRedirect && (!hasAsOfClause()) && (!hasNonDefaultFetchGroup()) && (this.shouldUseSerializedObjectPolicy == shouldUseSerializedObjectPolicyDefault) && this.wasDefaultLockMode && (shouldBindAllParameters == null) && (this.hintString == null)) { if ((this.selectionId != null) || (this.selectionObject != null)) {// Must be primary key. return Boolean.TRUE; } else { Expression selectionCriteria = getSelectionCriteria(); if (selectionCriteria != null) { AbstractRecord primaryKeyRow = this.descriptor.getObjectBuilder().extractPrimaryKeyRowFromExpression( selectionCriteria, translationRow, session); // Only execute the query if the selection criteria has the primary key fields set if (primaryKeyRow != null) { return Boolean.TRUE; } } } } } } return useCustomQuery; } /** * INTERNAL: * Get custom single object read query from query manager. * Called from {@link #checkForCustomQuery(AbstractSession, AbstractRecord)} to retrieve custom read query. * @return Custom single object read query from query manager. */ @Override protected ObjectLevelReadQuery getReadQuery() { return descriptor.getQueryManager().getReadObjectQuery(); } /** * INTERNAL: * Conform the result in the UnitOfWork. */ protected Object conformResult(Object result, UnitOfWorkImpl unitOfWork, AbstractRecord databaseRow, boolean buildDirectlyFromRows) { // Note that if the object does not conform even though other objects might exist on the database null is returned. // Note that new objects is checked before the read is executed so does not have to be re-checked. // Must unwrap as the built object is always wrapped. // Note the object is unwrapped on the parent which it belongs to, as we // do not want to trigger a registration just yet. Object clone = null; if (buildDirectlyFromRows) { clone = buildObject((AbstractRecord)result); } else { clone = registerIndividualResult( this.descriptor.getObjectBuilder().unwrapObject(result, unitOfWork.getParent()), null, unitOfWork, null, null); } Expression selectionCriteria = getSelectionCriteria(); if ((selectionCriteria != null) && (this.selectionId == null) && (this.selectionObject == null)) { ExpressionBuilder builder = selectionCriteria.getBuilder(); builder.setSession(unitOfWork.getRootSession(null)); builder.setQueryClass(getReferenceClass()); } clone = conformIndividualResult(clone, unitOfWork, databaseRow, selectionCriteria, null); if (clone == null) { return clone; } if (shouldUseWrapperPolicy()) { return this.descriptor.getObjectBuilder().wrapObject(clone, unitOfWork); } else { return clone; } } /** * PUBLIC: * Do not refesh/load into the selection object, this is the default. * This property allows for the selection object of the query to be refreshed or put into the TopLink cache. * By default on a read or refresh the object in the cache is refreshed and returned or a new object is built from the database, * in some cases such as EJB BMP it is desirable to refresh or load into the object passed into the read object query. *

Note: This forces the selection object into the cache a replaces any existing object that may already be there, * this is a strict violation of object identity and other objects can still be refering to the old object. */ public void dontLoadResultIntoSelectionObject() { setShouldLoadResultIntoSelectionObject(false); } /** * INTERNAL: * Execute the query. If there are cached results return those. * This must override the super to support result caching. * * @param session the session in which the receiver will be executed. * @return An object or vector, the result of executing the query. * @exception DatabaseException - an error has occurred on the database */ @Override public Object execute(AbstractSession session, AbstractRecord row) throws DatabaseException { if (shouldCacheQueryResults()) { if (shouldConformResultsInUnitOfWork()) { throw QueryException.cannotConformAndCacheQueryResults(this); } if (isPrepared()) {// only prepared queries can have cached results. Object result = getQueryResults(session, row, true); // Bug6138532 - if result is "cached no results", return null immediately if (result == InvalidObject.instance) { return null; } if (result != null) { if (session.isUnitOfWork()) { result = ((UnitOfWorkImpl)session).registerExistingObject(result); } return result; } } } return super.execute(session, row); } /** * INTERNAL: * Execute the query. * Do a cache lookup and build object from row if required. * @exception DatabaseException - an error has occurred on the database * @return object - the first object found or null if none. */ @Override protected Object executeObjectLevelReadQuery() throws DatabaseException { if (this.descriptor.isDescriptorForInterface() || this.descriptor.hasTablePerClassPolicy()) { Object returnValue = this.descriptor.getInterfacePolicy().selectOneObjectUsingMultipleTableSubclassRead(this); if (this.descriptor.hasTablePerClassPolicy() && (!this.descriptor.isAbstract()) && (returnValue == null)) { // let it fall through to query the root. } else { this.executionTime = System.currentTimeMillis(); return returnValue; } } boolean shouldSetRowsForJoins = hasJoining() && this.joinedAttributeManager.isToManyJoin(); AbstractSession session = getSession(); Object result = null; AbstractRecord row = null; Object sopObject = getTranslationRow().getSopObject(); boolean useOptimization = false; if (sopObject == null) { useOptimization = usesResultSetAccessOptimization(); } if (useOptimization) { DatabaseCall call = ((DatasourceCallQueryMechanism)this.queryMechanism).selectResultSet(); this.executionTime = System.currentTimeMillis(); boolean exceptionOccured = false; ResultSet resultSet = call.getResult(); DatabaseAccessor dbAccessor = (DatabaseAccessor)getAccessor(); try { if (resultSet.next()) { ResultSetMetaData metaData = call.getResult().getMetaData(); boolean useSimple = this.descriptor.getObjectBuilder().isSimple(); DatabasePlatform platform = dbAccessor.getPlatform(); boolean optimizeData = platform.shouldOptimizeDataConversion(); if (useSimple) { row = new SimpleResultSetRecord(call.getFields(), call.getFieldsArray(), resultSet, metaData, dbAccessor, getExecutionSession(), platform, optimizeData); if (this.descriptor.isDescriptorTypeAggregate()) { // Aggregate Collection may have an unmapped primary key referencing the owner, the corresponding field will not be used when the object is populated and therefore may not be cleared. ((SimpleResultSetRecord)row).setShouldKeepValues(true); } } else { row = new ResultSetRecord(call.getFields(), call.getFieldsArray(), resultSet, metaData, dbAccessor, getExecutionSession(), platform, optimizeData); } if (session.isUnitOfWork()) { result = registerResultInUnitOfWork(row, (UnitOfWorkImpl)session, this.translationRow, true); } else { result = buildObject(row); } if (!useSimple && this.descriptor.getObjectBuilder().shouldKeepRow()) { if (((ResultSetRecord)row).hasResultSet()) { // ResultSet has not been fully triggered - that means the cached object was used. // Yet the row still may be cached in a value holder (see loadBatchReadAttributes and loadJoinedAttributes methods). // Remove ResultSet to avoid attempt to trigger it (already closed) when pk or fk values (already extracted) accessed when the value holder is instantiated. ((ResultSetRecord)row).removeResultSet(); } else { ((ResultSetRecord)row).removeNonIndirectionValues(); } } } } catch (SQLException exception) { exceptionOccured = true; DatabaseException commException = dbAccessor.processExceptionForCommError(session, exception, call); if (commException != null) { throw commException; } throw DatabaseException.sqlException(exception, call, getAccessor(), session, false); } finally { try { if (resultSet != null) { resultSet.close(); } if (dbAccessor != null) { if (call.getStatement() != null) { dbAccessor.releaseStatement(call.getStatement(), call.getSQLString(), call, session); } } if (call.hasAllocatedConnection()) { getExecutionSession().releaseConnectionAfterCall(this); } } catch (RuntimeException cleanupException) { if (!exceptionOccured) { throw cleanupException; } } catch (SQLException cleanupSQLException) { if (!exceptionOccured) { throw DatabaseException.sqlException(cleanupSQLException, call, dbAccessor, session, false); } } } } else { if (sopObject != null) { row = new DatabaseRecord(0); row.setSopObject(sopObject); } else { // If using 1-m joins, must select all rows. if (shouldSetRowsForJoins) { List rows = getQueryMechanism().selectAllRows(); if (rows.size() > 0) { row = (AbstractRecord)rows.get(0); } getJoinedAttributeManager().setDataResults(rows, session); } else { row = getQueryMechanism().selectOneRow(); } } this.executionTime = System.currentTimeMillis(); if (row != null) { if (session.isUnitOfWork()) { result = registerResultInUnitOfWork(row, (UnitOfWorkImpl)session, this.translationRow, true); } else { result = buildObject(row); } if (sopObject != null) { // remove sopObject so it's not stuck in a value holder. row.setSopObject(null); } } } if ((result == null) && shouldCacheQueryResults()) { cacheResult(null); } if ((result == null) && this.shouldRefreshIdentityMapResult) { // bug5955326, should invalidate the shared cached if refreshed object no longer exists. if (this.selectionId != null) { session.getParentIdentityMapSession(this.descriptor, true, true).getIdentityMapAccessor().invalidateObject(this.selectionId, this.referenceClass); } else if (this.selectionObject != null) { session.getParentIdentityMapSession(this.descriptor, true, true).getIdentityMapAccessor().invalidateObject(this.selectionObject); } } if (this.shouldIncludeData && (sopObject == null)) { ComplexQueryResult complexResult = new ComplexQueryResult(); complexResult.setResult(result); complexResult.setData(row); return complexResult; } return result; } /** * INTERNAL: * Execute the query building the objects directly from the database result-set. * @exception DatabaseException - an error has occurred on the database * @return object - the first object found or null if none. */ @Override protected Object executeObjectLevelReadQueryFromResultSet() throws DatabaseException { AbstractSession session = this.session; DatabasePlatform platform = session.getPlatform(); DatabaseCall call = ((DatasourceCallQueryMechanism)this.queryMechanism).selectResultSet(); Statement statement = call.getStatement(); ResultSet resultSet = call.getResult(); DatabaseAccessor accessor = (DatabaseAccessor)((List)this.accessors).get(0); boolean exceptionOccured = false; try { if (!resultSet.next()) { return null; } ResultSetMetaData metaData = resultSet.getMetaData(); return this.descriptor.getObjectBuilder().buildObjectFromResultSet(this, null, resultSet, session, accessor, metaData, platform, call.getFields(), call.getFieldsArray()); } catch (SQLException exception) { exceptionOccured = true; DatabaseException commException = accessor.processExceptionForCommError(session, exception, call); if (commException != null) { throw commException; } throw DatabaseException.sqlException(exception, call, accessor, session, false); } finally { try { if (resultSet != null) { resultSet.close(); } if (statement != null) { accessor.releaseStatement(statement, call.getSQLString(), call, session); } if (accessor != null) { session.releaseReadConnection(accessor); } } catch (SQLException exception) { if (!exceptionOccured) { //in the case of an external connection pool the connection may be null after the statement release // if it is null we will be unable to check the connection for a comm error and //therefore must return as if it was not a comm error. DatabaseException commException = accessor.processExceptionForCommError(session, exception, call); if (commException != null) { throw commException; } throw DatabaseException.sqlException(exception, call, accessor, session, false); } } } } /** * INTERNAL: * Extract the correct query result from the transporter. */ @Override public Object extractRemoteResult(Transporter transporter) { return ((DistributedSession)getSession()).getObjectCorrespondingTo(transporter.getObject(), transporter.getObjectDescriptors(), new IdentityHashMap(), this); } /** * INTERNAL: * Returns the specific default redirector for this query type. There are numerous default query redirectors. * See ClassDescriptor for their types. */ @Override protected QueryRedirector getDefaultRedirector(){ return descriptor.getDefaultReadObjectQueryRedirector(); } /** * PUBLIC: * Return the selection object of the query. * This can be used instead of a where clause expression for single object primary key queries. * The selection object given should have a primary key defined, * this primary key will be used to query the database instance of the same object. * This is a basic form of query by example where only the primary key is required, * it can be used for simple query forms, or testing. */ public Object getSelectionObject() { return selectionObject; } /** * PUBLIC: * Return if this is a read object query. */ @Override public boolean isReadObjectQuery() { return true; } /** * INTERNAL: * Return if the query is by primary key. */ @Override public boolean isPrimaryKeyQuery() { return (this.selectionId != null) || (this.selectionObject != null); } /** * PUBLIC: * Allow for the selection object of the query to be refreshed or put into the EclipseLink cache. * By default on a read or refresh the object in the cache is refreshed and returned or a new object is built from the database, * in some cases such as EJB BMP it is desirable to refresh or load into the object passed into the read object query. *

Note: This forces the selection object into the cache a replaces any existing object that may already be there, * this is a strict violation of object identity and other objects can still be referring to the old object. */ public void loadResultIntoSelectionObject() { setShouldLoadResultIntoSelectionObject(true); } /** * INTERNAL: * Copy all setting from the query. * This is used to morph queries from one type to the other. * By default this calls prepareFromQuery, but additional properties may be required * to be copied as prepareFromQuery only copies properties that affect the SQL. */ @Override public void copyFromQuery(DatabaseQuery query) { super.copyFromQuery(query); if (query.isReadObjectQuery()) { ReadObjectQuery readQuery = (ReadObjectQuery)query; this.selectionId = readQuery.selectionId; this.selectionObject = readQuery.selectionObject; this.shouldLoadResultIntoSelectionObject = readQuery.shouldLoadResultIntoSelectionObject; } } /** * INTERNAL: * Prepare the receiver for execution in a session. */ @Override protected void prepare() throws QueryException { if (prepareFromCachedQuery()) { return; } super.prepare(); if ((this.selectionId != null) || (this.selectionObject != null)) { // The expression is set in the prepare as params. setSelectionCriteria(this.descriptor.getObjectBuilder().getPrimaryKeyExpression()); setExpressionBuilder(getSelectionCriteria().getBuilder()); extendPessimisticLockScope(); // For bug 2989998 the translation row is required to be set at this point. if (!shouldPrepare()) { if (this.selectionId != null) { // Row must come from the key. setTranslationRow(this.descriptor.getObjectBuilder().buildRowFromPrimaryKeyValues(this.selectionId, this.session)); } else {//(getSelectionObject() != null) setTranslationRow(this.descriptor.getObjectBuilder().buildRowForTranslation(this.selectionObject, this.session)); } } } if (this.descriptor.isDescriptorForInterface()) { return; } // PERF: Disable cache check if not a primary key query. if (isExpressionQuery()) { Expression selectionCriteria = getSelectionCriteria(); if (selectionCriteria != null) { if (((this.cacheUsage == CheckCacheByPrimaryKey) && (!this.descriptor.getObjectBuilder().isPrimaryKeyExpression(false, selectionCriteria, this.session))) || ((this.cacheUsage == CheckCacheByExactPrimaryKey) && (!this.descriptor.getObjectBuilder().isPrimaryKeyExpression(true, selectionCriteria, this.session)))) { this.cacheUsage = DoNotCheckCache; } } } // If using 1-m joining select all rows. if ((this.joinedAttributeManager != null) && this.joinedAttributeManager.isToManyJoin()) { getQueryMechanism().prepareSelectAllRows(); } else { getQueryMechanism().prepareSelectOneRow(); } // should be called after prepareSelectRow so that the call knows whether it returns ResultSet prepareResultSetAccessOptimization(); } /** * INTERNAL: * Set the properties needed to be cascaded into the custom query including the translation row. * This is used only for primary key queries, as the descriptor query manager * stores a predefined query for this query to avoid having to re-prepare and allow for customization. */ @Override protected void prepareCustomQuery(DatabaseQuery customQuery) { super.prepareCustomQuery(customQuery); ReadObjectQuery customReadQuery = (ReadObjectQuery)customQuery; customReadQuery.shouldRefreshIdentityMapResult = this.shouldRefreshIdentityMapResult; customReadQuery.cascadePolicy = this.cascadePolicy; customReadQuery.shouldMaintainCache = this.shouldMaintainCache; customReadQuery.shouldUseWrapperPolicy = this.shouldUseWrapperPolicy; // CR... was missing some values, execution could cause infinite loop. customReadQuery.queryId = this.queryId; customReadQuery.executionTime = this.executionTime; customReadQuery.shouldLoadResultIntoSelectionObject = this.shouldLoadResultIntoSelectionObject; AbstractRecord primaryKeyRow; if (this.selectionObject != null) { // CR#... Must also set the selection object as may be loading into the object (refresh) customReadQuery.selectionObject = this.selectionObject; // The translation/primary key row will be set in prepareForExecution. } else if (this.selectionId != null) { customReadQuery.selectionId = this.selectionId; } else { // The primary key row must be used. primaryKeyRow = customQuery.getDescriptor().getObjectBuilder().extractPrimaryKeyRowFromExpression(getSelectionCriteria(), customQuery.getTranslationRow(), customReadQuery.getSession()); customReadQuery.setTranslationRow(primaryKeyRow); } } /** * INTERNAL: * Prepare the receiver for execution in a session. */ @Override public void prepareForExecution() throws QueryException { super.prepareForExecution(); // For bug 2989998 the translation row now sometimes set earlier in prepare. if (shouldPrepare()) { if (this.selectionId != null) { // Row must come from the key. this.translationRow = this.descriptor.getObjectBuilder().buildRowFromPrimaryKeyValues(this.selectionId, getSession()); } else if (this.selectionObject != null) { // The expression is set in the prepare as params. this.translationRow = this.descriptor.getObjectBuilder().buildRowForTranslation(this.selectionObject, getSession()); } } // If we have tenant discriminator fields we need to add them to the // database row when doing a primary key query. // Modifying the translation row here will modify it on the original // query which is not good (will append the tenant field to the sql call // for subsequent queries) The translation row must be cloned to isolate // this. if (getDescriptor().hasMultitenantPolicy()) { this.translationRow = this.translationRow.clone(); getDescriptor().getMultitenantPolicy().addFieldsToRow(this.translationRow, getSession()); } } /** * INTERNAL: * Prepare the receiver for execution in a session. */ @Override protected void prePrepare() throws QueryException { super.prePrepare(); //Bug#3947714 In case getSelectionObject() is proxy if (getSelectionObject() != null && getSession().getProject().hasProxyIndirection()) { setSelectionObject(ProxyIndirectionPolicy.getValueFromProxy(getSelectionObject())); } } /** * INTERNAL: * All objects queried via a UnitOfWork get registered here. If the query * went to the database. *

* Involves registering the query result individually and in totality, and * hence refreshing / conforming is done here. * @param result may be collection (read all) or an object (read one), * or even a cursor. If in transaction the shared cache will * be bypassed, meaning the result may not be originals from the parent * but raw database rows. * @param unitOfWork the unitOfWork the result is being registered in. * @param arguments the original arguments/parameters passed to the query * execution. Used by conforming * @param buildDirectlyFromRows If in transaction must construct * a registered result from raw database rows. * @return the final (conformed, refreshed, wrapped) UnitOfWork query result */ @Override public Object registerResultInUnitOfWork(Object result, UnitOfWorkImpl unitOfWork, AbstractRecord arguments, boolean buildDirectlyFromRows) { if (result == null) { return null; } if (unitOfWork.hasCloneMapping() // PERF: Avoid conforming empty uow. && (shouldConformResultsInUnitOfWork() || this.descriptor.shouldAlwaysConformResultsInUnitOfWork())) { return conformResult(result, unitOfWork, arguments, buildDirectlyFromRows); } Object clone = null; if (buildDirectlyFromRows) { clone = buildObject((AbstractRecord)result); } else { clone = registerIndividualResult(result, null, unitOfWork, null, null); } if (shouldUseWrapperPolicy()) { clone = this.descriptor.getObjectBuilder().wrapObject(clone, unitOfWork); } return clone; } @Override protected Object remoteExecute() { // Do a cache lookup. checkDescriptor(session); // As the selection object is transient, compute the key. if (getSelectionObject() != null) { // Must be checked separately as the expression and row is not yet set. setSelectionId(getDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(getSelectionObject(), session)); } Object cacheHit = checkEarlyReturn(getSession(), getTranslationRow()); if ((cacheHit != null) || shouldCheckCacheOnly()) { if (cacheHit == InvalidObject.instance) { return null; } return cacheHit; } return super.remoteExecute(); } /** * INTERNAL: * replace the value holders in the specified result object(s) */ @Override public Map replaceValueHoldersIn(Object object, RemoteSessionController controller) { return controller.replaceValueHoldersIn(object); } /** * INTERNAL: * Return the primary key stored in this query * * @return the selection id of this ReadObjectQuery */ @Override protected Object getQueryPrimaryKey(){ return getSelectionId(); } /** * PUBLIC: * Return Id of the object to be selected by the query. */ public Object getSelectionId() { return this.selectionId; } /** * INTERNAL: * Clear the selection id and object. * This is done after cloning queries to prepare them in inheritance. */ public void clearSelectionId() { this.selectionId = null; this.selectionObject = null; } /** * PUBLIC: * The Id of the object to be selected by the query. * This will generate a query by primary key. * This can be used instead of an Expression, SQL, or JPQL, or example object. * The Id is the Id value for a singleton primary key, * for a composite it is an instance of CacheId. * @see CacheId */ public void setSelectionId(Object id) { this.selectionId = id; } /** * PUBLIC: * Used to set the where clause of the query. * This can be used instead of a where clause expression for single object primary key queries. * The selection object given should have a primary key defined, * this primary key will be used to query the database instance of the same object. * This is a basic form of query by example where only the primary key is required, * it can be used for simple query forms, or testing. */ public void setSelectionObject(Object selectionObject) { if (selectionObject == null) { throw QueryException.selectionObjectCannotBeNull(this); } setSelectionId(null); // Check if the query needs to be unprepared. if ((this.selectionObject == null) || (this.selectionObject.getClass() != selectionObject.getClass())) { setIsPrepared(false); } setReferenceClass(selectionObject.getClass()); this.selectionObject = selectionObject; } /** * PUBLIC: * Allow for the selection object of the query to be refreshed or put into the EclipseLink cache. * By default on a read or refresh the object in the cache is refreshed and returned or a new object is built from the database, * in some cases such as EJB BMP it is desirable to refresh or load into the object passed into the read object query. *

Note: This forces the selection object into the cache a replaces any existing object that may already be there, * this is a strict violation of object identity and other objects can still be referring to the old object. */ public void setShouldLoadResultIntoSelectionObject(boolean shouldLoadResultIntoSelectionObject) { this.shouldLoadResultIntoSelectionObject = shouldLoadResultIntoSelectionObject; } /** * PUBLIC: * Return if cache should be checked. */ public boolean shouldCheckCacheByExactPrimaryKey() { return this.cacheUsage == CheckCacheByExactPrimaryKey; } /** * PUBLIC: * Return if cache should be checked. */ public boolean shouldCheckCacheByPrimaryKey() { return (this.cacheUsage == CheckCacheByPrimaryKey) || (this.cacheUsage == UseDescriptorSetting); } /** * PUBLIC: * Return if cache should be checked. */ public boolean shouldCheckCacheThenDatabase() { return this.cacheUsage == CheckCacheThenDatabase; } /** * PUBLIC: * return true if the result should be loaded into the passed in selection Object */ public boolean shouldLoadResultIntoSelectionObject() { return shouldLoadResultIntoSelectionObject; } /** * INTERNAL: * Return if the query has an non-default fetch group defined for itself. */ protected boolean hasNonDefaultFetchGroup() { return this.descriptor.hasFetchGroupManager() && ((this.fetchGroup != null) || (this.fetchGroupName != null) || (!this.shouldUseDefaultFetchGroup && (this.descriptor.getFetchGroupManager().getDefaultFetchGroup() != null))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy