oracle.toplink.essentials.queryframework.ReadAllQuery Maven / Gradle / Ivy
/*
* 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.queryframework;
import java.util.*;
import oracle.toplink.essentials.internal.helper.*;
import oracle.toplink.essentials.internal.queryframework.*;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.expressions.*;
import oracle.toplink.essentials.internal.sessions.AbstractRecord;
import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
import oracle.toplink.essentials.internal.sessions.AbstractSession;
/**set
* Purpose:
* Concrete class for all read queries involving a collection of objects.
*
*
Responsibilities:
* Return a container of the objects generated by the query.
* Implements the inheritance feature when dealing with abstract descriptors
*
* @author Yvon Lavoie
* @since TOPLink/Java 1.0
*/
public class ReadAllQuery extends ObjectLevelReadQuery {
/** Used for ordering support. */
protected Vector orderByExpressions;
/** Used for collection and stream support. */
protected ContainerPolicy containerPolicy;
/**
* PUBLIC:
* Return a new read all query.
* A reference class must be specified before execution.
* It is better to provide the class and expression builder on construction to esnure a single expression builder is used.
* If no selection criteria is specified this will read all objects of the class from the database.
*/
public ReadAllQuery() {
super();
this.useCollectionClass(ClassConstants.Vector_class);
}
/**
* PUBLIC:
* Return a new read all query.
* It is better to provide the class and expression builder on construction to esnure a single expression builder is used.
* If no selection criteria is specified this will read all objects of the class from the database.
*/
public ReadAllQuery(Class classToRead) {
this();
setReferenceClass(classToRead);
}
/**
* PUBLIC:
* Return a new read all query for the class and the selection criteria.
*/
public ReadAllQuery(Class classToRead, Expression selectionCriteria) {
this();
setReferenceClass(classToRead);
setSelectionCriteria(selectionCriteria);
}
/**
* PUBLIC:
* Return a new read all query for the class.
* The expression builder must be used for all associated expressions used with the query.
*/
public ReadAllQuery(Class classToRead, ExpressionBuilder builder) {
this();
this.defaultBuilder = builder;
setReferenceClass(classToRead);
}
/**
* PUBLIC:
* Return a new read all query.
* The call represents a database interaction such as SQL, Stored Procedure.
*/
public ReadAllQuery(Class classToRead, Call call) {
this();
setReferenceClass(classToRead);
setCall(call);
}
/**
* PUBLIC:
* The expression builder should be provide on creation to ensure only one is used.
*/
public ReadAllQuery(ExpressionBuilder builder) {
this();
this.defaultBuilder = builder;
}
/**
* PUBLIC:
* Create a read all query with the database call.
*/
public ReadAllQuery(Call call) {
this();
setCall(call);
}
/**
* PUBLIC:
* Order the query results by the object's attribute or query key name.
*/
public void addAscendingOrdering(String queryKeyName) {
addOrdering(getExpressionBuilder().get(queryKeyName).ascending());
}
/**
* PUBLIC:
* Order the query results by the object's attribute or query key name.
*/
public void addDescendingOrdering(String queryKeyName) {
addOrdering(getExpressionBuilder().get(queryKeyName).descending());
}
/**
* PUBLIC:
* Add the ordering expression. This allows for ordering across relationships or functions.
* Example: readAllQuery.addOrdering(expBuilder.get("address").get("city").toUpperCase().descending())
*/
public void addOrdering(Expression orderingExpression) {
getOrderByExpressions().addElement(orderingExpression);
//Bug2804042 Must un-prepare if prepared as the SQL may change.
setIsPrepared(false);
}
/**
* INTERNAL:
* The cache check is done before the prepare as a hit will not require the work to be done.
*/
protected Object checkEarlyReturnImpl(AbstractSession session, AbstractRecord translationRow) {
// Check for in-memory only query.
if (shouldCheckCacheOnly()) {
// assert !isReportQuery();
if (shouldUseWrapperPolicy()) {
getContainerPolicy().setElementDescriptor(getDescriptor());
}
// PERF: Fixed to not query each unit of work cache (is not conforming),
// avoid hashtable and primary key indexing.
// At some point we may need to support some kind of in-memory with conforming option,
// but we do not currently allow this.
AbstractSession rootSession = session;
while (rootSession.isUnitOfWork()) {
rootSession = ((UnitOfWorkImpl)rootSession).getParent();
}
Vector allCachedVector = rootSession.getIdentityMapAccessor().getAllFromIdentityMap(getSelectionCriteria(), getReferenceClass(), translationRow, getInMemoryQueryIndirectionPolicy(), false);
// Must ensure that all of the objects returned are correctly registered in the unit of work.
if (session.isUnitOfWork()) {
allCachedVector = ((UnitOfWorkImpl)session).registerAllObjects(allCachedVector);
}
return getContainerPolicy().buildContainerFromVector(allCachedVector, session);
} else {
return null;
}
}
/**
* INTERNAL:
* Check to see if a custom query should be used for this query.
* This is done before the query is copied and prepared/executed.
* null means there is none.
*/
protected DatabaseQuery checkForCustomQuery(AbstractSession session, AbstractRecord translationRow) {
checkDescriptor(session);
// check if user defined a custom query
if ((!isUserDefined()) && isExpressionQuery() && (getSelectionCriteria() == null) && (!hasOrderByExpressions()) && (getDescriptor().getQueryManager().hasReadAllQuery())) {
return getDescriptor().getQueryManager().getReadAllQuery();
} else {
return null;
}
}
/**
* INTERNAL:
* Clone the query.
*/
public Object clone() {
ReadAllQuery cloneQuery = (ReadAllQuery)super.clone();
// Don't use setters as that will trigger unprepare
if (hasOrderByExpressions()) {
cloneQuery.orderByExpressions = (Vector)getOrderByExpressions().clone();
}
cloneQuery.containerPolicy = getContainerPolicy().clone(cloneQuery);
return cloneQuery;
}
/**
* INTERNAL:
* Conform the result if specified.
*/
protected Object conformResult(Object result, UnitOfWorkImpl unitOfWork, AbstractRecord arguments, boolean buildDirectlyFromRows) {
ContainerPolicy cp;
IdentityHashtable indexedInterimResult = null;
Expression selectionCriteriaClone = null;
if (getSelectionCriteria() != null) {
selectionCriteriaClone = (Expression)getSelectionCriteria().clone();
selectionCriteriaClone.getBuilder().setSession(unitOfWork.getRootSession(null));
selectionCriteriaClone.getBuilder().setQueryClass(getReferenceClass());
}
cp = getContainerPolicy();
// This code is now a great deal different... For one, registration is done
// as part of conforming. Also, this should only be called if one actually
// is conforming.
// First scan the UnitOfWork for conforming instances.
// This will walk through the entire cache of registered objects.
// Let p be objects from result not in the cache.
// Let c be objects from cache.
// Presently p intersect c = empty set, but later p subset c.
// By checking cache now doesConform will be called p fewer times.
indexedInterimResult = unitOfWork.scanForConformingInstances(selectionCriteriaClone, getReferenceClass(), arguments, this);
// Now conform the result from the database.
// Remove any deleted or changed objects that no longer conform.
// Deletes will only work for simple queries, queries with or's or anyof's may not return
// correct results when untriggered indirection is in the model.
Vector fromDatabase = null;
// When building directly from rows, one of the performance benefits
// is that we no longer have to wrap and then unwrap the originals.
// result is just a vector, not a container of wrapped originals.
if (buildDirectlyFromRows) {
Vector rows = (Vector)result;
Set identitySet = null;
fromDatabase = new Vector(rows.size());
for (int i = 0; i < rows.size(); i++) {
Object object = rows.elementAt(i);
// null is placed in the row collection for 1-m joining to filter duplicate rows.
if (object != null) {
Object clone = conformIndividualResult(object, unitOfWork, arguments, selectionCriteriaClone, indexedInterimResult, buildDirectlyFromRows);
if (clone != null) {
// Avoid duplicates if -m joining was used and a cache hit occured.
if (getJoinedAttributeManager().isToManyJoin()) {
if (identitySet == null) {
identitySet = new TopLinkIdentityHashSet(rows.size());
}
if (!identitySet.contains(clone)) {
identitySet.add(clone);
fromDatabase.addElement(clone);
}
} else {
fromDatabase.addElement(clone);
}
}
}
}
} else {
fromDatabase = new Vector(cp.sizeFor(result));
AbstractSession sessionToUse = unitOfWork.getParent();
for (Object iter = cp.iteratorFor(result); cp.hasNext(iter);) {
Object object = cp.next(iter, sessionToUse);
Object clone = conformIndividualResult(object, unitOfWork, arguments, selectionCriteriaClone, indexedInterimResult, buildDirectlyFromRows);
if (clone != null) {
fromDatabase.addElement(clone);
}
}
}
// Now add the unwrapped conforming instances into an appropriate container.
// Wrapping is done automatically.
// Make sure a vector of exactly the right size is returned.
//
Object conformedResult = cp.containerInstance(indexedInterimResult.size() + fromDatabase.size());
Object eachClone;
for (Enumeration enumtr = indexedInterimResult.elements(); enumtr.hasMoreElements();) {
eachClone = enumtr.nextElement();
cp.addInto(eachClone, conformedResult, unitOfWork);
}
for (Enumeration enumtr = fromDatabase.elements(); enumtr.hasMoreElements();) {
eachClone = enumtr.nextElement();
cp.addInto(eachClone, conformedResult, unitOfWork);
}
return conformedResult;
}
/**
* INTERNAL:
* Execute the query.
* Get the rows and build the object from the rows.
* @exception DatabaseException - an error has occurred on the database
* @return java.lang.Object collection of objects resulting from execution of query.
*/
protected Object executeObjectLevelReadQuery() throws DatabaseException {
Object result = null;
if (getContainerPolicy().overridesRead()) {
return getContainerPolicy().execute();
}
Vector rows = getQueryMechanism().selectAllRows();
setExecutionTime(System.currentTimeMillis());
// If using -m joins, must set all rows.
if (getJoinedAttributeManager().isToManyJoin()) {
getJoinedAttributeManager().setDataResults(rows, getSession());
}
if (getSession().isUnitOfWork()) {
result = registerResultInUnitOfWork(rows, (UnitOfWorkImpl)getSession(), getTranslationRow(), true);//
} else {
result = getQueryMechanism().buildObjectsFromRows(rows);
}
if (shouldIncludeData()) {
ComplexQueryResult complexResult = new ComplexQueryResult();
complexResult.setResult(result);
complexResult.setData(rows);
return complexResult;
}
return result;
}
/**
* INTERNAL:
* Return the query's container policy.
* @return oracle.toplink.essentials.internal.queryframework.ContainerPolicy
*/
public ContainerPolicy getContainerPolicy() {
return containerPolicy;
}
/**
* INTERNAL:
* Return the order expressions for the query.
*/
public Vector getOrderByExpressions() {
if (orderByExpressions == null) {
orderByExpressions = new Vector();
}
return orderByExpressions;
}
/**
* INTERNAL:
* The order bys are lazy initialized to conserv space.
*/
public boolean hasOrderByExpressions() {
return orderByExpressions != null;
}
/**
* INTERNAL:
* Verify that we have hierarchical query expressions
*/
public boolean hasHierarchicalExpressions() {
return false;
}
/**
* INTERNAL:
* Return true is this query has batching
*/
public boolean hasBatchReadAttributes() {
return false;
}
/**
* INTERNAL:
* Return if the attribute is specified for batch reading.
*/
public boolean isAttributeBatchRead(String attributeName) {
return false;
}
/**
* PUBLIC:
* Return if this is a read all query.
*/
public boolean isReadAllQuery() {
return true;
}
/**
* INTERNAL:
* Prepare the receiver for execution in a session.
*/
protected void prepare() throws QueryException {
super.prepare();
getContainerPolicy().prepare(this, getSession());
if (getContainerPolicy().overridesRead()) {
return;
}
prepareSelectAllRows();
}
/**
* INTERNAL:
* Set the properties needed to be cascaded into the custom query.
*/
protected void prepareCustomQuery(DatabaseQuery customQuery) {
ReadAllQuery customReadQuery = (ReadAllQuery)customQuery;
customReadQuery.setContainerPolicy(getContainerPolicy());
customReadQuery.setCascadePolicy(getCascadePolicy());
customReadQuery.setShouldRefreshIdentityMapResult(shouldRefreshIdentityMapResult());
customReadQuery.setShouldMaintainCache(shouldMaintainCache());
customReadQuery.setShouldUseWrapperPolicy(shouldUseWrapperPolicy());
}
/**
* INTERNAL:
* Prepare the receiver for execution in a session.
*/
public void prepareForExecution() throws QueryException {
super.prepareForExecution();
getContainerPolicy().prepareForExecution();
}
/**
* INTERNAL:
* Prepare the mechanism.
*/
protected void prepareSelectAllRows() {
getQueryMechanism().prepareSelectAllRows();
}
/**
* INTERNAL:
* All objects queried via a UnitOfWork get registered here. If the query
* went to the database.
*
* Involves registering the query result individually and in totality, and
* hence refreshing / conforming is done here.
*
* @param result may be collection (read all) or an object (read one),
* or even a cursor. If in transaction the shared cache will
* be bypassed, meaning the result may not be originals from the parent
* but raw database rows.
* @param unitOfWork the unitOfWork the result is being registered in.
* @param arguments the original arguments/parameters passed to the query
* execution. Used by conforming
* @param buildDirectlyFromRows If in transaction must construct
* a registered result from raw database rows.
*
* @return the final (conformed, refreshed, wrapped) UnitOfWork query result
*/
public Object registerResultInUnitOfWork(Object result, UnitOfWorkImpl unitOfWork, AbstractRecord arguments, boolean buildDirectlyFromRows) {
// For bug 2612366: Conforming results in UOW extremely slow.
// Replacing results with registered versions in the UOW is a part of
// conforming and is now done while conforming to maximize performance.
if (shouldConformResultsInUnitOfWork() || getDescriptor().shouldAlwaysConformResultsInUnitOfWork()) {
return conformResult(result, unitOfWork, arguments, buildDirectlyFromRows);
}
// When building directly from rows, one of the performance benefits
// is that we no longer have to wrap and then unwrap the originals.
// result is just a vector, not a collection of wrapped originals.
// Also for cursors the initial connection is automatically registered.
if (buildDirectlyFromRows) {
Vector rows = (Vector)result;
Set identitySet = null;
ContainerPolicy cp = getContainerPolicy();
Object clones = cp.containerInstance(rows.size());
for (Enumeration enumtr = rows.elements(); enumtr.hasMoreElements();) {
Object row = enumtr.nextElement();
// null is placed in the row collection for 1-m joining to filter duplicate rows.
if (row != null) {
Object clone = registerIndividualResult(row, unitOfWork, buildDirectlyFromRows, null);
// Avoid duplicates if -m joining was used and a cache hit occured.
if (getJoinedAttributeManager().isToManyJoin()) {
if (identitySet == null) {
identitySet = new TopLinkIdentityHashSet(rows.size());
}
if (!identitySet.contains(clone)) {
identitySet.add(clone);
cp.addInto(clone, clones, unitOfWork);
}
} else {
cp.addInto(clone, clones, unitOfWork);
}
}
}
return clones;
}
ContainerPolicy cp;
cp = getContainerPolicy();
Object clones = cp.containerInstance(cp.sizeFor(result));
AbstractSession sessionToUse = unitOfWork.getParent();
for (Object iter = cp.iteratorFor(result); cp.hasNext(iter);) {
Object object = cp.next(iter, sessionToUse);
Object clone = registerIndividualResult(object, unitOfWork, buildDirectlyFromRows, null);
cp.addInto(clone, clones, unitOfWork);
}
return clones;
}
/**
* PUBLIC:
* Set the container policy. Used to support different containers
* (e.g. Collections, Maps).
*/
public void setContainerPolicy(ContainerPolicy containerPolicy) {
// CR#... a container policy is always required, default is vector,
// required for deployment XML.
if (containerPolicy == null) {
return;
}
this.containerPolicy = containerPolicy;
setIsPrepared(false);
}
/**
* INTERNAL:
* Set the order expressions for the query.
*/
public void setOrderByExpressions(Vector orderByExpressions) {
this.orderByExpressions = orderByExpressions;
}
/**
* PUBLIC:
* Configure the mapping to use an instance of the specified container class
* to hold the target objects.
*
jdk1.2.x: The container class must implement (directly or indirectly) the Collection interface.
*
jdk1.1.x: The container class must be a subclass of Vector.
*/
public void useCollectionClass(Class concreteClass) {
// Set container policy.
setContainerPolicy(ContainerPolicy.buildPolicyFor(concreteClass));
}
/**
* PUBLIC:
* Configure the query to use an instance of the specified container class
* to hold the result objects. The key used to index the value in the Map
* is the value returned by a call to the specified zero-argument method.
* The method must be implemented by the class (or a superclass) of the
* value to be inserted into the Map.
*
jdk1.2.x: The container class must implement (directly or indirectly) the Map interface.
*
jdk1.1.x: The container class must be a subclass of Hashtable.
*
The referenceClass must set before calling this method.
*/
public void useMapClass(Class concreteClass, String methodName) {
// the reference class has to be specified before coming here
if (getReferenceClass() == null) {
throw QueryException.referenceClassMissing(this);
}
ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass);
policy.setKeyName(methodName, getReferenceClass().getName());
setContainerPolicy(policy);
}
}