| /* |
| * 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<Expression> 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. |
| * <p>Example: |
| * <pre> |
| * 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" |
| * </pre> |
| */ |
| @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<DatabaseTable> 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. |
| * <p>Example: |
| * <pre> |
| * 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') |
| * </pre> |
| * @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. |
| * <p>Example: |
| * <pre> |
| * 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') |
| * </pre> |
| * @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<Expression> copyDerivedExpressions(Map alreadyDone) { |
| if (this.derivedExpressions == null) { |
| return null; |
| } |
| List<Expression> derivedExpressionsCopy; |
| synchronized(this) { |
| derivedExpressionsCopy = new ArrayList(this.derivedExpressions); |
| } |
| List<Expression> 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<Expression> 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. |
| * <p>Example: |
| * <pre> |
| * builder.getClassForInheritance().equal(SmallProject.class); |
| * </pre> |
| */ |
| @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<DatabaseField> 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<DatabaseTable> getAdditionalTables() { |
| return null; |
| } |
| |
| /** |
| * Return any tables that are defined by this expression (and not its base). |
| */ |
| @Override |
| public List<DatabaseTable> getOwnedTables() { |
| ClassDescriptor descriptor = getDescriptor(); |
| List<DatabaseTable> tables = null; |
| if (descriptor == null) { |
| List<DatabaseTable> 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<DatabaseTable> 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<Expression> oldDerivedFields, List<Expression> 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; |
| } |
| |
| } |