/**********************************************************************
Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
...
**********************************************************************/
package org.datanucleus.store.query;
import java.util.Iterator;
import java.util.Map;
import org.datanucleus.ObjectManager;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.query.JPQLSingleStringParser;
import org.datanucleus.query.QueryUtils;
import org.datanucleus.query.compiler.JPQLCompiler;
import org.datanucleus.query.compiler.JavaQueryCompiler;
import org.datanucleus.query.compiler.QueryCompilation;
import org.datanucleus.util.NucleusLogger;
/**
* Abstract representation of a JPQL query used by DataNucleus.
* The query can be specified via method calls, or via a single-string form.
* @see Query
*/
public abstract class AbstractJPQLQuery extends AbstractJavaQuery
{
/**
* Constructor.
* @param om ObjectManager
*/
public AbstractJPQLQuery(ObjectManager om)
{
super(om);
}
/**
* Constructs a new query instance having the same criteria as the given query.
* @param om The ObjectManager
* @param q The query from which to copy criteria.
*/
public AbstractJPQLQuery(ObjectManager om, AbstractJPQLQuery q)
{
super(om);
if (q == null)
{
candidateClass = null;
candidateClassName = null;
filter = null;
imports = null;
explicitVariables = null;
explicitParameters = null;
grouping = null;
ordering = null;
result = null;
resultClass = null;
resultClassName = null;
resultDistinct = false;
range = null;
fromInclNo = 0;
toExclNo = Long.MAX_VALUE;
}
else
{
candidateClass = q.candidateClass;
candidateClassName = q.candidateClassName;
filter = q.filter;
imports = q.imports;
explicitVariables = q.explicitVariables;
explicitParameters = q.explicitParameters;
grouping = q.grouping;
ordering = q.ordering;
result = q.result;
resultClass = q.resultClass;
resultClassName = q.resultClassName;
resultDistinct = q.resultDistinct;
range = q.range;
fromInclNo = q.fromInclNo;
toExclNo = q.toExclNo;
// JDO 1.0.1, Sect 12.6.3 : IgnoreCache is preserved when using other query baseline
ignoreCache = q.ignoreCache;
}
}
/**
* Constructor for a JPQL query where the query is specified using the "Single-String" format.
* @param om The ObjectManager
* @param query The query string
*/
public AbstractJPQLQuery(ObjectManager om, String query)
{
super(om);
new JPQLSingleStringParser(this, query).parse();
}
/**
* Set the result for the results.
* @param result Optional keyword "distinct" followed by comma-separated list of
* result expressions or a result class
*/
public void setResult(String result)
{
discardCompiled();
assertIsModifiable();
if (result == null)
{
this.result = null;
this.resultDistinct = false;
return;
}
String str = result.trim();
if (str.toUpperCase().startsWith("DISTINCT "))
{
this.resultDistinct = true;
this.result = str.substring(8).trim();
}
else
{
this.resultDistinct = false;
this.result = str;
}
}
/**
* Method to take the defined parameters for the query and form a single string.
* This is used to print out the query for logging.
* @return The single string
*/
public String getSingleStringQuery()
{
if (singleString != null)
{
return singleString;
}
StringBuffer str = new StringBuffer();
if (type == BULK_UPDATE)
{
str.append("UPDATE " + update + " ");
}
else if (type == BULK_DELETE)
{
str.append("DELETE ");
}
else
{
str.append("SELECT ");
}
if (result != null)
{
if (resultDistinct)
{
str.append("DISTINCT ");
}
str.append(result + " ");
}
if (from != null)
{
str.append("FROM " + from + " ");
}
if (filter != null)
{
str.append("WHERE " + filter + " ");
}
if (grouping != null)
{
str.append("GROUP BY " + grouping + " ");
}
if (having != null)
{
str.append("HAVING " + having + " ");
}
if (ordering != null)
{
str.append("ORDER BY " + ordering + " ");
}
singleString = str.toString().trim();
return singleString;
}
/**
* Method to compile the JPQL query.
* This implementation assumes that we are using the "generic" JPQL compiler in
* <i>org.datanucleus.query.compiler</i>. If not then override this method.
* Will populate the "compilation" class variable.
* @param forExecute Whether compiling for execution NOT USED HERE. TODO Remove this when possible
* @param parameterValues Map of param values keyed by param name.
*/
protected void compileInternal(boolean forExecute, Map parameterValues)
{
if (compilation != null)
{
return;
}
QueryManager queryMgr = om.getOMFContext().getQueryManager();
boolean caching = getBooleanExtensionProperty("datanucleus.query.cached", true);
if (caching)
{
QueryCompilation cachedCompilation = queryMgr.getQueryCompilationForQuery("JPQL", toString());
if (cachedCompilation != null)
{
if (NucleusLogger.QUERY.isDebugEnabled())
{
NucleusLogger.QUERY.debug(LOCALISER.msg("021079", toString(), "JPQL"));
}
compilation = cachedCompilation;
if (compilation.getExprResult() == null)
{
// If the result was "Object(e)" or "e" then this is meaningless so remove
result = null;
}
checkParameterTypesAgainstCompilation(parameterValues);
return;
}
}
long startTime = 0;
if (NucleusLogger.QUERY.isDebugEnabled())
{
startTime = System.currentTimeMillis();
NucleusLogger.QUERY.debug(LOCALISER.msg("021044", "JPQL", getSingleStringQuery()));
}
JavaQueryCompiler compiler = new JPQLCompiler(om.getMetaDataManager(), om.getClassLoaderResolver(),
from, candidateClass, candidateCollection,
this.filter, getParsedImports(), this.ordering, this.result, this.grouping, this.having,
explicitParameters, update);
compilation = compiler.compile(parameterValues, subqueries);
if (QueryUtils.queryReturnsSingleRow(this))
{
compilation.setReturnsSingleRow();
}
if (compilation.getExprResult() == null)
{
// If the result was "Object(e)" or "e" then this is meaningless so remove
result = null;
}
if (NucleusLogger.QUERY.isDebugEnabled())
{
NucleusLogger.QUERY.debug(LOCALISER.msg("021045", "JPQL",
"" + (System.currentTimeMillis() - startTime)));
}
if (subqueries != null)
{
// Compile any subqueries
Iterator iter = subqueries.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry entry = (Map.Entry)iter.next();
SubqueryDefinition subqueryDefinition = (SubqueryDefinition)entry.getValue();
Query subquery = subqueryDefinition.getQuery();
if (NucleusLogger.QUERY.isDebugEnabled())
{
startTime = System.currentTimeMillis();
NucleusLogger.QUERY.debug(LOCALISER.msg("021044", "JPQL",
((AbstractJPQLQuery)subquery).getSingleStringQuery()));
}
compiler = new JPQLCompiler(om.getMetaDataManager(), om.getClassLoaderResolver(),
subquery.from, subquery.candidateClass, null,
subquery.filter, getParsedImports(), subquery.ordering, subquery.result,
subquery.grouping, subquery.having, null, null);
QueryCompilation subqueryCompilation = compiler.compile(parameterValues, null);
compilation.addSubqueryCompilation((String)entry.getKey(), subqueryCompilation);
if (NucleusLogger.QUERY.isDebugEnabled())
{
NucleusLogger.QUERY.debug(LOCALISER.msg("021045", "JPQL",
"" + (System.currentTimeMillis() - startTime)));
}
}
}
if (implicitParameters != null)
{
// Make sure any implicit parameters have their values in the compilation
Iterator paramKeyIter = implicitParameters.keySet().iterator();
while (paramKeyIter.hasNext())
{
Object paramKey = paramKeyIter.next();
String paramName = "" + paramKey;
applyImplicitParameterValueToCompilation(paramName, implicitParameters.get(paramName));
}
}
checkParameterTypesAgainstCompilation(parameterValues);
if (caching)
{
// Cache for future reference
queryMgr.addQueryCompilation("JPQL", toString(), compilation);
}
}
/**
* Utility to resolve the declaration to a particular class.
* Takes the passed in name, together with the defined import declarations and returns the
* class represented by the declaration.
* @param classDecl The declaration
* @return The class it resolves to (if any)
* @throws NucleusUserException Thrown if the class cannot be resolved.
*/
public Class resolveClassDeclaration(String classDecl)
{
// Try to find an entity name before relaying to the superclass method
AbstractClassMetaData acmd = this.getStoreManager().getOMFContext().getMetaDataManager().getMetaDataForEntityName(classDecl);
if (acmd != null)
{
classDecl = acmd.getFullClassName();
}
return super.resolveClassDeclaration(classDecl);
}
}