org.datanucleus.store.rdbms.query.SQLQuery Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-rdbms Show documentation
Show all versions of datanucleus-rdbms Show documentation
Plugin for DataNucleus providing persistence to RDBMS datastores.
/**********************************************************************
Copyright (c) 2004 Erik Bengtson 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:
2004 Andy Jefferson - commented and localised
2004 Andy Jefferson - changed to allow for null candidate class
2005 Andy Jefferson - changed to use JDBC parameters
2005 Andy Jefferson - added timeout support
2005 Andy Jefferson - changed to use PersistenceCapable candidate class
2005 Andy Jefferson - added checks on missing columns to compile step
2006 Andy Jefferson - implemented executeWithMap taking Integer args
...
**********************************************************************/
package org.datanucleus.store.rdbms.query;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.exceptions.ClassNotPersistableException;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.store.StoreManager;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.connection.ManagedConnectionResourceListener;
import org.datanucleus.store.rdbms.adapter.DatastoreAdapter;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.mapping.StatementClassMapping;
import org.datanucleus.store.rdbms.mapping.StatementMappingIndex;
import org.datanucleus.store.rdbms.mapping.datastore.DatastoreMapping;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.mapping.java.PersistableMapping;
import org.datanucleus.store.rdbms.table.DatastoreClass;
import org.datanucleus.store.query.AbstractSQLQuery;
import org.datanucleus.store.query.Query;
import org.datanucleus.store.query.QueryInterruptedException;
import org.datanucleus.store.query.QueryResult;
import org.datanucleus.store.rdbms.SQLController;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* A Query using SQL, and keeping to the JDO2 definition of a SQL query.
* The majority of this has to be specified in the query filter itself.
* There are no variables/imports. Ordering/grouping is explicit in the query.
* Results
* Results from this type of query will be :-
*
* - resultClass - each row of the ResultSet is converted into an instance of the result class
* - candidateClass - each row of the ResultSet is converted into an instance of the candidate
* - Object[] - when no candidate or result class specified
* - Long - when the query is an INSERT/UPDATE/DELETE/MERGE
*
* Parameters
* Parameters to this type of query can be :-
*
* - Positional : The SQL statement includes "?" and the parameters are positional starting at 1
* (just like in JDBC).
* - Numbered : The SQL statement includes "?1", "?2" etc (with numbers starting at 1) and the users input
* of parameters at execute is of the numbered parameters.
* - Named : The SQL statement includes ":myParam1", ":myOtherParam" etc and the users input of parameters
* via "executeWithMap" includes values for all specified names.
*
*/
public final class SQLQuery extends AbstractSQLQuery
{
/** Localiser of messages. */
protected static final Localiser LOCALISER_RDBMS = Localiser.getInstance(
"org.datanucleus.store.rdbms.Localisation", RDBMSStoreManager.class.getClassLoader());
/** State variable for the compilation state */
protected transient boolean isCompiled = false;
/** Mappings for the result of the query. */
protected transient StatementMappingIndex[] stmtMappings;
/**
* Constructor for a new query using the existing query.
* @param storeMgr StoreManager for this query
* @param ec execution context
* @param query The existing query
*/
public SQLQuery(StoreManager storeMgr, ExecutionContext ec, SQLQuery query)
{
super(storeMgr, ec, query);
}
/**
* Constructs a new query instance having the same criteria as the given query.
* @param storeMgr StoreManager for this query
* @param ec execution context
*/
public SQLQuery(StoreManager storeMgr, ExecutionContext ec)
{
super(storeMgr, ec, (String)null);
}
/**
* Constructs a new query instance for the provided single-string SQL query.
* @param storeMgr StoreManager for this query
* @param ec execution context
* @param queryString The SQL query string
*/
public SQLQuery(StoreManager storeMgr, ExecutionContext ec, String queryString)
{
super(storeMgr, ec, queryString);
}
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof SQLQuery) || !super.equals(obj))
{
return false;
}
return inputSQL.equals(((SQLQuery)obj).inputSQL);
}
public int hashCode()
{
return super.hashCode() ^ inputSQL.hashCode();
}
/**
* Utility to remove any previous compilation of this Query.
*/
protected void discardCompiled()
{
isCompiled = false;
stmtMappings = null;
super.discardCompiled();
}
/**
* Method to return if the query is compiled.
* @return Whether it is compiled
*/
protected boolean isCompiled()
{
return isCompiled;
}
/* (non-Javadoc)
* @see org.datanucleus.store.query.Query#processesRangeInDatastoreQuery()
*/
@Override
public boolean processesRangeInDatastoreQuery()
{
return true;
}
/**
* Verify the elements of the query and provide a hint to the query to prepare and optimize an
* execution plan.
*/
public void compileInternal(Map parameterValues)
{
if (isCompiled)
{
return;
}
compiledSQL = generateQueryStatement();
if (NucleusLogger.QUERY.isDebugEnabled())
{
NucleusLogger.QUERY.debug(LOCALISER.msg("059012", compiledSQL));
}
isCompiled = true;
}
/**
* Execute the query and return the result.
* For a SELECT query this will be the QueryResult.
* For an UPDATE/DELETE it will be the row count for the update statement.
* @param parameters the Map containing all of the parameters (positional parameters) (not null)
* @return the result of the query
*/
protected Object performExecute(Map parameters)
{
if (parameters.size() != (parameterNames != null ? parameterNames.length : 0))
{
throw new NucleusUserException(LOCALISER_RDBMS.msg("059019",
"" + parameterNames.length,"" + parameters.size()));
}
if (type == BULK_DELETE || type == BULK_UPDATE)
{
// Update/Delete statement (INSERT/UPDATE/DELETE/MERGE)
try
{
RDBMSStoreManager storeMgr = (RDBMSStoreManager)getStoreManager();
ManagedConnection mconn = storeMgr.getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try
{
PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, compiledSQL, false);
try
{
// Set the values of any parameters
for (int i=0;i 0)
{
for (int colNum = 0;colNum= 0)
{
int[] exprIndices = null;
if (stmtMappings[fieldNumber].getColumnPositions() != null)
{
exprIndices = new int[stmtMappings[fieldNumber].getColumnPositions().length+1];
for (int i=0;i 0)
{
// Allow for user specification of "XX.YY AS ZZ"
colName = colName.substring(colName.indexOf(" AS ")+4).trim();
}
else if (colName.indexOf(" as ") > 0)
{
// Allow for user specification of "XX.YY as ZZ"
colName = colName.substring(colName.indexOf(" as ")+4).trim();
}
if (candidateCmd.getIdentityType() == IdentityType.DATASTORE)
{
// Check for existence of id column, allowing for any RDBMS using quoted identifiers
if (SQLQuery.columnNamesAreTheSame(dba, idColNames[0], colName))
{
idColMissing[0] = false;
}
}
else if (candidateCmd.getIdentityType() == IdentityType.APPLICATION)
{
for (int j=0; j