// Create the SQL statement, and its result/parameter definitions
RDBMSStoreManager storeMgr = (RDBMSStoreManager)getStoreManager();
if (candidateClass == null)
{
throw new NucleusUserException(LOCALISER.msg("021009", candidateClassName));
}
// Make sure any persistence info is loaded
ec.hasPersistenceInformationForClass(candidateClass);
AbstractClassMetaData acmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
if (candidateClass.isInterface())
{
// Query of interface
String[] impls = ec.getMetaDataManager().getClassesImplementingInterface(candidateClass.getName(), clr);
if (impls.length == 1 && acmd.isImplementationOfPersistentDefinition())
{
// Only the generated implementation, so just use its metadata
}
else
{
// Use metadata for the persistent interface
acmd = ec.getMetaDataManager().getMetaDataForInterface(candidateClass, clr);
if (acmd == null)
{
throw new NucleusUserException("Attempting to query an interface yet it is not declared 'persistent'." +
" Define the interface in metadata as being persistent to perform this operation, and make sure" +
" any implementations use the same identity and identity member(s)");
}
}
}
if (parameterValues != null)
{
// Check for null values on primitive parameters
Set paramNames = parameterValues.entrySet();
Iterator<Map.Entry> iter = paramNames.iterator();
while (iter.hasNext())
{
Map.Entry entry = iter.next();
Object paramName = entry.getKey();
if (paramName instanceof String)
{
Symbol sym = compilation.getSymbolTable().getSymbol((String)paramName);
Object value = entry.getValue();
if (value == null)
{
// When we have parameters supplied and have the flag
// "datanucleus.query.checkUnusedParameters" set to false, the symbol may be null
// so omit the check for that case
if (sym != null && sym.getValueType() != null && sym.getValueType().isPrimitive())
{
throw new NucleusUserException(LOCALISER.msg("021117", paramName,
sym.getValueType().getName()));
}
}
}
}
}
QueryManager qm = getQueryManager();
String datastoreKey = storeMgr.getQueryCacheKey();
String queryCacheKey = getQueryCacheKey();
if (useCaching() && queryCacheKey != null)
{
// Check if we have any parameters set to null, since this can invalidate a datastore compilation
// e.g " field == :val" can be "COL IS NULL" or "COL = <val>"
boolean nullParameter = false;
if (parameterValues != null)
{
Iterator iter = parameterValues.values().iterator();
while (iter.hasNext())
{
Object val = iter.next();
if (val == null)
{
nullParameter = true;
break;
}
}
}
if (!nullParameter)
{
// Allowing caching so try to find compiled (datastore) query
datastoreCompilation = (RDBMSQueryCompilation)qm.getDatastoreQueryCompilation(datastoreKey,
getLanguage(), queryCacheKey);
if (datastoreCompilation != null)
{
// Cached compilation exists for this datastore so reuse it
setResultDistinct(compilation.getResultDistinct());
return;
}
}
}
// Compile the query for the datastore since not cached
if (type == Query.BULK_UPDATE)
{
datastoreCompilation = new RDBMSQueryCompilation();
compileQueryUpdate(parameterValues, acmd);
}
else if (type == Query.BULK_DELETE)
{
datastoreCompilation = new RDBMSQueryCompilation();
compileQueryDelete(parameterValues, acmd);
}
else
{
datastoreCompilation = new RDBMSQueryCompilation();
synchronized (datastoreCompilation)
{
if (inMemory)
{
// Generate statement to just retrieve all candidate objects for later processing
compileQueryToRetrieveCandidates(parameterValues, acmd);
}
else
{
// Generate statement to perform the full query in the datastore
compileQueryFull(parameterValues, acmd);
if (result != null)
{
// Check existence of invalid selections in the result
StatementResultMapping resultMapping = datastoreCompilation.getResultDefinition();
for (int i=0;i<resultMapping.getNumberOfResultExpressions();i++)
{
Object stmtMap = resultMapping.getMappingForResultExpression(i);
if (stmtMap instanceof StatementMappingIndex)
{
StatementMappingIndex idx = (StatementMappingIndex)stmtMap;
AbstractMemberMetaData mmd = idx.getMapping().getMemberMetaData();
if (mmd != null)
{
if ((mmd.hasCollection() || mmd.hasMap() || mmd.hasArray()) &&
idx.getMapping() instanceof AbstractContainerMapping)
{
throw new NucleusUserException(LOCALISER.msg("021213"));
}
}
}
}
if (resultClass != null)
{
// Check validity of resultClass for the result (PrivilegedAction since uses reflection)
AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
// Check that this class has the necessary constructor/setters/fields to be used
StatementResultMapping resultMapping = datastoreCompilation.getResultDefinition();
if (QueryUtils.resultClassIsSimple(resultClass.getName()))
{
if (resultMapping.getNumberOfResultExpressions() > 1)
{
// Invalid number of result expressions
throw new NucleusUserException(LOCALISER.msg("021201", resultClass.getName()));
}
Object stmtMap = resultMapping.getMappingForResultExpression(0);
if (stmtMap instanceof StatementMappingIndex)
{
StatementMappingIndex idx = (StatementMappingIndex)stmtMap;
Class exprType = idx.getMapping().getJavaType();
boolean typeConsistent = false;
if (exprType == resultClass)
{
typeConsistent = true;
}
else if (exprType.isPrimitive())
{
Class resultClassPrimitive = ClassUtils.getPrimitiveTypeForType(resultClass);
if (resultClassPrimitive == exprType)
{
typeConsistent = true;
}
}
if (!typeConsistent)
{
// Inconsistent expression type not matching the result class type
throw new NucleusUserException(LOCALISER.msg("021202", resultClass.getName(), exprType));
}
}
else
{
// TODO Handle StatementClassMapping
// TODO Handle StatementNewObjectMapping
throw new NucleusUserException("Don't support result clause of " +
result + " with resultClass of " + resultClass.getName());
}
}
else if (QueryUtils.resultClassIsUserType(resultClass.getName()))
{
// Check for valid constructor (either using param types, or using default ctr)
Class[] ctrTypes = new Class[resultMapping.getNumberOfResultExpressions()];
for (int i=0;i<ctrTypes.length;i++)
{
Object stmtMap = resultMapping.getMappingForResultExpression(i);
if (stmtMap instanceof StatementMappingIndex)
{
ctrTypes[i] = ((StatementMappingIndex)stmtMap).getMapping().getJavaType();
}
else if (stmtMap instanceof StatementNewObjectMapping)
{
// TODO Handle this
}
}
Constructor ctr = ClassUtils.getConstructorWithArguments(resultClass, ctrTypes);
if (ctr == null && !ClassUtils.hasDefaultConstructor(resultClass))
{
// No valid constructor found!
throw new NucleusUserException(LOCALISER.msg("021205", resultClass.getName()));
}
else if (ctr == null)
{
// We are using default constructor, so check the types of the result expressions for means of input
for (int i=0;i<resultMapping.getNumberOfResultExpressions();i++)
{
Object stmtMap = resultMapping.getMappingForResultExpression(i);
if (stmtMap instanceof StatementMappingIndex)
{
StatementMappingIndex mapIdx = (StatementMappingIndex)stmtMap;
AbstractMemberMetaData mmd = mapIdx.getMapping().getMemberMetaData();
String fieldName = mapIdx.getColumnAlias();
Class fieldType = mapIdx.getMapping().getJavaType();
if (fieldName == null && mmd != null)
{
fieldName = mmd.getName();
}
if (fieldName != null)
{
// Check for the field of that name in the result class
Class resultFieldType = null;
boolean publicField = true;
try
{
Field fld = resultClass.getDeclaredField(fieldName);
resultFieldType = fld.getType();
// Check the type of the field
if (!ClassUtils.typesAreCompatible(fieldType, resultFieldType) &&
!ClassUtils.typesAreCompatible(resultFieldType, fieldType))
{
throw new NucleusUserException(LOCALISER.msg("021211",
fieldName, fieldType.getName(), resultFieldType.getName()));
}
if (!Modifier.isPublic(fld.getModifiers()))
{
publicField = false;
}
}
catch (NoSuchFieldException nsfe)
{
publicField = false;
}
// Check for a public set method
if (!publicField)
{
Method setMethod = QueryUtils.getPublicSetMethodForFieldOfResultClass(resultClass, fieldName, resultFieldType);
if (setMethod == null)
{
// No setter, so check for a public put(Object, Object) method
Method putMethod = QueryUtils.getPublicPutMethodForResultClass(resultClass);
if (putMethod == null)
{
throw new NucleusUserException(LOCALISER.msg("021212",
resultClass.getName(), fieldName));
}
}
}
}