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

org.eclipse.persistence.internal.expressions.ObjectExpression Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2021 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
package org.eclipse.persistence.internal.expressions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.NonSynchronizedVector;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.querykeys.ForeignReferenceQueryKey;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadQuery;

/**
 * Superclass for any object type expressions.
 */
public abstract class ObjectExpression extends DataExpression {
    protected transient ClassDescriptor descriptor;
    public List derivedExpressions;

    /** indicates whether subclasses should be joined */
    protected boolean shouldUseOuterJoinForMultitableInheritance;

    /** Is this query key to be resolved using an outer join or not. Does not apply to attributes. */
    protected boolean shouldUseOuterJoin;

    /** Allow an expression node to be cast to a subclass or implementation class. */
    protected Class castClass;

    /** Defines that this expression has been joined to the source expression. */
    protected Expression joinSource;

    /** Allow for an ON clause to be specified on a join condition. */
    protected Expression onClause;

    /** Used to track the index of the OuterJoinExpressionHolder that might be associated to this expression */
    private Integer outerJoinExpIndex = null;

    /** Allow hasBeenAliased to be marked independently from the existence of the tableAliases collection. */
    protected boolean hasBeenAliased = false;

    protected ObjectExpression() {
        this.shouldUseOuterJoin = false;
    }

    /**
     * Return an expression that allows you to treat its base as if it were a subclass of the class returned by the base
     * This can only be called on an ExpressionBuilder, the result of expression.get(String), expression.getAllowingNull(String),
     * the result of expression.anyOf("String") or the result of expression.anyOfAllowingNull("String")
     *
     * downcast uses Expression.type() internally to guarantee the results are of the specified class.
     * 

Example: *

     *     Expression: employee.get("project").as(LargeProject.class).get("budget").equal(1000)
     *     Java: ((LargeProject)employee.getProjects().get(0)).getBudget() == 1000
     *     SQL: LPROJ.PROJ_ID (+)= PROJ.PROJ_ID AND L_PROJ.BUDGET = 1000 AND PROJ.TYPE = "L"
     * 
*/ @Override public Expression treat(Class castClass){ //to be used on expressionBuilders QueryKeyExpression clonedExpression = new TreatAsExpression(castClass, this); clonedExpression.shouldQueryToManyRelationship = false; clonedExpression.hasQueryKey = false; clonedExpression.hasMapping = false; this.addDerivedExpression(clonedExpression); return clonedExpression; } /** * INTERNAL: * Return if the expression is equal to the other. * This is used to allow dynamic expression's SQL to be cached. */ @Override public boolean equals(Object expression) { if (this == expression) { return true; } if (!super.equals(expression)) { return false; } if ((this.onClause != null) || (((ObjectExpression)expression).onClause != null)) { return false; } return this.shouldUseOuterJoin == ((ObjectExpression)expression).shouldUseOuterJoin; } /** * INTERNAL: * Add the expression as a derived child of this expression. * i.e. e.get("name"), "name" is a derived child of "e". */ public synchronized void addDerivedExpression(Expression addThis) { if (this.derivedExpressions == null) { this.derivedExpressions = new ArrayList(); } this.derivedExpressions.add(addThis); } /** * INTERNAL: * Return the expression to join the main table of this node to any auxiliary tables. */ public Expression additionalExpressionCriteria() { if (getDescriptor() == null) { return null; } Expression criteria = getDescriptor().getQueryManager().getAdditionalJoinExpression(); if(getSession().getPlatform().shouldPrintOuterJoinInWhereClause()) { if(isUsingOuterJoinForMultitableInheritance()) { Expression childrenCriteria = getDescriptor().getInheritancePolicy().getChildrenJoinExpression(); childrenCriteria = getBaseExpression().twist(childrenCriteria, this); childrenCriteria.convertToUseOuterJoin(); if(criteria == null) { criteria = childrenCriteria; } else { criteria = criteria.and(childrenCriteria); } } } return criteria; } /** * INTERNAL: * Used in case outer joins should be printed in FROM clause. * Each of the additional tables mapped to expressions that joins it. */ public Map additionalExpressionCriteriaMap() { if (getDescriptor() == null) { return null; } HashMap tablesJoinExpressions = null; if(isUsingOuterJoinForMultitableInheritance()) { tablesJoinExpressions = new HashMap(); List childrenTables = getDescriptor().getInheritancePolicy().getChildrenTables(); for( int i=0; i < childrenTables.size(); i++) { DatabaseTable table = childrenTables.get(i); Expression joinExpression = getDescriptor().getInheritancePolicy().getChildrenTablesJoinExpressions().get(table); if (getBaseExpression() != null){ joinExpression = getBaseExpression().twist(joinExpression, this); } else { joinExpression = twist(joinExpression, this); } tablesJoinExpressions.put(table, joinExpression); } } return tablesJoinExpressions; } /** * INTERNAL: * Assign aliases to any tables which I own. Start with t(initialValue), * and return the new value of the counter , i.e. if initialValue is one * and I have tables ADDRESS and EMPLOYEE I will assign them t1 and t2 respectively, and return 3. */ @Override public int assignTableAliasesStartingAt(int initialValue) { //This differs from the Expression implementation only in that it must set the hasBeenAliased flag when done. int returnVal = super.assignTableAliasesStartingAt(initialValue); this.hasBeenAliased = true; return returnVal; } /** * PUBLIC: * Return an expression representing traversal of a 1:many or many:many relationship. * This allows you to query whether any of the "many" side of the relationship satisfies the remaining criteria. *

Example: *

     *     Expression: employee.anyOf("managedEmployees").get("firstName").equal("Bob")
     *     Java: no direct equivalent
     *     SQL: SELECT DISTINCT ... WHERE (t2.MGR_ID = t1.ID) AND (t2.F_NAME = 'Bob')
     * 
* @param shouldJoinBeIndependent indicates whether a new expression should be created. */ @Override public Expression anyOf(String attributeName, boolean shouldJoinBeIndependent) { QueryKeyExpression queryKey; if (shouldJoinBeIndependent) { queryKey = newDerivedExpressionNamed(attributeName); } else { queryKey = derivedExpressionNamed(attributeName); } queryKey.doQueryToManyRelationship(); return queryKey; } /** * ADVANCED: * Return an expression representing traversal of a 1:many or many:many relationship. * This allows you to query whether any of the "many" side of the relationship satisfies the remaining criteria. *

Example: *

     *     Expression: employee.anyOf("managedEmployees").get("firstName").equal("Bob")
     *     Java: no direct equivalent
     *     SQL: SELECT DISTINCT ... WHERE (t2.MGR_ID (+) = t1.ID) AND (t2.F_NAME = 'Bob')
     * 
* @param shouldJoinBeIndependent indicates whether a new expression should be created. */ @Override public Expression anyOfAllowingNone(String attributeName, boolean shouldJoinBeIndependent) { QueryKeyExpression queryKey; if (shouldJoinBeIndependent) { queryKey = newDerivedExpressionNamed(attributeName); } else { queryKey = derivedExpressionNamed(attributeName); } queryKey.doUseOuterJoin(); queryKey.doQueryToManyRelationship(); return queryKey; } /** * INTERNAL * Return true if it uses a cast class and query is downcasting. It will * look into inheritance hierarchy of the root descriptor. */ public boolean isDowncast(ClassDescriptor rootDescriptor, AbstractSession session) { if (castClass == null){ return false; } if (rootDescriptor.getJavaClass() == castClass){ return false; } ClassDescriptor castDescriptor = session.getClassDescriptor(castClass); if (castDescriptor == null){ throw QueryException.couldNotFindCastDescriptor(castClass, getBaseExpression()); } if (castDescriptor.getInheritancePolicy() == null){ throw QueryException.castMustUseInheritance(getBaseExpression()); } ClassDescriptor parentDescriptor = castDescriptor.getInheritancePolicy().getParentDescriptor(); while (parentDescriptor != null){ if (parentDescriptor == rootDescriptor){ return true; } parentDescriptor = parentDescriptor.getInheritancePolicy().getParentDescriptor(); } throw QueryException.couldNotFindCastDescriptor(castClass, getBaseExpression()); } /** * INTERNAL * Return true if treat was used on this expression */ public boolean isTreatUsed() { if (this.hasDerivedExpressions() ) for (Expression exp: this.derivedExpressions) { if (exp.isTreatExpression()) { return true; } } return false; } /** * INTERNAL * Return the descriptor which contains this query key, look in the inheritance hierarchy * of rootDescriptor for the descriptor. */ public ClassDescriptor convertToCastDescriptor(ClassDescriptor rootDescriptor, AbstractSession session) { if (castClass == null || rootDescriptor == null || rootDescriptor.getJavaClass() == castClass) { return rootDescriptor; } ClassDescriptor castDescriptor = session.getClassDescriptor(castClass); if (castDescriptor == null){ throw QueryException.couldNotFindCastDescriptor(castClass, getBaseExpression()); } if (!castDescriptor.hasInheritance()){ throw QueryException.castMustUseInheritance(getBaseExpression()); } ClassDescriptor parentDescriptor = castDescriptor.getInheritancePolicy().getParentDescriptor(); while (parentDescriptor != null){ if (parentDescriptor == rootDescriptor){ return castDescriptor; } parentDescriptor = parentDescriptor.getInheritancePolicy().getParentDescriptor(); } ClassDescriptor childDescriptor = rootDescriptor; while (childDescriptor != null){ if (childDescriptor == castDescriptor){ return rootDescriptor; } childDescriptor = childDescriptor.getInheritancePolicy().getParentDescriptor(); } throw QueryException.couldNotFindCastDescriptor(castClass, getBaseExpression()); } public List copyDerivedExpressions(Map alreadyDone) { if (this.derivedExpressions == null) { return null; } List derivedExpressionsCopy; synchronized(this) { derivedExpressionsCopy = new ArrayList(this.derivedExpressions); } List result = new ArrayList(derivedExpressionsCopy.size()); for (Expression exp : derivedExpressionsCopy) { result.add(exp.copiedVersionFrom(alreadyDone)); } return result; } public QueryKeyExpression derivedExpressionNamed(String attributeName) { QueryKeyExpression existing = existingDerivedExpressionNamed(attributeName); if (existing != null) { return existing; } return newDerivedExpressionNamed(attributeName); } public Expression derivedManualExpressionNamed(String attributeName, ClassDescriptor aDescriptor) { Expression existing = existingDerivedExpressionNamed(attributeName); if (existing != null) { return existing; } return newManualDerivedExpressionNamed(attributeName, aDescriptor); } public void doNotUseOuterJoin() { shouldUseOuterJoin = false; } public void doUseOuterJoin() { shouldUseOuterJoin = true; } public QueryKeyExpression existingDerivedExpressionNamed(String attributeName) { if (this.derivedExpressions == null) { return null; } List derivedExpressionsCopy; synchronized(this) { derivedExpressionsCopy = new ArrayList(this.derivedExpressions); } for (Expression derivedExpression : derivedExpressionsCopy) { QueryKeyExpression exp = (QueryKeyExpression)derivedExpression; if (exp.getName().equals(attributeName)) { return exp; } } return null; } /** * Return the expression from the attribute dervied from this expression. */ @Override public Expression get(String attributeName, boolean forceInnerJoin) { ObjectExpression result = derivedExpressionNamed(attributeName); if (forceInnerJoin) { result.doNotUseOuterJoin(); } return result; } /** * Defines a join between this expression and the target expression based on the ON clause. */ @Override public Expression leftJoin(Expression target, Expression onClause) { join(target, onClause); ((ObjectExpression)target).doUseOuterJoin(); return this; } /** * Defines a join between this expression and the target expression based on the ON clause. */ @Override public Expression join(Expression target, Expression onClause) { if (target instanceof ObjectExpression) { ((ObjectExpression)target).setJoinSource(this); ((ObjectExpression)target).setOnClause(onClause); } else { throw new IllegalArgumentException(); } return this; } @Override public Expression getAllowingNull(String attributeName) { ObjectExpression exp = existingDerivedExpressionNamed(attributeName); // The same (aliased) table cannot participate in a normal join and an outer join. // To help enforce this, if the node already exists if (exp != null) { return exp; } ObjectExpression result = derivedExpressionNamed(attributeName); result.doUseOuterJoin(); return result; } public Class getCastClass() { return castClass; } /** * PUBLIC: * Return an expression that wraps the inheritance type field in an expression. *

Example: *

     *  builder.getClassForInheritance().equal(SmallProject.class);
     * 
*/ @Override public Expression type() { return new ClassTypeExpression(this); } @Override public ClassDescriptor getDescriptor() { if (isAttribute()) { return null; } if (descriptor == null) { // Look first for query keys, then mappings. Ultimately we should have query keys // for everything and can dispense with the mapping part. ForeignReferenceQueryKey queryKey = (ForeignReferenceQueryKey)getQueryKeyOrNull(); if (queryKey != null) { descriptor = convertToCastDescriptor(getSession().getDescriptor(queryKey.getReferenceClass()), getSession()); return descriptor; } if (getMapping() == null) { throw QueryException.invalidQueryKeyInExpression(this); } // We assume this is either a foreign reference or an aggregate mapping descriptor = getMapping().getReferenceDescriptor(); if (getMapping().isVariableOneToOneMapping()) { throw QueryException.cannotQueryAcrossAVariableOneToOneMapping(getMapping(), descriptor); } descriptor = convertToCastDescriptor(descriptor, getSession()); } return descriptor; } /** * INTERNAL: Not to be confused with the public getField(String) * This returns a collection of all fields associated with this object. Really * only applies to query keys representing an object or to expression builders. */ @Override public Vector getFields() { if (getDescriptor() == null) { DatabaseMapping mapping = getMapping(); if (mapping != null) { return mapping.getSelectFields(); } return new NonSynchronizedVector(0); } if (descriptor.hasInheritance() && descriptor.getInheritancePolicy().shouldReadSubclasses() && (!descriptor.getInheritancePolicy().hasMultipleTableChild()) || shouldUseOuterJoinForMultitableInheritance()) { // return all fields because we can. return descriptor.getAllFields(); } else { return descriptor.getFields(); } } /** * INTERNAL: */ @Override public List getSelectionFields(ReadQuery query) { if (getDescriptor() == null) { DatabaseMapping mapping = getMapping(); if (mapping != null) { return mapping.getSelectFields(); } return new ArrayList<>(0); } if (descriptor.hasInheritance() && descriptor.getInheritancePolicy().shouldReadSubclasses() && (!descriptor.getInheritancePolicy().hasMultipleTableChild()) || shouldUseOuterJoinForMultitableInheritance()) { // return all fields because we can. if (query != null && query.isObjectLevelReadQuery()) { return descriptor.getAllSelectionFields((ObjectLevelReadQuery)query); } else { return descriptor.getAllSelectionFields(); } } else { if (query != null && query.isObjectLevelReadQuery()) { return descriptor.getSelectionFields((ObjectLevelReadQuery)query); } else { return descriptor.getSelectionFields(); } } } /** * INTERNAL: * Returns the first field from each of the owned tables, used for * fine-grained pessimistic locking. */ protected Vector getForUpdateOfFields() { Vector allFields = getFields(); int expected = getTableAliases().size(); Vector firstFields = new Vector(expected); DatabaseTable lastTable = null; DatabaseField field = null; int i = 0; // The following loop takes O(n*m) time. n=# of fields. m=#tables. // However, in the m=1 case this will take one pass only. // Also assuming that fields are generally sorted by table, this will // take O(n) time. // An even faster way may be to go getDescriptor().getAdditionalPrimaryKeyFields. while ((i < allFields.size()) && (firstFields.size() < expected)) { field = (DatabaseField)allFields.elementAt(i++); if ((lastTable == null) || !field.getTable().equals(lastTable)) { lastTable = field.getTable(); int j = 0; while (j < firstFields.size()) { if (lastTable.equals(((DatabaseField)firstFields.elementAt(j)).getTable())) { break; } j++; } if (j == firstFields.size()) { firstFields.addElement(field); } } } return firstFields; } public Expression getManualQueryKey(String attributeName, ClassDescriptor aDescriptor) { return derivedManualExpressionNamed(attributeName, aDescriptor); } /** * Return any tables in addition to the descriptor's tables, such as the mappings join table. */ public List getAdditionalTables() { return null; } /** * Return any tables that are defined by this expression (and not its base). */ @Override public List getOwnedTables() { ClassDescriptor descriptor = getDescriptor(); List tables = null; if (descriptor == null) { List additionalTables = getAdditionalTables(); if (additionalTables == null) { return null; } else { return new ArrayList(additionalTables); } } else if (descriptor.isAggregateDescriptor()) { return null; } else if ((descriptor.getHistoryPolicy() != null) && (getAsOfClause().getValue() != null)) { tables = descriptor.getHistoryPolicy().getHistoricalTables(); } else if (isUsingOuterJoinForMultitableInheritance()) { tables = descriptor.getInheritancePolicy().getAllTables(); } else { tables = descriptor.getTables(); } List additionalTables = getAdditionalTables(); if (additionalTables != null) { tables = new Vector(tables); Helper.addAllUniqueToList(tables, additionalTables); return tables; } return tables; } @Override public boolean hasBeenAliased() { return hasBeenAliased; } /** * INTERNAL: */ @Override public void clearAliases() { hasBeenAliased = false; super.clearAliases(); } protected boolean hasDerivedExpressions() { return derivedExpressions != null; } @Override public boolean isObjectExpression() { return true; } /** * INTERNAL: * indicates whether additional expressions for multitable inheritance should be used and are available */ public boolean isUsingOuterJoinForMultitableInheritance() { return shouldUseOuterJoinForMultitableInheritance() && getDescriptor() != null && getDescriptor().hasInheritance() && getDescriptor().getInheritancePolicy().hasMultipleTableChild() && getDescriptor().getInheritancePolicy().shouldReadSubclasses(); } public QueryKeyExpression newDerivedExpressionNamed(String attributeName) { QueryKeyExpression result = new QueryKeyExpression(attributeName, this); addDerivedExpression(result); return result; } public Expression newManualDerivedExpressionNamed(String attributeName, ClassDescriptor aDescriptor) { QueryKeyExpression result = new ManualQueryKeyExpression(attributeName, this, aDescriptor); addDerivedExpression(result); return result; } /** * INTERNAL: * Used for cloning. */ @Override protected void postCopyIn(Map alreadyDone) { super.postCopyIn(alreadyDone); this.derivedExpressions = copyDerivedExpressions(alreadyDone); if (this.onClause != null) { this.onClause = this.onClause.copiedVersionFrom(alreadyDone); } if (this.joinSource != null) { this.joinSource = this.joinSource.copiedVersionFrom(alreadyDone); } } /** * Return null by default, only QueryKeyExpression can have a relation table. */ public DatabaseTable getRelationTable() { return null; } /** * Return false by default, only possible for QueryKeyExpression. */ public boolean isDirectCollection() { return false; } /** * INTERNAL: * The method was added to circumvent derivedFields and derivedTables being * protected. * @see org.eclipse.persistence.expressions.ExpressionBuilder#registerIn(Map alreadyDone) */ public void postCopyIn(Map alreadyDone, List oldDerivedFields, List oldDerivedTables) { // bug 2637484 INVALID QUERY KEY EXCEPTION THROWN USING BATCH READS AND PARALLEL EXPRESSIONS if (oldDerivedFields != null) { if (this.derivedFields == null) { this.derivedFields = copyCollection(oldDerivedFields, alreadyDone); } else { this.derivedFields.addAll(copyCollection(oldDerivedFields, alreadyDone)); } } if (oldDerivedTables != null) { if (this.derivedTables == null) { this.derivedTables = copyCollection(oldDerivedTables, alreadyDone); } else { this.derivedTables.addAll(copyCollection(oldDerivedTables, alreadyDone)); } } } public Expression getOnClause() { return onClause; } public void setOnClause(Expression onClause) { this.onClause = onClause; } public void setCastClass(Class castClass) { this.castClass = castClass; } /** * INTERNAL: * set the flag indicating whether subclasses should be joined */ public void setShouldUseOuterJoinForMultitableInheritance(boolean shouldUseOuterJoinForMultitableInheritance) { this.shouldUseOuterJoinForMultitableInheritance = shouldUseOuterJoinForMultitableInheritance; } public boolean shouldUseOuterJoin() { return shouldUseOuterJoin; } public boolean shouldUseOuterJoinForMultitableInheritance() { return shouldUseOuterJoinForMultitableInheritance; } /** * INTERNAL: * writes the first field from each of the owned tables, used for * fine-grained pessimistic locking. */ protected void writeForUpdateOfFields(ExpressionSQLPrinter printer, SQLSelectStatement statement) { for (Iterator iterator = getForUpdateOfFields().iterator(); iterator.hasNext();) { DatabaseField field = (DatabaseField)iterator.next(); if (printer.getPlatform().shouldPrintAliasForUpdate()) { writeAlias(printer, field, statement); } else { writeField(printer, field, statement); } } } public Expression getJoinSource() { return joinSource; } public void setJoinSource(Expression joinSource) { this.joinSource = joinSource; } public Integer getOuterJoinExpIndex() { return outerJoinExpIndex; } public void setOuterJoinExpIndex(Integer outerJoinExpIndex) { this.outerJoinExpIndex = outerJoinExpIndex; } /** * INTERNAL: * Parses an expression to return the first non-AggregateObjectMapping expression after the base ExpressionBuilder. * This is used by joining and batch fetch to get the list of mappings that really need to be processed (non-aggregates). * @param aggregateMappingsEncountered - collection of aggregateObjectMapping expressions encountered in the returned expression * between the first expression and the ExpressionBuilder * @return first non-AggregateObjectMapping expression after the base ExpressionBuilder from the fullExpression */ public ObjectExpression getFirstNonAggregateExpressionAfterExpressionBuilder(List aggregateMappingsEncountered) { boolean done = false; ObjectExpression baseExpression = this; ObjectExpression prevExpression = this; while (!baseExpression.getBaseExpression().isExpressionBuilder() && !done) { baseExpression = (ObjectExpression)baseExpression.getBaseExpression(); while (!baseExpression.isExpressionBuilder() && baseExpression.getMapping().isAggregateObjectMapping()) { aggregateMappingsEncountered.add(baseExpression.getMapping()); baseExpression = (ObjectExpression)baseExpression.getBaseExpression(); } if (baseExpression.isExpressionBuilder()) { done = true; //use the one closest to the expression builder that wasn't an aggregate baseExpression = prevExpression; } else { prevExpression = baseExpression; } } return baseExpression; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy