001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jdbc.core;
018:
019: import java.sql.Connection;
020: import java.sql.PreparedStatement;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.sql.Types;
024: import java.util.Arrays;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.List;
030:
031: import org.springframework.dao.InvalidDataAccessApiUsageException;
032: import org.springframework.dao.InvalidDataAccessResourceUsageException;
033: import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
034: import org.springframework.util.Assert;
035:
036: /**
037: * Helper class that efficiently creates multiple {@link PreparedStatementCreator}
038: * objects with different parameters based on a SQL statement and a single
039: * set of parameter declarations.
040: *
041: * @author Rod Johnson
042: * @author Juergen Hoeller
043: */
044: public class PreparedStatementCreatorFactory {
045:
046: /** The SQL, which won't change when the parameters change */
047: private final String sql;
048:
049: /** List of SqlParameter objects. May not be <code>null</code>. */
050: private final List declaredParameters;
051:
052: private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
053:
054: private boolean updatableResults = false;
055:
056: private boolean returnGeneratedKeys = false;
057:
058: private String[] generatedKeysColumnNames = null;
059:
060: private NativeJdbcExtractor nativeJdbcExtractor;
061:
062: /**
063: * Create a new factory. Will need to add parameters
064: * via the addParameter() method or have no parameters.
065: */
066: public PreparedStatementCreatorFactory(String sql) {
067: this .sql = sql;
068: this .declaredParameters = new LinkedList();
069: }
070:
071: /**
072: * Create a new factory with the given SQL and JDBC types.
073: * @param sql SQL to execute
074: * @param types int array of JDBC types
075: */
076: public PreparedStatementCreatorFactory(String sql, int[] types) {
077: this .sql = sql;
078: this .declaredParameters = SqlParameter
079: .sqlTypesToAnonymousParameterList(types);
080: }
081:
082: /**
083: * Create a new factory with the given SQL and parameters.
084: * @param sql SQL
085: * @param declaredParameters list of SqlParameter objects
086: * @see SqlParameter
087: */
088: public PreparedStatementCreatorFactory(String sql,
089: List declaredParameters) {
090: this .sql = sql;
091: this .declaredParameters = declaredParameters;
092: }
093:
094: /**
095: * Add a new declared parameter.
096: * <p>Order of parameter addition is significant.
097: * @param param the parameter to add to the list of declared parameters.
098: */
099: public void addParameter(SqlParameter param) {
100: this .declaredParameters.add(param);
101: }
102:
103: /**
104: * Set whether to use prepared statements that return a
105: * specific type of ResultSet.
106: * @param resultSetType the ResultSet type
107: * @see java.sql.ResultSet#TYPE_FORWARD_ONLY
108: * @see java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE
109: * @see java.sql.ResultSet#TYPE_SCROLL_SENSITIVE
110: */
111: public void setResultSetType(int resultSetType) {
112: this .resultSetType = resultSetType;
113: }
114:
115: /**
116: * Set whether to use prepared statements capable of returning
117: * updatable ResultSets.
118: */
119: public void setUpdatableResults(boolean updatableResults) {
120: this .updatableResults = updatableResults;
121: }
122:
123: /**
124: * Set whether prepared statements should be capable of returning
125: * auto-generated keys.
126: */
127: public void setReturnGeneratedKeys(boolean returnGeneratedKeys) {
128: this .returnGeneratedKeys = returnGeneratedKeys;
129: }
130:
131: /**
132: * Set the column names of the auto-generated keys.
133: */
134: public void setGeneratedKeysColumnNames(String[] names) {
135: this .generatedKeysColumnNames = names;
136: }
137:
138: /**
139: * Specify the NativeJdbcExtractor to use for unwrapping
140: * PreparedStatements, if any.
141: */
142: public void setNativeJdbcExtractor(
143: NativeJdbcExtractor nativeJdbcExtractor) {
144: this .nativeJdbcExtractor = nativeJdbcExtractor;
145: }
146:
147: /**
148: * Return a new PreparedStatementSetter for the given parameters.
149: * @param params List of parameters (may be <code>null</code>)
150: */
151: public PreparedStatementSetter newPreparedStatementSetter(
152: List params) {
153: return new PreparedStatementCreatorImpl(params != null ? params
154: : Collections.EMPTY_LIST);
155: }
156:
157: /**
158: * Return a new PreparedStatementSetter for the given parameters.
159: * @param params the parameter array (may be <code>null</code>)
160: */
161: public PreparedStatementSetter newPreparedStatementSetter(
162: Object[] params) {
163: return new PreparedStatementCreatorImpl(params != null ? Arrays
164: .asList(params) : Collections.EMPTY_LIST);
165: }
166:
167: /**
168: * Return a new PreparedStatementCreator for the given parameters.
169: * @param params List of parameters (may be <code>null</code>)
170: */
171: public PreparedStatementCreator newPreparedStatementCreator(
172: List params) {
173: return new PreparedStatementCreatorImpl(params != null ? params
174: : Collections.EMPTY_LIST);
175: }
176:
177: /**
178: * Return a new PreparedStatementCreator for the given parameters.
179: * @param params the parameter array (may be <code>null</code>)
180: */
181: public PreparedStatementCreator newPreparedStatementCreator(
182: Object[] params) {
183: return new PreparedStatementCreatorImpl(params != null ? Arrays
184: .asList(params) : Collections.EMPTY_LIST);
185: }
186:
187: /**
188: * Return a new PreparedStatementCreator for the given parameters.
189: * @param sqlToUse the actual SQL statement to use (if different from
190: * the factory's, for example because of named parameter expanding)
191: * @param params the parameter array (may be <code>null</code>)
192: */
193: public PreparedStatementCreator newPreparedStatementCreator(
194: String sqlToUse, Object[] params) {
195: return new PreparedStatementCreatorImpl(sqlToUse,
196: params != null ? Arrays.asList(params)
197: : Collections.EMPTY_LIST);
198: }
199:
200: /**
201: * PreparedStatementCreator implementation returned by this class.
202: */
203: private class PreparedStatementCreatorImpl implements
204: PreparedStatementCreator, PreparedStatementSetter,
205: SqlProvider, ParameterDisposer {
206:
207: private final String actualSql;
208:
209: private final List parameters;
210:
211: public PreparedStatementCreatorImpl(List parameters) {
212: this (sql, parameters);
213: }
214:
215: public PreparedStatementCreatorImpl(String actualSql,
216: List parameters) {
217: this .actualSql = actualSql;
218: Assert.notNull(parameters,
219: "Parameters List must not be null");
220: this .parameters = parameters;
221: if (this .parameters.size() != declaredParameters.size())
222: throw new InvalidDataAccessApiUsageException("SQL ["
223: + sql + "]: given " + this .parameters.size()
224: + " parameters but expected "
225: + declaredParameters.size());
226: }
227:
228: public PreparedStatement createPreparedStatement(Connection con)
229: throws SQLException {
230: PreparedStatement ps = null;
231: if (generatedKeysColumnNames != null || returnGeneratedKeys) {
232: try {
233: if (generatedKeysColumnNames != null) {
234: ps = con.prepareStatement(this .actualSql,
235: generatedKeysColumnNames);
236: } else {
237: ps = con
238: .prepareStatement(
239: this .actualSql,
240: PreparedStatement.RETURN_GENERATED_KEYS);
241: }
242: } catch (AbstractMethodError ex) {
243: throw new InvalidDataAccessResourceUsageException(
244: "The JDBC driver is not compliant to JDBC 3.0 and thus "
245: + "does not support retrieval of auto-generated keys",
246: ex);
247: }
248: } else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY
249: && !updatableResults) {
250: ps = con.prepareStatement(this .actualSql);
251: } else {
252: ps = con.prepareStatement(this .actualSql,
253: resultSetType,
254: updatableResults ? ResultSet.CONCUR_UPDATABLE
255: : ResultSet.CONCUR_READ_ONLY);
256: }
257: setValues(ps);
258: return ps;
259: }
260:
261: public void setValues(PreparedStatement ps) throws SQLException {
262: // Determine PreparedStatement to pass to custom types.
263: PreparedStatement psToUse = ps;
264: if (nativeJdbcExtractor != null) {
265: psToUse = nativeJdbcExtractor
266: .getNativePreparedStatement(ps);
267: }
268:
269: // Set arguments: Does nothing if there are no parameters.
270: int sqlColIndx = 1;
271: for (int i = 0; i < this .parameters.size(); i++) {
272: Object in = this .parameters.get(i);
273: SqlParameter declaredParameter = null;
274: // SqlParameterValue overrides declared parameter metadata, in particular for
275: // independence from the declared parameter position in case of named parameters.
276: if (in instanceof SqlParameterValue) {
277: SqlParameterValue paramValue = (SqlParameterValue) in;
278: in = paramValue.getValue();
279: declaredParameter = paramValue;
280: } else {
281: declaredParameter = (SqlParameter) declaredParameters
282: .get(i);
283: }
284: if (in instanceof Collection
285: && declaredParameter.getSqlType() != Types.ARRAY) {
286: Collection entries = (Collection) in;
287: for (Iterator it = entries.iterator(); it.hasNext();) {
288: Object entry = it.next();
289: if (entry instanceof Object[]) {
290: Object[] valueArray = ((Object[]) entry);
291: for (int k = 0; k < valueArray.length; k++) {
292: Object argValue = valueArray[k];
293: StatementCreatorUtils
294: .setParameterValue(psToUse,
295: sqlColIndx++,
296: declaredParameter,
297: argValue);
298: }
299: } else {
300: StatementCreatorUtils.setParameterValue(
301: psToUse, sqlColIndx++,
302: declaredParameter, entry);
303: }
304: }
305: } else {
306: StatementCreatorUtils.setParameterValue(psToUse,
307: sqlColIndx++, declaredParameter, in);
308: }
309: }
310: }
311:
312: public String getSql() {
313: return sql;
314: }
315:
316: public void cleanupParameters() {
317: StatementCreatorUtils.cleanupParameters(this .parameters);
318: }
319:
320: public String toString() {
321: StringBuffer buf = new StringBuffer(
322: "PreparedStatementCreatorFactory.PreparedStatementCreatorImpl: sql=[");
323: buf.append(sql).append("]; parameters=").append(
324: this.parameters);
325: return buf.toString();
326: }
327: }
328:
329: }
|