| /* |
| * 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.queries; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| 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.expressions.ExpressionBuilder; |
| import org.eclipse.persistence.internal.descriptors.ObjectBuilder; |
| import org.eclipse.persistence.internal.expressions.BaseExpression; |
| import org.eclipse.persistence.internal.expressions.ForUpdateOfClause; |
| import org.eclipse.persistence.internal.expressions.ObjectExpression; |
| import org.eclipse.persistence.internal.expressions.QueryKeyExpression; |
| import org.eclipse.persistence.internal.helper.NonSynchronizedSubVector; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| import org.eclipse.persistence.queries.Cursor; |
| import org.eclipse.persistence.queries.FetchGroup; |
| import org.eclipse.persistence.queries.ObjectBuildingQuery; |
| import org.eclipse.persistence.queries.ObjectLevelReadQuery; |
| import org.eclipse.persistence.sessions.DatabaseRecord; |
| |
| /** |
| * <p><b>Purpose</b>: |
| * A common class to be used by ObjectLevelReadQueries and ReportItems. This |
| * Class will be used to store Joined Attribute Expressions. It will also |
| * store the indexes for object construction. |
| * |
| * @author Gordon Yorke |
| * @since EJB3.0 RI |
| */ |
| |
| public class JoinedAttributeManager implements Cloneable, Serializable { |
| |
| /** Stores AggregateObjectMapping expressions used within local join expressions */ |
| protected transient List<DatabaseMapping> joinedAggregateMappings = new ArrayList(0); |
| |
| /** indexed list of mappings corresponding to */ |
| protected transient List<DatabaseMapping> joinedAttributeMappings = new ArrayList(0); |
| |
| /** Stores the joined attributes added through the query */ |
| protected List<Expression> joinedAttributeExpressions; |
| |
| /** Stores the joined attributes as specified in the descriptor */ |
| protected List<Expression> joinedMappingExpressions; |
| |
| /** PERF: Cache the local joined attribute expressions. */ |
| protected List<Expression> joinedAttributes; |
| |
| /** Used to determine if -m joining has been used. */ |
| protected boolean isToManyJoin = false; |
| |
| /** PERF: Used to avoid null checks for inner attribute joining. */ |
| protected boolean hasOuterJoinedAttribute = true; |
| |
| /** Used internally for joining. */ |
| protected transient Map<DatabaseMapping, Object> joinedMappingIndexes; |
| |
| /** Used internally for joining. */ |
| protected transient Map<DatabaseMapping, ObjectLevelReadQuery> joinedMappingQueries; |
| |
| /** PERF: Stores the cloned joinedMappingQueries. */ |
| protected transient Map<DatabaseMapping, ObjectLevelReadQuery> joinedMappingQueryClones; |
| |
| /** Stored all row results to -m joining. */ |
| protected transient List<AbstractRecord> dataResults; |
| |
| /** Stored all row results to -m joining by cache key. */ |
| protected transient Map<Object, List<AbstractRecord>> dataResultsByPrimaryKey; |
| |
| /** Stores the descriptor that these joins apply on */ |
| protected transient ClassDescriptor descriptor; |
| |
| /** Stores the base builder for resolving joined attributes by name. */ |
| protected ExpressionBuilder baseExpressionBuilder; |
| |
| /** Stores the last used base expression while adding joined attribute expression. */ |
| protected Expression lastJoinedAttributeBaseExpression; |
| |
| /** Stores the base query. */ |
| protected ObjectBuildingQuery baseQuery; |
| |
| /** Stores the result index of the parent, used for oneToMany joins. */ |
| protected int parentResultIndex; |
| |
| /** Determine if duplicate rows should be filter when using 1-m joining. */ |
| protected boolean shouldFilterDuplicates = true; |
| |
| //** Stores orderBy expressions of the joined CollectionMappings - in case the mapping has listFieldOrder */ |
| protected transient List<Expression> orderByExpressions; |
| |
| //** Stores additional field expressions of the joined CollectionMappings - in case the mapping has listFieldOrder */ |
| protected transient List<Expression> additionalFieldExpressions; |
| |
| public JoinedAttributeManager(){ |
| } |
| |
| public JoinedAttributeManager(ClassDescriptor descriptor, ExpressionBuilder baseBuilder, ObjectBuildingQuery baseQuery){ |
| this.descriptor = descriptor; |
| this.baseQuery = baseQuery; |
| this.baseExpressionBuilder = baseBuilder; |
| this.parentResultIndex = 0; |
| } |
| |
| /** |
| * Return if duplicate rows should be filter when using 1-m joining. |
| */ |
| public boolean shouldFilterDuplicates() { |
| return shouldFilterDuplicates; |
| } |
| |
| /** |
| * Set if duplicate rows should be filter when using 1-m joining. |
| */ |
| public void setShouldFilterDuplicates(boolean shouldFilterDuplicates) { |
| this.shouldFilterDuplicates = shouldFilterDuplicates; |
| } |
| |
| public void addJoinedAttribute(Expression attributeExpression) { |
| this.getJoinedAttributes().add(attributeExpression); |
| } |
| |
| public void addJoinedAttributeExpression(Expression attributeExpression) { |
| if(!getJoinedAttributeExpressions().contains(attributeExpression)) { |
| getJoinedAttributeExpressions().add(attributeExpression); |
| } |
| } |
| |
| /** |
| * Add an attribute represented by the given attribute name to the list of joins for this query. |
| * Note: Mapping level joins are represented separately from query level joins. |
| */ |
| public void addJoinedMappingExpression(Expression mappingExpression) { |
| getJoinedMappingExpressions().add(mappingExpression); |
| } |
| |
| /** |
| * Add an attribute represented by the given attribute name to the list of joins for this query. |
| * Note: Mapping level joins are represented separately from query level joins. |
| */ |
| public void addJoinedMapping(String attributeName) { |
| addJoinedMappingExpression(this.baseExpressionBuilder.get(attributeName)); |
| } |
| |
| /** |
| * Clones the Joined Attribute Manager. Generally called from Query.clone(). |
| */ |
| @Override |
| public JoinedAttributeManager clone(){ |
| JoinedAttributeManager joinManager = null; |
| try { |
| joinManager = (JoinedAttributeManager)super.clone(); |
| } catch (CloneNotSupportedException exception) { |
| throw new InternalError(exception.toString()); |
| } |
| if (this.joinedAttributeExpressions != null) { |
| joinManager.joinedAttributeExpressions = new ArrayList<>(this.joinedAttributeExpressions); |
| } |
| if (this.joinedMappingExpressions != null) { |
| joinManager.joinedMappingExpressions = new ArrayList<>(this.joinedMappingExpressions); |
| } |
| if (this.joinedAttributes != null) { |
| joinManager.joinedAttributes = new ArrayList<>(this.joinedAttributes); |
| } |
| if (this.joinedMappingIndexes != null) { |
| joinManager.joinedMappingIndexes = new HashMap<>(this.joinedMappingIndexes); |
| } |
| if (this.joinedMappingQueries != null) { |
| joinManager.joinedMappingQueries = new HashMap<>(this.joinedMappingQueries); |
| } |
| if (this.orderByExpressions != null) { |
| joinManager.orderByExpressions = new ArrayList<>(this.orderByExpressions); |
| } |
| if (this.additionalFieldExpressions != null) { |
| joinManager.additionalFieldExpressions = new ArrayList<>(this.additionalFieldExpressions); |
| } |
| if (this.joinedAttributeMappings != null) { |
| joinManager.joinedAttributeMappings = new ArrayList<>(this.joinedAttributeMappings); |
| } |
| if (this.joinedAggregateMappings !=null) { |
| joinManager.joinedAggregateMappings = new ArrayList<>(this.joinedAggregateMappings); |
| } |
| return joinManager; |
| } |
| |
| /** |
| * Copies settings from another manager. Should copy all the attributes that clone method clones. |
| */ |
| public void copyFrom(JoinedAttributeManager otherJoinManager){ |
| this.joinedAttributeExpressions = otherJoinManager.joinedAttributeExpressions; |
| this.joinedMappingExpressions = otherJoinManager.joinedMappingExpressions; |
| this.joinedAttributes = otherJoinManager.joinedAttributes; |
| this.joinedMappingIndexes = otherJoinManager.joinedMappingIndexes; |
| this.joinedMappingQueries = otherJoinManager.joinedMappingQueries; |
| this.orderByExpressions = otherJoinManager.orderByExpressions; |
| this.additionalFieldExpressions = otherJoinManager.additionalFieldExpressions; |
| this.joinedAttributeMappings = otherJoinManager.joinedAttributeMappings; |
| this.joinedAggregateMappings = otherJoinManager.joinedAggregateMappings; |
| } |
| |
| /** |
| * Clear the joining state. This is used to redefine a queries joins for nested joins. |
| */ |
| public void clear(){ |
| this.joinedAttributeExpressions = null; |
| this.joinedMappingExpressions = null; |
| this.joinedAttributes = null; |
| this.joinedMappingIndexes = null; |
| this.isToManyJoin = false; |
| this.hasOuterJoinedAttribute = false; |
| this.joinedMappingQueries = null; |
| this.joinedMappingQueryClones = null; |
| this.orderByExpressions = null; |
| this.additionalFieldExpressions = null; |
| this.joinedAttributeMappings = null; |
| this.joinedAggregateMappings = null; |
| } |
| |
| /** |
| * For joining the resulting rows include the field/values for many objects. |
| * As some of the objects may have the same field names, these row partitions need to be calculated. |
| * The indexes are stored in the query and used later when building the objects. |
| */ |
| public int computeJoiningMappingIndexes(boolean includeAllSubclassFields, AbstractSession session, int offset) { |
| if (!hasJoinedExpressions()) { |
| return offset; |
| } |
| setJoinedMappingIndexes_(new HashMap(getJoinedAttributeExpressions().size() + getJoinedMappingExpressions().size())); |
| int fieldIndex = 0; |
| if (getBaseQuery().hasPartialAttributeExpressions()) { |
| fieldIndex = getDescriptor().getPrimaryKeyFields().size(); // Query will select pks |
| //next check for any partial attributes that are not joined attributes |
| Iterator partialAttributes = ((ObjectLevelReadQuery)getBaseQuery()).getPartialAttributeExpressions().iterator(); |
| while(partialAttributes.hasNext()){ |
| Expression expression = (Expression)partialAttributes.next(); |
| if (expression.isQueryKeyExpression()){ |
| if (!getJoinedMappingExpressions().contains(expression) && ! getJoinedAttributeExpressions().contains(expression)){ |
| fieldIndex += ((QueryKeyExpression)expression).getFields().size(); |
| } |
| } |
| } |
| } else if (getBaseQuery().hasExecutionFetchGroup()) { |
| fieldIndex = ((ObjectLevelReadQuery)getBaseQuery()).getFetchGroupNonNestedFieldsSet().size(); |
| } else { |
| if (includeAllSubclassFields) { |
| fieldIndex = getDescriptor().getAllSelectionFields((ObjectLevelReadQuery)getBaseQuery()).size(); |
| } else { |
| fieldIndex = getDescriptor().getSelectionFields((ObjectLevelReadQuery)getBaseQuery()).size(); |
| } |
| } |
| fieldIndex += offset; |
| fieldIndex = computeIndexesForJoinedExpressions(getJoinedAttributeExpressions(), fieldIndex, session); |
| fieldIndex = computeIndexesForJoinedExpressions(getJoinedMappingExpressions(), fieldIndex, session); |
| return fieldIndex; |
| } |
| |
| /** |
| * This method is used when computing the nested queries for joined mappings. |
| * It recurses computing the nested mapping queries and their join indexes. |
| */ |
| protected void computeNestedQueriesForJoinedExpressions(List joinedExpressions, AbstractSession session, ObjectLevelReadQuery readQuery) { |
| for (int index = 0; index < joinedExpressions.size(); index++) { |
| ObjectExpression objectExpression = (ObjectExpression)joinedExpressions.get(index); |
| |
| // Expression may not have been initialized. |
| objectExpression.getBuilder().setSession(session.getRootSession(null)); |
| if (objectExpression.getBuilder().getQueryClass() == null){ |
| objectExpression.getBuilder().setQueryClass(descriptor.getJavaClass()); |
| } |
| //get the first expression after the builder that is not an aggregate, and populate the aggregateMapping list if there are aggregates |
| ObjectExpression baseExpression = objectExpression.getFirstNonAggregateExpressionAfterExpressionBuilder(getJoinedAggregateMappings()); |
| |
| // PERF: Cache local join attribute Expression. |
| this.addJoinedAttribute(baseExpression); |
| |
| DatabaseMapping mapping = baseExpression.getMapping(); |
| this.getJoinedAttributeMappings().add(mapping); |
| |
| // focus on the base expression. Nested queries will handle nested expressions, and only need to be processed once |
| if (mapping.isForeignReferenceMapping() && !getJoinedMappingQueries_().containsKey(mapping)) { |
| // A nested query must be built to pass to the descriptor that looks like the real query execution would. |
| ObjectLevelReadQuery nestedQuery = ((ForeignReferenceMapping)mapping).prepareNestedJoins(this, readQuery, session); |
| if (nestedQuery != null) { |
| // Register the nested query to be used by the mapping for all the objects. |
| getJoinedMappingQueries_().put(mapping, nestedQuery); |
| } |
| if (mapping.isCollectionMapping()){ |
| mapping.getContainerPolicy().addNestedJoinsQueriesForMapKey(this, readQuery, session); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Used to optimize joining by pre-computing the nested join queries for the mappings. |
| */ |
| public void computeJoiningMappingQueries(AbstractSession session) { |
| if (hasJoinedExpressions()) { |
| this.joinedAttributeMappings = new ArrayList<>(getJoinedAttributeExpressions().size() + getJoinedMappingExpressions().size()); |
| this.joinedAttributes = new ArrayList<>(getJoinedAttributeExpressions().size() + getJoinedMappingExpressions().size()); |
| setJoinedMappingQueries_(new HashMap(getJoinedAttributeExpressions().size() + getJoinedMappingExpressions().size())); |
| computeNestedQueriesForJoinedExpressions(getJoinedAttributeExpressions(), session, (ObjectLevelReadQuery)this.baseQuery); |
| computeNestedQueriesForJoinedExpressions(getJoinedMappingExpressions(), session, (ObjectLevelReadQuery)this.baseQuery); |
| } |
| } |
| |
| /** |
| * This method is used when computing the indexes for joined mappings. It iterates through a list of join |
| * expressions and adds an index that represents where the fields represented by that expression will appear |
| * in the row returned by a read query. |
| * Method {@link #computeNestedQueriesForJoinedExpressions(List, AbstractSession, ObjectLevelReadQuery)} |
| * must be already called. |
| * @param joinedExpressions Join expressions {@link List}. |
| * @param currentIndex Current joined mapping index. |
| * @param session Current session. |
| * @return Current joined mapping index updated. |
| */ |
| protected int computeIndexesForJoinedExpressions(final List joinedExpressions, int currentIndex, |
| final AbstractSession session) { |
| for (int index = 0; index < joinedExpressions.size(); index++) { |
| final ObjectExpression objectExpression = (ObjectExpression)joinedExpressions.get(index); |
| final DatabaseMapping mapping = objectExpression.getMapping(); |
| // Only store the index if this is the local expression to avoid it being added multiple times. |
| // This means the base local expression must be first on the list, followed by nested expressions. |
| final ObjectExpression localExpression = objectExpression |
| .getFirstNonAggregateExpressionAfterExpressionBuilder(new ArrayList(1)); |
| if ((localExpression == objectExpression) && (mapping != null) && mapping.isForeignReferenceMapping()) { |
| getJoinedMappingIndexes_().put(mapping, currentIndex); |
| } |
| final ClassDescriptor descriptor = mapping.getReferenceDescriptor(); |
| int numberOfFields = 0; |
| if (descriptor == null) { |
| // Direct-collection mappings do not have descriptor. |
| if (mapping.isDirectCollectionMapping()) { |
| numberOfFields = 1; |
| } |
| } else { |
| final ObjectLevelReadQuery nestedQuery = getNestedJoinedMappingQuery(objectExpression); |
| FetchGroup fetchGroup = null; |
| if(descriptor.hasFetchGroupManager()) { |
| fetchGroup = nestedQuery.getExecutionFetchGroup(); |
| } |
| if(fetchGroup != null) { |
| numberOfFields = nestedQuery.getFetchGroupNonNestedFieldsSet(mapping).size(); |
| } else { |
| if (objectExpression.isQueryKeyExpression() |
| && objectExpression.isUsingOuterJoinForMultitableInheritance()) { |
| numberOfFields = descriptor.getAllSelectionFields(nestedQuery).size(); |
| } else if(objectExpression.isQueryKeyExpression() && objectExpression.getDescriptor() != null |
| && objectExpression.getDescriptor().hasInheritance() |
| && objectExpression.getDescriptor().getInheritancePolicy().shouldReadSubclasses()) { |
| numberOfFields = descriptor.getAllFields().size(); |
| } else { |
| numberOfFields = descriptor.getSelectionFields(nestedQuery).size(); |
| } |
| } |
| } |
| if (mapping.isCollectionMapping()){ |
| // map keys are indexed within the collection's row. |
| // Therefore we use an offset from within the collections row |
| numberOfFields += mapping.getContainerPolicy() |
| .updateJoinedMappingIndexesForMapKey(getJoinedMappingIndexes_(), numberOfFields); |
| } |
| currentIndex = currentIndex + numberOfFields; |
| } |
| return currentIndex; |
| } |
| |
| /** |
| * Get the list of additional field expressions. |
| */ |
| public List<Expression> getAdditionalFieldExpressions() { |
| if (this.additionalFieldExpressions == null){ |
| this.additionalFieldExpressions = new ArrayList<>(); |
| } |
| return additionalFieldExpressions; |
| } |
| |
| /** |
| * Get the list of additional field expressions. |
| */ |
| public List<Expression> getAdditionalFieldExpressions_() { |
| return additionalFieldExpressions; |
| } |
| |
| /** |
| * Returns the base expression builder for this query. |
| */ |
| public ExpressionBuilder getBaseExpressionBuilder(){ |
| return this.baseExpressionBuilder; |
| } |
| |
| /** |
| * Returns the base query. |
| */ |
| public ObjectBuildingQuery getBaseQuery(){ |
| return this.baseQuery; |
| } |
| |
| /** |
| * Return all of the rows fetched by the query, used for 1-m joining. |
| */ |
| public List<AbstractRecord> getDataResults_() { |
| return dataResults; |
| } |
| |
| public ClassDescriptor getDescriptor(){ |
| if (this.descriptor == null){ |
| this.descriptor = this.baseQuery.getDescriptor(); |
| } |
| return this.descriptor; |
| } |
| |
| /** |
| * Return if there are additional field expressions. |
| */ |
| public boolean hasAdditionalFieldExpressions() { |
| return (this.additionalFieldExpressions != null) && (!this.additionalFieldExpressions.isEmpty()); |
| } |
| |
| /** |
| * Set the list of additional field expressions. |
| */ |
| public void setAdditionalFieldExpressions_(List<Expression> expressions) { |
| this.additionalFieldExpressions = expressions; |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public List<DatabaseMapping> getJoinedAggregateMappings() { |
| if (this.joinedAggregateMappings == null){ |
| this.joinedAggregateMappings = new ArrayList<>(); |
| } |
| return joinedAggregateMappings; |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public List<Expression> getJoinedAttributeExpressions() { |
| if (this.joinedAttributeExpressions == null){ |
| this.joinedAttributeExpressions = new ArrayList<>(); |
| } |
| return joinedAttributeExpressions; |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public List<DatabaseMapping> getJoinedAttributeMappings() { |
| if (this.joinedAttributeMappings == null){ |
| this.joinedAttributeMappings = new ArrayList<>(); |
| } |
| return this.joinedAttributeMappings; |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public List<Expression> getJoinedAttributes() { |
| if (this.joinedAttributes == null){ |
| this.joinedAttributes = new ArrayList<>(); |
| } |
| return this.joinedAttributes; |
| } |
| |
| /** |
| * Get the list of expressions that represent elements that are joined because of their |
| * mapping for this query. |
| */ |
| public List<Expression> getJoinedMappingExpressions() { |
| if (this.joinedMappingExpressions == null){ |
| this.joinedMappingExpressions = new ArrayList<>(); |
| } |
| return joinedMappingExpressions; |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public boolean hasJoinedAttributeExpressions() { |
| return (this.joinedAttributeExpressions != null) && (!this.joinedAttributeExpressions.isEmpty()); |
| } |
| |
| /** |
| * This method checks both attribute expressions and mapping expressions and |
| * determines if there are any joins to be made. |
| */ |
| public boolean hasJoinedExpressions() { |
| return hasJoinedAttributeExpressions() || hasJoinedMappingExpressions(); |
| } |
| |
| /** |
| * Return the attributes that must be joined. |
| */ |
| public boolean hasJoinedMappingExpressions() { |
| return (this.joinedMappingExpressions != null) && (!this.joinedMappingExpressions.isEmpty()); |
| } |
| |
| /** |
| * Return if any attributes are joined. This is a convience method that |
| * is only valid after prepare. |
| */ |
| public boolean hasJoinedAttributes() { |
| return (this.joinedAttributes != null) && (!this.joinedAttributes.isEmpty()); |
| } |
| |
| /** |
| * PERF: Return if the query uses any outer attribute joins, used to avoid null checks in building objects. |
| */ |
| public boolean hasOuterJoinedAttributeQuery() { |
| return this.hasOuterJoinedAttribute; |
| } |
| |
| /** |
| * Get the list of orderBy expressions. |
| */ |
| public List<Expression> getOrderByExpressions() { |
| if (this.orderByExpressions == null){ |
| this.orderByExpressions = new ArrayList<>(); |
| } |
| return orderByExpressions; |
| } |
| |
| /** |
| * Get the list of orderBy expressions. |
| */ |
| public List<Expression> getOrderByExpressions_() { |
| return orderByExpressions; |
| } |
| |
| /** |
| * INTERNAL: |
| * Helper method to get the value from the clone for the expression passed in, triggering joins on |
| * all intermediate steps. |
| * Example expression "emp.project.pk" with a clone Employee will trigger indirection and return |
| * the project pk value. |
| * @param session |
| * @param clone |
| * @param expression |
| * @return |
| */ |
| public Object getValueFromObjectForExpression(AbstractSession session, Object clone, ObjectExpression expression){ |
| if (!expression.isExpressionBuilder()){ |
| //can only operate over querykeys representing aggregate Objects. Indirection should not be needed |
| Object baseValue = this.getValueFromObjectForExpression(session, clone, (ObjectExpression)expression.getBaseExpression()); |
| if ( baseValue == null ) { |
| return null; |
| } |
| DatabaseMapping mapping = expression.getMapping(); |
| Object attributeValue = mapping.getRealAttributeValueFromObject(baseValue, session); |
| if (attributeValue != null) { |
| if (mapping.isForeignReferenceMapping() && (((ForeignReferenceMapping)mapping).getIndirectionPolicy().usesTransparentIndirection())) { |
| //getRealAttributeValueFromObject does not trigger transparent indirection, but instantiateObject will (it calls size on it) |
| ((ForeignReferenceMapping)mapping).getIndirectionPolicy().instantiateObject(baseValue, attributeValue); |
| } |
| } |
| return attributeValue; |
| |
| } |
| return clone; |
| } |
| |
| /** |
| * Return if there are orderBy expressions. |
| */ |
| public boolean hasOrderByExpressions() { |
| return (this.orderByExpressions != null) && (!this.orderByExpressions.isEmpty()); |
| } |
| |
| /** |
| * Set the list of orderBy expressions. |
| */ |
| public void setOrderByExpressions_(List<Expression> expressions) { |
| this.orderByExpressions = expressions; |
| } |
| |
| /** |
| * Return if the query uses any -m joins, and thus return duplicate/multiple rows. |
| */ |
| public boolean isToManyJoin() { |
| return this.isToManyJoin; |
| } |
| |
| /** |
| * Return if the attribute is specified for joining. |
| */ |
| public boolean isAttributeJoined(ClassDescriptor mappingDescriptor, DatabaseMapping attributeMapping) { |
| // Since aggregates share the same query as their parent, must avoid the aggregate thinking |
| // the parents mappings is for it, (queries only share if the aggregate was not joined). |
| //This isn't taking into account inheritance - a query on a child may use/join parent level mappings |
| if (this.hasJoinedAttributes()) { |
| //if it has joined attributes, the other collections must also be set and so don't need to be checked |
| if (attributeMapping.isAggregateMapping()){ |
| return this.getJoinedAggregateMappings().contains(attributeMapping); |
| } else { |
| return this.getJoinedAttributeMappings().contains(attributeMapping); |
| }} |
| return isAttributeExpressionJoined(attributeMapping) || isAttributeMappingJoined(attributeMapping); |
| } |
| |
| /** |
| * Iterate through a list of expressions searching for the given attribute name. |
| * Return true if it is found, false otherwise. Only use if the query was preprepared so that join expressions |
| * were processed. |
| */ |
| protected boolean isMappingInJoinedExpressionList(DatabaseMapping attributeMapping, List joinedExpressionList) { |
| for (Iterator joinEnum = joinedExpressionList.iterator(); joinEnum.hasNext();) { |
| List aggregateMappings = new ArrayList(); |
| ObjectExpression expression = ((ObjectExpression)joinEnum.next()).getFirstNonAggregateExpressionAfterExpressionBuilder(aggregateMappings); |
| if (attributeMapping.isAggregateObjectMapping() && aggregateMappings.contains(attributeMapping)) { |
| return true; |
| } else if (attributeMapping.equals(expression.getMapping())) {//expression may not have been processed yet |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| /** |
| * Iterate through a list of expressions searching for the given attribute name. |
| * Return true if it is found, false otherwise. |
| */ |
| protected boolean isAttributeNameInJoinedExpressionList(String attributeName, List joinedExpressionList) { |
| for (Iterator joinEnum = joinedExpressionList.iterator(); joinEnum.hasNext();) { |
| QueryKeyExpression expression = (QueryKeyExpression)joinEnum.next(); |
| while (!expression.getBaseExpression().isExpressionBuilder()) { |
| expression = (QueryKeyExpression)expression.getBaseExpression(); |
| } |
| if (expression.getName().equals(attributeName)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Return if the attribute is specified for joining. |
| */ |
| protected boolean isAttributeExpressionJoined(DatabaseMapping attributeMapping) { |
| return isMappingInJoinedExpressionList(attributeMapping, getJoinedAttributeExpressions()); |
| } |
| |
| /** |
| * Return whether the given attribute is joined as a result of a join on a mapping |
| */ |
| protected boolean isAttributeMappingJoined(DatabaseMapping attributeMapping) { |
| return isAttributeNameInJoinedExpressionList(attributeMapping.getAttributeName(), getJoinedMappingExpressions()); |
| } |
| |
| /** |
| * Set the list of expressions that represent elements that are joined because of their |
| * mapping for this query. |
| */ |
| public void setJoinedAttributeExpressions_(List joinedExpressions) { |
| this.joinedAttributeExpressions = joinedExpressions; |
| } |
| |
| /** |
| * Set the list of expressions that represent elements that are joined because of their |
| * mapping for this query. |
| */ |
| public void setJoinedMappingExpressions_(List joinedMappingExpressions) { |
| this.joinedMappingExpressions = joinedMappingExpressions; |
| } |
| |
| /** |
| * Return the joined mapping indexes, used to compute mapping row partitions. |
| */ |
| public Map<DatabaseMapping, Object> getJoinedMappingIndexes_() { |
| return joinedMappingIndexes; |
| } |
| |
| /** |
| * Return the joined mapping queries, used optimize joining, only compute the nested queries once. |
| */ |
| public Map<DatabaseMapping, ObjectLevelReadQuery> getJoinedMappingQueries_() { |
| return joinedMappingQueries; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the nested query corresponding to the expression. |
| * The passed expression should be either join mapping or joined attribute expression. |
| */ |
| public ObjectLevelReadQuery getNestedJoinedMappingQuery(Expression expression) { |
| // the first element of the list is the passed expression, |
| // next one is its base, ... |
| // the last one's base is ExpressionBuilder. |
| ObjectExpression currentExpression = (ObjectExpression)expression; |
| ArrayList<Expression> expressionBaseList = new ArrayList(); |
| do { |
| //skip aggregates since they do not have nested query objects added to JoinedMappingQueries, instead |
| //reference mappings on aggregates are added to the parent's joinAttributeManager |
| if (!currentExpression.getMapping().isAggregateObjectMapping()){ |
| expressionBaseList.add(currentExpression); |
| } |
| currentExpression = (ObjectExpression)currentExpression.getBaseExpression(); |
| } while(!currentExpression.isExpressionBuilder()); |
| |
| // the last expression in the list is not nested - its mapping should have corresponding nestedQuery. |
| DatabaseMapping currentMapping = ((QueryKeyExpression)expressionBaseList.get(expressionBaseList.size() - 1)).getMapping(); |
| ObjectLevelReadQuery nestedQuery = getJoinedMappingQueries_().get(currentMapping); |
| |
| // unless the passed expression was not nested, repeat moving up the list. |
| // the last step is the passed expression (first on the list) getting nested query corresponding to its mapping. |
| for(int i = expressionBaseList.size() - 2; i >= 0; i--) { |
| currentMapping = ((QueryKeyExpression)expressionBaseList.get(i)).getMapping(); |
| nestedQuery = nestedQuery.getJoinedAttributeManager().getJoinedMappingQueries_().get(currentMapping); |
| } |
| return nestedQuery; |
| } |
| |
| /** |
| * Set the joined mapping queries, used optimize joining, only compute the nested queries once. |
| */ |
| public void setJoinedMappingQueries_(Map joinedMappingQueries) { |
| this.joinedMappingQueries = joinedMappingQueries; |
| } |
| |
| /** |
| * Set the joined mapping indexes, used to compute mapping row partitions. |
| */ |
| public void setJoinedMappingIndexes_(Map joinedMappingIndexes) { |
| this.joinedMappingIndexes = joinedMappingIndexes; |
| } |
| |
| /** |
| * PERF: Set if the query uses any outer attribute joins, used to avoid null checks in building objects. |
| */ |
| protected void setIsOuterJoinedAttributeQuery(boolean isOuterJoinedAttribute) { |
| this.hasOuterJoinedAttribute = isOuterJoinedAttribute; |
| } |
| |
| /** |
| * Set if the query uses any -m joins, and thus return duplicate/multiple rows. |
| */ |
| public void setIsToManyJoinQuery(boolean isToManyJoin) { |
| this.isToManyJoin = isToManyJoin; |
| } |
| |
| |
| /** |
| * Validate and prepare join expressions. |
| */ |
| public void prepareJoinExpressions(AbstractSession session) { |
| // The prepareJoinExpression check for outer-joins to set this to true. |
| setIsOuterJoinedAttributeQuery(false); |
| Expression lastJoinedAttributeBaseExpression = null; |
| List groupedExpressionList = new ArrayList(getJoinedAttributeExpressions().size()); |
| for (int index = 0; index < getJoinedAttributeExpressions().size(); index++) { |
| Expression expression = getJoinedAttributeExpressions().get(index); |
| expression = prepareJoinExpression(expression, session); |
| //EL bug 307497: break base expressions out onto the list and sort/group expressions by base expression |
| lastJoinedAttributeBaseExpression = addExpressionAndBaseToGroupedList(expression, groupedExpressionList, lastJoinedAttributeBaseExpression); |
| } |
| //use the grouped list instead of the original |
| this.setJoinedAttributeExpressions_(groupedExpressionList); |
| for (int index = 0; index < getJoinedMappingExpressions().size(); index++) { |
| Expression expression = getJoinedMappingExpressions().get(index); |
| expression = prepareJoinExpression(expression, session); |
| getJoinedMappingExpressions().set(index, expression); |
| } |
| } |
| |
| /** |
| * adds expression and its base expressions recursively to the expressionList in groups, so that an expression is never listed before |
| * its base expression |
| * @param expression |
| * @param expressionlist |
| * @param lastJoinedAttributeBaseExpression |
| * @return |
| */ |
| protected Expression addExpressionAndBaseToGroupedList(Expression expression, List expressionlist, Expression lastJoinedAttributeBaseExpression){ |
| if(!expressionlist.contains(expression)) { |
| int baseExpressionIndex = -1; |
| boolean sameBase = false;//better than using instanceof BaseExpression. If its not an objectExpression, it will get an exception in prepare anyway |
| if((expression.isObjectExpression())) { |
| Expression baseExpression = ((BaseExpression)expression).getBaseExpression(); |
| //filter out aggregate expressions between this and the next node. |
| while (!baseExpression.isExpressionBuilder() && ((QueryKeyExpression)baseExpression).getMapping().isAggregateMapping()){ |
| baseExpression = ((BaseExpression)baseExpression).getBaseExpression(); |
| } |
| |
| if(baseExpression != null && !baseExpression.isExpressionBuilder()) { |
| addExpressionAndBaseToGroupedList(baseExpression, expressionlist, lastJoinedAttributeBaseExpression); |
| // EL bug 307497 |
| if (baseExpression != lastJoinedAttributeBaseExpression) { |
| baseExpressionIndex = getJoinedAttributeExpressions().indexOf(baseExpression); |
| } else { |
| sameBase = true; |
| } |
| } |
| } |
| |
| // EL bug 307497 |
| if (baseExpressionIndex == -1) { |
| expressionlist.add(expression); |
| if (!sameBase) { |
| lastJoinedAttributeBaseExpression = expression; |
| } |
| } else { |
| //Add attributeExpression at baseExpressionIndex + 1. |
| expressionlist.add(baseExpressionIndex+1, expression); |
| } |
| } |
| return lastJoinedAttributeBaseExpression; |
| } |
| |
| /** |
| * Validate and prepare the join expression. |
| */ |
| protected Expression prepareJoinExpression(Expression expression, AbstractSession session) { |
| // Must be query key expression. |
| if (!expression.isQueryKeyExpression()) { |
| throw QueryException.mappingForExpressionDoesNotSupportJoining(expression); |
| } |
| QueryKeyExpression objectExpression = (QueryKeyExpression)expression; |
| |
| // Expression may not have been initialized. |
| if (objectExpression.getBuilder().getQueryClass() == null) { |
| objectExpression = (QueryKeyExpression)objectExpression.rebuildOn(this.baseExpressionBuilder); |
| |
| if (objectExpression.getBuilder().getQueryClass() == null) { |
| objectExpression.getBuilder().setQueryClass(this.descriptor.getJavaClass()); |
| } |
| } |
| objectExpression.getBuilder().setSession(session.getRootSession(null)); |
| |
| // Can only join relationships. |
| if ((objectExpression.getMapping() == null) || (!objectExpression.getMapping().isJoiningSupported())) { |
| throw QueryException.mappingForExpressionDoesNotSupportJoining(objectExpression); |
| } |
| |
| // Search if any of the expression traverse a 1-m. |
| ObjectExpression baseExpression = objectExpression; |
| while (!baseExpression.isExpressionBuilder()) { |
| //pulled from prepareJoinExpressions |
| baseExpression.setShouldUseOuterJoinForMultitableInheritance(true); |
| |
| if (((QueryKeyExpression)baseExpression).shouldQueryToManyRelationship()) { |
| setIsToManyJoinQuery(true); |
| } |
| if (baseExpression.shouldUseOuterJoin()) { |
| setIsOuterJoinedAttributeQuery(true); |
| } |
| baseExpression = (ObjectExpression)baseExpression.getBaseExpression(); |
| } |
| |
| return objectExpression; |
| } |
| |
| /** |
| * This method collects the Joined Mappings from the descriptor and initializes them. |
| * Excludes the mapping that are not in the passed mappingsAllowedToJoin set (if it's not null). |
| */ |
| public void processJoinedMappings(AbstractSession session) { |
| Set<String> fetchGroupAttributes = null; |
| FetchGroup fetchGroup = getBaseQuery().getExecutionFetchGroup(); |
| if(fetchGroup != null) { |
| fetchGroupAttributes = fetchGroup.getAttributeNames(); |
| } |
| ObjectBuilder objectBuilder = getDescriptor().getObjectBuilder(); |
| if (objectBuilder.hasJoinedAttributes()) { |
| List mappingJoinedAttributes = objectBuilder.getJoinedAttributes(); |
| if (!hasJoinedAttributeExpressions()) { |
| for (int i = 0; i < mappingJoinedAttributes.size(); i++) { |
| ForeignReferenceMapping mapping = (ForeignReferenceMapping) mappingJoinedAttributes.get(i); |
| if(fetchGroupAttributes == null || fetchGroupAttributes.contains(mapping.getAttributeName())) { |
| addAndPrepareJoinedMapping(mapping, session); |
| } |
| } |
| } else { |
| for (int i = 0; i < mappingJoinedAttributes.size(); i++) { |
| ForeignReferenceMapping mapping = (ForeignReferenceMapping) mappingJoinedAttributes.get(i); |
| if (!isAttributeExpressionJoined(mapping)) { |
| if(fetchGroupAttributes == null || fetchGroupAttributes.contains(mapping.getAttributeName())) { |
| addAndPrepareJoinedMapping(mapping, session); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Add the mapping for join fetch, prepare and return the join expression being used. |
| */ |
| public Expression addAndPrepareJoinedMapping(ForeignReferenceMapping mapping, AbstractSession session) { |
| Expression joinMappingExpression = null; |
| if (mapping.isCollectionMapping()) { |
| if (mapping.isInnerJoinFetched()) { |
| joinMappingExpression = getBaseExpressionBuilder().anyOf(mapping.getAttributeName(), false); |
| } else if (mapping.isOuterJoinFetched()) { |
| joinMappingExpression = getBaseExpressionBuilder().anyOfAllowingNone(mapping.getAttributeName(), false); |
| } |
| } else { |
| if (mapping.isInnerJoinFetched()) { |
| joinMappingExpression = getBaseExpressionBuilder().get(mapping.getAttributeName()); |
| } else if (mapping.isOuterJoinFetched()) { |
| joinMappingExpression = getBaseExpressionBuilder().getAllowingNull(mapping.getAttributeName()); |
| } |
| } |
| if (joinMappingExpression != null) { |
| joinMappingExpression = prepareJoinExpression(joinMappingExpression, session); |
| addJoinedMappingExpression(joinMappingExpression); |
| } |
| return joinMappingExpression; |
| } |
| |
| /** |
| * Reset the JoinedAttributeManager. This will be called when the Query is re-prepared |
| */ |
| public void reset(){ |
| this.joinedMappingExpressions = null; |
| this.joinedAttributes = null; |
| this.isToManyJoin = false; |
| this.hasOuterJoinedAttribute = true; |
| this.joinedMappingIndexes = null; |
| this.joinedMappingQueries = null; |
| this.dataResults = null; |
| this.joinedAttributeMappings = null; |
| this.joinedAggregateMappings = null; |
| } |
| |
| /** |
| * This method is called from within this package it is used when |
| * initializing a report Item |
| */ |
| public void setBaseQuery(ObjectLevelReadQuery query){ |
| this.baseQuery = query; |
| } |
| |
| /** |
| * This method is called from within this package, it is used when |
| * initializing a ReportItem |
| */ |
| protected void setBaseExpressionBuilder(ExpressionBuilder builder){ |
| this.baseExpressionBuilder = builder; |
| } |
| |
| /** |
| * Return all of the rows fetched by the query by cache-key, used for 1-m joining. |
| */ |
| public Map<Object, List<AbstractRecord>> getDataResultsByPrimaryKey() { |
| return dataResultsByPrimaryKey; |
| } |
| |
| /** |
| * Set all of the rows fetched by the query by cache-key, used for 1-m joining. |
| */ |
| protected void setDataResultsByPrimaryKey(Map<Object, List<AbstractRecord>> dataResultsByPrimaryKey) { |
| this.dataResultsByPrimaryKey = dataResultsByPrimaryKey; |
| } |
| |
| /** |
| * Set all of the rows fetched by the query, used for 1-m joining. |
| */ |
| public void setDataResults(List dataResults, AbstractSession session) { |
| this.dataResults = dataResults; |
| processDataResults(session); |
| } |
| |
| /** |
| * Process the data-results for joined data for a 1-m join. |
| * This allows all the data to be processed once, instead of n times for each object. |
| */ |
| protected void processDataResults(AbstractSession session) { |
| this.dataResultsByPrimaryKey = new HashMap(); |
| int size = this.dataResults.size(); |
| Object firstKey = null; |
| Object lastKey = null; |
| List<AbstractRecord> childRows = null; |
| ObjectBuilder builder = getDescriptor().getObjectBuilder(); |
| int parentIndex = getParentResultIndex(); |
| Vector trimedFields = null; |
| for (int dataResultsIndex = 0; dataResultsIndex < size; dataResultsIndex++) { |
| AbstractRecord row = this.dataResults.get(dataResultsIndex); |
| AbstractRecord parentRow = row; |
| // Must adjust for the parent index to ensure the correct pk is extracted. |
| if (parentIndex > 0) { |
| if (trimedFields == null) { // The fields are always the same, so only build once. |
| trimedFields = new NonSynchronizedSubVector(row.getFields(), parentIndex, row.size()); |
| } |
| Vector trimedValues = new NonSynchronizedSubVector(row.getValues(), parentIndex, row.size()); |
| parentRow = new DatabaseRecord(trimedFields, trimedValues); |
| } |
| // Extract the primary key of the source object, to filter only the joined rows for that object. |
| Object sourceKey = builder.extractPrimaryKeyFromRow(parentRow, session); |
| // May be any outer-join so ignore null. |
| if (sourceKey != null) { |
| if (firstKey == null) { |
| firstKey = sourceKey; |
| } |
| if ((lastKey != null) && lastKey.equals(sourceKey)) { |
| childRows.add(row); |
| if (shouldFilterDuplicates()) { |
| // Also null out the row because it is a duplicate to avoid object building processing it. |
| this.dataResults.set(dataResultsIndex, null); |
| } |
| } else { |
| childRows = this.dataResultsByPrimaryKey.get(sourceKey); |
| if (childRows == null) { |
| childRows = new ArrayList(); |
| this.dataResultsByPrimaryKey.put(sourceKey, childRows); |
| } else { |
| if (shouldFilterDuplicates()) { |
| // Also null out the row because it is a duplicate to avoid object building processing it. |
| this.dataResults.set(dataResultsIndex, null); |
| } |
| } |
| childRows.add(row); |
| lastKey = sourceKey; |
| } |
| } |
| } |
| // If pagination is used, the first and last rows may be missing their 1-m joined rows, so reject them from the results. |
| // This will cause them to build normally by executing a query. |
| if (this.isToManyJoin) { |
| if ((lastKey != null) && (this.baseQuery.getMaxRows() > 0)) { |
| this.dataResultsByPrimaryKey.remove(lastKey); |
| } |
| if ((firstKey != null) && (this.baseQuery.getFirstResult() > 0)) { |
| this.dataResultsByPrimaryKey.remove(firstKey); |
| } |
| } |
| } |
| |
| /** |
| * Clear the data-results for joined data for a 1-m join. |
| */ |
| public void clearDataResults() { |
| this.dataResults = null; |
| this.dataResultsByPrimaryKey = null; |
| } |
| |
| /** |
| * Process the data-results for joined data for a 1-m join. |
| * This allows incremental processing for a cursor. |
| */ |
| public AbstractRecord processDataResults(AbstractRecord row, Cursor cursor, boolean forward) { |
| if (this.dataResultsByPrimaryKey == null) { |
| this.dataResultsByPrimaryKey = new HashMap(); |
| } |
| AbstractRecord parentRow = row; |
| List<AbstractRecord> childRows = new ArrayList<>(); |
| childRows.add(row); |
| int parentIndex = getParentResultIndex(); |
| // Must adjust for the parent index to ensure the correct pk is extracted. |
| Vector trimedFields = new NonSynchronizedSubVector(row.getFields(), parentIndex, row.size()); |
| if (parentIndex > 0) { |
| Vector trimedValues = new NonSynchronizedSubVector(row.getValues(), parentIndex, row.size()); |
| parentRow = new DatabaseRecord(trimedFields, trimedValues); |
| } |
| ObjectBuilder builder = getDescriptor().getObjectBuilder(); |
| AbstractSession session = cursor.getExecutionSession(); |
| // Extract the primary key of the source object, to filter only the joined rows for that object. |
| Object sourceKey = builder.extractPrimaryKeyFromRow(parentRow, session); |
| AbstractRecord extraRow = null; |
| while (true) { |
| AbstractRecord nextRow = null; |
| if (forward) { |
| nextRow = cursor.getAccessor().cursorRetrieveNextRow(cursor.getFields(), cursor.getResultSet(), session); |
| } else { |
| nextRow = cursor.getAccessor().cursorRetrievePreviousRow(cursor.getFields(), cursor.getResultSet(), session); |
| } |
| if (nextRow == null) { |
| break; |
| } |
| AbstractRecord nextParentRow = nextRow; |
| if (parentIndex > 0) { |
| Vector trimedValues = new NonSynchronizedSubVector(nextParentRow.getValues(), parentIndex, nextParentRow.size()); |
| nextParentRow = new DatabaseRecord(trimedFields, trimedValues); |
| } |
| // Extract the primary key of the source object, to filter only the joined rows for that object. |
| Object nextKey = builder.extractPrimaryKeyFromRow(nextParentRow, session); |
| if ((sourceKey != null) && sourceKey.equals(nextKey)) { |
| childRows.add(nextRow); |
| } else { |
| extraRow = nextRow; |
| break; |
| } |
| } |
| this.dataResultsByPrimaryKey.put(sourceKey, childRows); |
| return extraRow; |
| } |
| |
| /** |
| * Called to set the descriptor on a Join Managerwith in a ReportItem, durring |
| * initialization, and durring DatabaseQuery.checkDescriptor. |
| */ |
| public void setDescriptor(ClassDescriptor descriptor){ |
| this.descriptor = descriptor; |
| } |
| |
| /** |
| * Used for joining in conjunction with pessimistic locking. |
| * Iterate through a list of joined expressions and ensure expression is set on the locking |
| * clause for each expression that represents a pessimisically locked descriptor. |
| */ |
| public ForUpdateOfClause setupLockingClauseForJoinedExpressions(ForUpdateOfClause lockingClause, AbstractSession session) { |
| if (hasJoinedAttributeExpressions()){ |
| return setupLockingClauseForJoinedExpressions(getJoinedAttributeExpressions(), session,lockingClause); |
| } |
| if (hasJoinedMappingExpressions()){ |
| return setupLockingClauseForJoinedExpressions(getJoinedMappingExpressions(), session,lockingClause); |
| } |
| return lockingClause; |
| } |
| |
| /** |
| * Used for joining in conjunction with pessimistic locking. |
| * Iterate through a list of joined expressions and ensure expression is set on the locking |
| * clause for each expression that represents a pessimisically locked descriptor. |
| */ |
| private ForUpdateOfClause setupLockingClauseForJoinedExpressions(List joinedExpressions, AbstractSession session, ForUpdateOfClause lockingClause) { |
| // Must iterate over all of the joined attributes, just check |
| // if any of them have pessimistic locking defined on the descriptor. |
| for (Iterator e = joinedExpressions.iterator(); e.hasNext();) { |
| Expression expression = (Expression)e.next(); |
| |
| // Expression has not yet been validated. |
| if (expression.isObjectExpression()) { |
| ObjectExpression joinedAttribute = (ObjectExpression)expression; |
| |
| // Expression may not have been initialized. |
| joinedAttribute.getBuilder().setSession(session.getRootSession(null)); |
| if (joinedAttribute.getBuilder().getQueryClass() == null){ |
| joinedAttribute.getBuilder().setQueryClass(descriptor.getJavaClass()); |
| } |
| |
| ClassDescriptor nestedDescriptor = joinedAttribute.getDescriptor(); |
| |
| // expression may not be valid, no descriptor, validation occurs later. |
| if (nestedDescriptor == null) { |
| return lockingClause; |
| } |
| if (nestedDescriptor.hasPessimisticLockingPolicy()) { |
| if (lockingClause == null) { |
| lockingClause = new ForUpdateOfClause(); |
| lockingClause.setLockMode(nestedDescriptor.getCMPPolicy().getPessimisticLockingPolicy().getLockingMode()); |
| } |
| lockingClause.addLockedExpression(joinedAttribute); |
| } |
| } |
| } |
| return lockingClause; |
| } |
| |
| public void setParentResultIndex(int parentsResultIndex) { |
| this.parentResultIndex = parentsResultIndex; |
| } |
| |
| public int getParentResultIndex() { |
| return parentResultIndex; |
| } |
| |
| public Map<DatabaseMapping, ObjectLevelReadQuery> getJoinedMappingQueryClones() { |
| return joinedMappingQueryClones; |
| } |
| |
| public void setJoinedMappingQueryClones(Map joinedMappingQueryClones) { |
| this.joinedMappingQueryClones = joinedMappingQueryClones; |
| } |
| } |