/*
* Copyright 2002-2008 the original author or authors.
*
* 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.
*/
package org.springframework.jdbc.object;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
/**
* Reusable operation object representing a SQL query.
*
* <p>Subclasses must implement the {@link #newRowMapper} method to provide
* an object that can extract the results of iterating over the
* <code>ResultSet</code> created during the execution of the query.
*
* <p>This class provides a number of public <code>execute</code> methods that are
* analogous to the different convenient JDO query execute methods. Subclasses
* can either rely on one of these inherited methods, or can add their own
* custom execution methods, with meaningful names and typed parameters
* (definitely a best practice). Each custom query method will invoke one of
* this class's untyped query methods.
*
* <p>Like all <code>RdbmsOperation</code> classes that ship with the Spring
* Framework, <code>SqlQuery</code> instances are thread-safe after their
* initialization is complete. That is, after they are constructed and configured
* via their setter methods, they can be used safely from multiple threads.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Thomas Risberg
* @see SqlUpdate
*/
public abstract class SqlQuery<T> extends SqlOperation {
/** The number of rows to expect; if 0, unknown. */
private int rowsExpected = 0;
/**
* Constructor to allow use as a JavaBean.
* <p>The <code>DataSource</code> and SQL must be supplied before
* compilation and use.
*/
public SqlQuery() {
}
/**
* Convenient constructor with a <code>DataSource</code> and SQL string.
* @param ds the <code>DataSource</code> to use to get connections
* @param sql the SQL to execute; SQL can also be supplied at runtime
* by overriding the {@link #getSql()} method.
*/
public SqlQuery(DataSource ds, String sql) {
setDataSource(ds);
setSql(sql);
}
/**
* Set the number of rows expected.
* <p>This can be used to ensure efficient storage of results. The
* default behavior is not to expect any specific number of rows.
*/
public void setRowsExpected(int rowsExpected) {
this.rowsExpected = rowsExpected;
}
/**
* Get the number of rows expected.
*/
public int getRowsExpected() {
return this.rowsExpected;
}
/**
* Central execution method. All un-named parameter execution goes through this method.
* @param params parameters, similar to JDO query parameters.
* Primitive parameters must be represented by their Object wrapper type.
* The ordering of parameters is significant.
* @param context contextual information passed to the <code>mapRow</code>
* callback method. The JDBC operation itself doesn't rely on this parameter,
* but it can be useful for creating the objects of the result list.
* @return a List of objects, one per row of the ResultSet. Normally all these
* will be of the same class, although it is possible to use different types.
*/
public List<T> execute(Object[] params, Map context) throws DataAccessException {
validateParameters(params);
RowMapper<T> rowMapper = newRowMapper(params, context);
return getJdbcTemplate().query(newPreparedStatementCreator(params), rowMapper);
}
/**
* Convenient method to execute without context.
* @param params parameters for the query. Primitive parameters must
* be represented by their Object wrapper type. The ordering of parameters is
* significant.
*/
public List<T> execute(Object... params) throws DataAccessException {
return execute(params, null);
}
/**
* Convenient method to execute without parameters.
* @param context the contextual information for object creation
*/
public List<T> execute(Map context) throws DataAccessException {
return execute((Object[]) null, context);
}
/**
* Convenient method to execute without parameters nor context.
*/
public List<T> execute() throws DataAccessException {
return execute((Object[]) null);
}
/**
* Convenient method to execute with a single int parameter and context.
* @param p1 single int parameter
* @param context the contextual information for object creation
*/
public List<T> execute(int p1, Map context) throws DataAccessException {
return execute(new Object[] {p1}, context);
}
/**
* Convenient method to execute with a single int parameter.
* @param p1 single int parameter
*/
public List<T> execute(int p1) throws DataAccessException {
return execute(p1, null);
}
/**
* Convenient method to execute with two int parameters and context.
* @param p1 first int parameter
* @param p2 second int parameter
* @param context the contextual information for object creation
*/
public List<T> execute(int p1, int p2, Map context) throws DataAccessException {
return execute(new Object[] {p1, p2}, context);
}
/**
* Convenient method to execute with two int parameters.
* @param p1 first int parameter
* @param p2 second int parameter
*/
public List<T> execute(int p1, int p2) throws DataAccessException {
return execute(p1, p2, null);
}
/**
* Convenient method to execute with a single long parameter and context.
* @param p1 single long parameter
* @param context the contextual information for object creation
*/
public List<T> execute(long p1, Map context) throws DataAccessException {
return execute(new Object[] {p1}, context);
}
/**
* Convenient method to execute with a single long parameter.
* @param p1 single long parameter
*/
public List<T> execute(long p1) throws DataAccessException {
return execute(p1, null);
}
/**
* Convenient method to execute with a single String parameter and context.
* @param p1 single String parameter
* @param context the contextual information for object creation
*/
public List<T> execute(String p1, Map context) throws DataAccessException {
return execute(new Object[] {p1}, context);
}
/**
* Convenient method to execute with a single String parameter.
* @param p1 single String parameter
*/
public List<T> execute(String p1) throws DataAccessException {
return execute(p1, null);
}
/**
* Central execution method. All named parameter execution goes through this method.
* @param paramMap parameters associated with the name specified while declaring
* the SqlParameters. Primitive parameters must be represented by their Object wrapper
* type. The ordering of parameters is not significant since they are supplied in a
* SqlParameterMap which is an implementation of the Map interface.
* @param context contextual information passed to the <code>mapRow</code>
* callback method. The JDBC operation itself doesn't rely on this parameter,
* but it can be useful for creating the objects of the result list.
* @return a List of objects, one per row of the ResultSet. Normally all these
* will be of the same class, although it is possible to use different types.
*/
public List<T> executeByNamedParam(Map<String, ?> paramMap, Map context) throws DataAccessException {
validateNamedParameters(paramMap);
ParsedSql parsedSql = getParsedSql();
MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters());
RowMapper<T> rowMapper = newRowMapper(params, context);
return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, params), rowMapper);
}
/**
* Convenient method to execute without context.
* @param paramMap parameters associated with the name specified while declaring
* the SqlParameters. Primitive parameters must be represented by their Object wrapper
* type. The ordering of parameters is not significant.
*/
public List<T> executeByNamedParam(Map<String, ?> paramMap) throws DataAccessException {
return executeByNamedParam(paramMap, null);
}
/**
* Generic object finder method, used by all other <code>findObject</code> methods.
* Object finder methods are like EJB entity bean finders, in that it is
* considered an error if they return more than one result.
* @return the result object, or <code>null</code> if not found. Subclasses may
* choose to treat this as an error and throw an exception.
* @see org.springframework.dao.support.DataAccessUtils#singleResult
*/
public T findObject(Object[] params, Map context) throws DataAccessException {
List<T> results = execute(params, context);
return DataAccessUtils.singleResult(results);
}
/**
* Convenient method to find a single object without context.
*/
public T findObject(Object... params) throws DataAccessException {
return findObject(params, null);
}
/**
* Convenient method to find a single object given a single int parameter
* and a context.
*/
public T findObject(int p1, Map context) throws DataAccessException {
return findObject(new Object[] {p1}, context);
}
/**
* Convenient method to find a single object given a single int parameter.
*/
public T findObject(int p1) throws DataAccessException {
return findObject(p1, null);
}
/**
* Convenient method to find a single object given two int parameters
* and a context.
*/
public T findObject(int p1, int p2, Map context) throws DataAccessException {
return findObject(new Object[] {p1, p2}, context);
}
/**
* Convenient method to find a single object given two int parameters.
*/
public T findObject(int p1, int p2) throws DataAccessException {
return findObject(p1, p2, null);
}
/**
* Convenient method to find a single object given a single long parameter
* and a context.
*/
public T findObject(long p1, Map context) throws DataAccessException {
return findObject(new Object[] {p1}, context);
}
/**
* Convenient method to find a single object given a single long parameter.
*/
public T findObject(long p1) throws DataAccessException {
return findObject(p1, null);
}
/**
* Convenient method to find a single object given a single String parameter
* and a context.
*/
public T findObject(String p1, Map context) throws DataAccessException {
return findObject(new Object[] {p1}, context);
}
/**
* Convenient method to find a single object given a single String parameter.
*/
public T findObject(String p1) throws DataAccessException {
return findObject(p1, null);
}
/**
* Generic object finder method for named parameters.
* @param paramMap Map of parameter name to parameter object,
* matching named parameters specified in the SQL statement.
* Ordering is not significant.
* @param context contextual information passed to the <code>mapRow</code>
* callback method. The JDBC operation itself doesn't rely on this parameter,
* but it can be useful for creating the objects of the result list.
* @return a List of objects, one per row of the ResultSet. Normally all these
* will be of the same class, although it is possible to use different types.
*/
public T findObjectByNamedParam(Map<String, ?> paramMap, Map context) throws DataAccessException {
List<T> results = executeByNamedParam(paramMap, context);
return DataAccessUtils.singleResult(results);
}
/**
* Convenient method to execute without context.
* @param paramMap Map of parameter name to parameter object,
* matching named parameters specified in the SQL statement.
* Ordering is not significant.
*/
public T findObjectByNamedParam(Map<String, ?> paramMap) throws DataAccessException {
return findObjectByNamedParam(paramMap, null);
}
/**
* Subclasses must implement this method to extract an object per row, to be
* returned by the <cod>execute</code> method as an aggregated {@link List}.
* @param parameters the parameters to the <code>execute()</code> method,
* in case subclass is interested; may be <code>null</code> if there
* were no parameters.
* @param context contextual information passed to the <code>mapRow</code>
* callback method. The JDBC operation itself doesn't rely on this parameter,
* but it can be useful for creating the objects of the result list.
* @see #execute
*/
protected abstract RowMapper<T> newRowMapper(Object[] parameters, Map context);
}
|