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

oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.internal.queryframework;

import java.util.ArrayList;
import java.util.List;
import oracle.toplink.essentials.expressions.Expression;
import java.util.Map;
import java.util.HashMap;
import java.util.Vector;

import oracle.toplink.essentials.expressions.ExpressionBuilder;
import oracle.toplink.essentials.internal.expressions.QueryKeyExpression;
import oracle.toplink.essentials.internal.expressions.ObjectExpression;
import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.internal.sessions.AbstractSession;
import oracle.toplink.essentials.mappings.ForeignReferenceMapping;
import oracle.toplink.essentials.internal.sessions.AbstractRecord;
import java.util.Iterator;
import oracle.toplink.essentials.internal.descriptors.ObjectBuilder;
import oracle.toplink.essentials.internal.expressions.ForUpdateOfClause;
import oracle.toplink.essentials.exceptions.QueryException;
import oracle.toplink.essentials.queryframework.ObjectBuildingQuery;
import oracle.toplink.essentials.queryframework.ObjectLevelReadQuery;

/**
 * 

Purpose: * 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{ /** Stores the joined attributes added through the query */ protected ArrayList joinedAttributeExpressions_; /** Stores the joined attributes as specified in the descriptor */ protected ArrayList joinedMappingExpressions_; /** PERF: Cache the local joined attribute names. */ protected ArrayList 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 HashMap joinedMappingIndexes_; /** Used internally for joining. */ protected transient HashMap joinedMappingQueries_; /** Stored all row results to -m joining. */ protected List dataResults; /** Stores the descriptor that these joins apply on */ protected ClassDescriptor descriptor; /** Stores the base builder for resolving joined attributes by name */ protected ExpressionBuilder baseExpressionBuilder; /** Stores the baseQuery */ protected ObjectBuildingQuery baseQuery; /** Stores the result index of the parent, used for oneToMany joins */ protected int parentResultIndex; public JoinedAttributeManager(){ } public JoinedAttributeManager(ClassDescriptor descriptor, ExpressionBuilder baseBuilder, ObjectBuildingQuery baseQuery){ this.descriptor = descriptor; this.baseQuery = baseQuery; this.baseExpressionBuilder = baseBuilder; this.parentResultIndex = 0; } /** * INTERNAL: */ public void addJoinedAttribute(String attributeExpression) { this.getJoinedAttributes().add(attributeExpression); } /** * INTERNAL: */ public void addJoinedAttributeExpression(Expression attributeExpression) { getJoinedAttributeExpressions().add(attributeExpression); } /** * INTERNAL: * 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); } /** * INTERNAL: * 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)); } /** * INTERNAL: * Clones the Joined Attribute Manager. Generally called from Query.clone() */ public Object clone(){ JoinedAttributeManager joinManager = new JoinedAttributeManager(); joinManager.baseExpressionBuilder = this.baseExpressionBuilder; joinManager.baseQuery = this.baseQuery; joinManager.descriptor = this.descriptor; if (this.joinedAttributeExpressions_ != null){ joinManager.joinedAttributeExpressions_ = (ArrayList)this.joinedAttributeExpressions_.clone(); } if (this.joinedMappingExpressions_ != null){ joinManager.joinedMappingExpressions_ = (ArrayList)this.joinedMappingExpressions_.clone(); } if (this.joinedAttributes_ != null){ joinManager.joinedAttributes_ = (ArrayList)this.joinedAttributes_.clone(); } if (this.joinedMappingIndexes_ != null){ joinManager.joinedMappingIndexes_ = (HashMap)this.joinedMappingIndexes_.clone(); } if (this.joinedMappingQueries_ != null){ joinManager.joinedMappingQueries_ = (HashMap)this.joinedMappingQueries_.clone(); } joinManager.isToManyJoin = this.isToManyJoin; joinManager.hasOuterJoinedAttribute = this.hasOuterJoinedAttribute; return joinManager; } /** * INTERNAL: * 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 (includeAllSubclassFields) { fieldIndex = getDescriptor().getAllFields().size(); } else { fieldIndex = getDescriptor().getFields().size(); } fieldIndex += offset; fieldIndex = computeIndexesForJoinedExpressions(getJoinedAttributeExpressions(), fieldIndex, session); fieldIndex = computeIndexesForJoinedExpressions(getJoinedMappingExpressions(), fieldIndex, session); return fieldIndex; } /** * INTERNAL: * 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()); } // PERF: Cache join attribute names. ObjectExpression baseExpression = objectExpression; while (!baseExpression.getBaseExpression().isExpressionBuilder()) { baseExpression = (ObjectExpression)((QueryKeyExpression)baseExpression).getBaseExpression(); } this.addJoinedAttribute(baseExpression.getName()); // Ignore nested if ((objectExpression.getBaseExpression() == objectExpression.getBuilder()) && objectExpression.getMapping().isForeignReferenceMapping()) { ForeignReferenceMapping mapping = (ForeignReferenceMapping)objectExpression.getMapping(); // A nested query must be built to pass to the descriptor that looks like the real query execution would. ObjectLevelReadQuery nestedQuery = mapping.prepareNestedJoins(this, session); // Register the nested query to be used by the mapping for all the objects. getJoinedMappingQueries_().put(mapping, nestedQuery); } } } /** * INTERNAL: * Used to optimize joining by pre-computing the nested join queries for the mappings. */ public void computeJoiningMappingQueries(AbstractSession session) { if (hasJoinedExpressions()) { 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); } } /** * INTERNAL: * 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. * * @see #computeJoiningMappingIndexes(boolean, AbstractSession) */ protected int computeIndexesForJoinedExpressions(List joinedExpressions, int currentIndex, AbstractSession session) { for (int index = 0; index < joinedExpressions.size(); index++) { ObjectExpression objectExpression = (ObjectExpression)joinedExpressions.get(index); // Ignore nested if ((objectExpression.getBaseExpression() == objectExpression.getBuilder()) && objectExpression.getMapping() != null && objectExpression.getMapping().isForeignReferenceMapping()) { getJoinedMappingIndexes_().put(objectExpression.getMapping(), new Integer(currentIndex)); } ClassDescriptor descriptor = objectExpression.getMapping().getReferenceDescriptor(); int nFields; if(objectExpression.isQueryKeyExpression() && ((QueryKeyExpression)objectExpression).isUsingOuterJoinForMultitableInheritance()) { nFields = descriptor.getAllFields().size(); } else { nFields = descriptor.getFields().size(); } currentIndex = currentIndex + nFields; } return currentIndex; } /** * INTERNAL: * Returns the base expression builder for this query. */ public ExpressionBuilder getBaseExpressionBuilder(){ return this.baseExpressionBuilder; } /** * INTERNAL: * Returns the base query. */ public ObjectBuildingQuery getBaseQuery(){ return this.baseQuery; } /** * INTERNAL: * Return all of the rows fetched by the query, used for 1-m joining. */ public List getDataResults_() { return dataResults; } /** * INTERNAL: */ public ClassDescriptor getDescriptor(){ return this.descriptor; } /** * INTERNAL: * Return the attributes that must be joined. */ public List getJoinedAttributes() { if (this.joinedAttributes_ == null){ this.joinedAttributes_ = new ArrayList(); } return this.joinedAttributes_; } /** * INTERNAL: * Return the attributes that must be joined. */ public List getJoinedAttributeExpressions() { if (this.joinedAttributeExpressions_ == null){ this.joinedAttributeExpressions_ = new ArrayList(); } return joinedAttributeExpressions_; } /** * INTERNAL: * Get the list of expressions that represent elements that are joined because of their * mapping for this query. */ public List getJoinedMappingExpressions() { if (this.joinedMappingExpressions_ == null){ this.joinedMappingExpressions_ = new ArrayList(); } return joinedMappingExpressions_; } /** * INTERNAL: * Return the attributes that must be joined. */ public boolean hasJoinedAttributeExpressions() { return this.joinedAttributeExpressions_ != null && !this.joinedAttributeExpressions_.isEmpty(); } /** * INTERNAL: * THis methos checks bot attribute expressions and mapping expressions and * determines if there are any joins to be made */ public boolean hasJoinedExpressions() { return hasJoinedAttributeExpressions() || hasJoinedMappingExpressions(); } /** * INTERNAL: * Return the attributes that must be joined. */ public boolean hasJoinedMappingExpressions() { return this.joinedMappingExpressions_ != null && !this.joinedMappingExpressions_.isEmpty(); } /** * INTERNAL: * 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(); } /** * INTERNAL: * PERF: Return if the query uses any outer attribute joins, used to avoid null checks in building objects. */ public boolean hasOuterJoinedAttributeQuery() { return this.hasOuterJoinedAttribute; } /** * INTERNAL: * Return if the query uses any -m joins, and thus return duplicate/multiple rows. */ public boolean isToManyJoin() { return this.isToManyJoin; } /** * INTERNAL: * Return if the attribute is specified for joining. */ public boolean isAttributeJoined(ClassDescriptor mappingDescriptor, String attributeName) { // 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). if (mappingDescriptor.isAggregateDescriptor() && (mappingDescriptor != getDescriptor())) { return false; } if (this.hasJoinedAttributes()) { return this.joinedAttributes_.contains(attributeName); } return isAttributeExpressionJoined(attributeName) || isAttributeMappingJoined(attributeName); } /** * 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; } /** * INTERNAL: * Return if the attribute is specified for joining. */ protected boolean isAttributeExpressionJoined(String attributeName) { return isAttributeNameInJoinedExpressionList(attributeName, getJoinedAttributeExpressions()); } /** * INTERNAL: * Return whether the given attribute is joined as a result of a join on a mapping */ protected boolean isAttributeMappingJoined(String attributeName) { return isAttributeNameInJoinedExpressionList(attributeName, getJoinedMappingExpressions()); } /** * INTERNAL: * 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_ = new ArrayList(joinedExpressions); } /** * INTERNAL: * 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_ = new ArrayList(joinedMappingExpressions); } /** * INTERNAL: * Return the joined mapping indexes, used to compute mapping row partitions. */ public Map getJoinedMappingIndexes_() { return joinedMappingIndexes_; } /** * INTERNAL: * Return the joined mapping queries, used optimize joining, only compute the nested queries once. */ public Map getJoinedMappingQueries_() { return joinedMappingQueries_; } /** * INTERNAL: * Set the joined mapping queries, used optimize joining, only compute the nested queries once. */ protected void setJoinedMappingQueries_(HashMap joinedMappingQueries) { this.joinedMappingQueries_ = joinedMappingQueries; } /** * INTERNAL: * Set the joined mapping indexes, used to compute mapping row partitions. */ public void setJoinedMappingIndexes_(HashMap joinedMappingIndexes) { this.joinedMappingIndexes_ = joinedMappingIndexes; } /** * INTERNAL: * Set the attributes that must be joined. */ /* public void setJoinedAttributeExpressions(List joinedAttributeExpressions) { this.joinedAttributeExpressions = joinedAttributeExpressions; setIsPrePrepared(false); } */ /** * INTERNAL: * 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; } /** * INTERNAL: * Set if the query uses any -m joins, and thus return duplicate/multiple rows. */ protected void setIsToManyJoinQuery(boolean isToManyJoin) { this.isToManyJoin = isToManyJoin; } /** * INTERNAL: * Validate and prepare join expressions. */ public void prepareJoinExpressions(AbstractSession session) { // The prepareJoinExpression check for outer-joins to set this to true. setIsOuterJoinedAttributeQuery(false); for (int index = 0; index < getJoinedAttributeExpressions().size(); index++) { Expression expression = (Expression)getJoinedAttributeExpressions().get(index); if(expression.isObjectExpression()) { ((ObjectExpression)expression).setShouldUseOuterJoinForMultitableInheritance(true); } prepareJoinExpression(expression, session); } for (int index = 0; index < getJoinedMappingExpressions().size(); index++) { Expression expression = (Expression)getJoinedMappingExpressions().get(index); if(expression.isObjectExpression()) { ((ObjectExpression)expression).setShouldUseOuterJoinForMultitableInheritance(true); } prepareJoinExpression(expression, session); } computeJoiningMappingQueries(session); } /** * Validate and prepare the join expression. */ protected void 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. objectExpression.getBuilder().setSession(session.getRootSession(null)); if (objectExpression.getBuilder().getQueryClass() == null){ objectExpression.getBuilder().setQueryClass(descriptor.getJavaClass()); } // 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()) { if (((QueryKeyExpression)baseExpression).shouldQueryToManyRelationship()) { setIsToManyJoinQuery(true); } if (((QueryKeyExpression)baseExpression).shouldUseOuterJoin()) { setIsOuterJoinedAttributeQuery(true); } baseExpression = (ObjectExpression)((QueryKeyExpression)baseExpression).getBaseExpression(); } } /** * INTERNAL: * This method collects the Joined Mappings from the descriptor and initializes * them */ public void processJoinedMappings(){ ObjectBuilder objectBuilder = getDescriptor().getObjectBuilder(); if (objectBuilder.hasJoinedAttributes()) { Vector mappingJoinedAttributes = objectBuilder.getJoinedAttributes(); if (!hasJoinedExpressions()) { for (int i = 0; i < mappingJoinedAttributes.size(); i++) { addJoinedMapping((String)mappingJoinedAttributes.get(i)); } } else { for (int i = 0; i < mappingJoinedAttributes.size(); i++) { String attribute = (String)mappingJoinedAttributes.get(i); if (!isAttributeExpressionJoined(attribute)) { addJoinedMapping(attribute); } } } } } /** * INTERNAL: * 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; } /** * INTERNAL: * This method is called from within this package it is used when * initializing a report Item */ public void setBaseQuery(ObjectLevelReadQuery query){ this.baseQuery = query; } /** * INTERNAL: * This method is called from within this package, it is used when * initializing a ReportItem */ protected void setBaseExpressionBuilder(ExpressionBuilder builder){ this.baseExpressionBuilder = builder; } /** * INTERNAL: * Set all of the rows fetched by the query, used for 1-m joining. */ public void setDataResults(List dataResults, AbstractSession session) { this.dataResults = dataResults; if(getJoinedMappingQueries_() != null && !getJoinedMappingQueries_().isEmpty() && dataResults != null && !dataResults.isEmpty()) { Iterator it = getJoinedMappingQueries_().entrySet().iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); ObjectLevelReadQuery nestedQuery = (ObjectLevelReadQuery)entry.getValue(); if(nestedQuery.getJoinedAttributeManager().isToManyJoin()) { ForeignReferenceMapping frMapping = (ForeignReferenceMapping)entry.getKey(); Object indexObject = getJoinedMappingIndexes_().get(entry.getKey()); List nestedDataResults = new ArrayList(dataResults.size()); for(int i=0; i < dataResults.size(); i++) { AbstractRecord row = (AbstractRecord)dataResults.get(i); nestedDataResults.add(frMapping.trimRowForJoin(row, indexObject, session)); } nestedQuery.getJoinedAttributeManager().setDataResults(nestedDataResults, session); } } } } /** * INTERNAL: * 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; } /** * INTERNAL: * 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()){ setupLockingClauseForJoinedExpressions(getJoinedAttributeExpressions(), session); } if (hasJoinedMappingExpressions()){ setupLockingClauseForJoinedExpressions(getJoinedMappingExpressions(), session); } return lockingClause; } /** * INTERNAL: * 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 void setupLockingClauseForJoinedExpressions(List joinedExpressions, AbstractSession session) { // 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 = null;// joinedAttribute.getDescriptor(); // expression may not be valid, no descriptor, validation occurs later. if (nestedDescriptor == null) { return; } } } return; } public void setParentResultIndex(int parentsResultIndex) { this.parentResultIndex = parentsResultIndex; } public int getParentResultIndex() { return parentResultIndex; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy