001: /*
002: * Copyright 2002-2006 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.CallableStatement;
020: import java.sql.Connection;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.util.HashMap;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.springframework.dao.InvalidDataAccessApiUsageException;
029: import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
030:
031: /**
032: * Helper class that can efficiently create multiple CallableStatementCreator
033: * objects with different parameters based on a SQL statement and a single
034: * set of parameter declarations.
035: *
036: * @author Rod Johnson
037: * @author Thomas Risberg
038: * @author Juergen Hoeller
039: */
040: public class CallableStatementCreatorFactory {
041:
042: /** The SQL call string, which won't change when the parameters change. */
043: private final String callString;
044:
045: /** List of SqlParameter objects. May not be <code>null</code>. */
046: private final List declaredParameters;
047:
048: private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
049:
050: private boolean updatableResults = false;
051:
052: private NativeJdbcExtractor nativeJdbcExtractor;
053:
054: /**
055: * Create a new factory. Will need to add parameters
056: * via the addParameter() method or have no parameters.
057: */
058: public CallableStatementCreatorFactory(String callString) {
059: this .callString = callString;
060: this .declaredParameters = new LinkedList();
061: }
062:
063: /**
064: * Create a new factory with sql and the given parameters.
065: * @param callString the SQL call string
066: * @param declaredParameters list of SqlParameter objects
067: */
068: public CallableStatementCreatorFactory(String callString,
069: List declaredParameters) {
070: this .callString = callString;
071: this .declaredParameters = declaredParameters;
072: }
073:
074: /**
075: * Add a new declared parameter.
076: * Order of parameter addition is significant.
077: */
078: public void addParameter(SqlParameter param) {
079: this .declaredParameters.add(param);
080: }
081:
082: /**
083: * Set whether to use prepared statements that return a
084: * specific type of ResultSet.
085: * @param resultSetType the ResultSet type
086: * @see java.sql.ResultSet#TYPE_FORWARD_ONLY
087: * @see java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE
088: * @see java.sql.ResultSet#TYPE_SCROLL_SENSITIVE
089: */
090: public void setResultSetType(int resultSetType) {
091: this .resultSetType = resultSetType;
092: }
093:
094: /**
095: * Set whether to use prepared statements capable of returning
096: * updatable ResultSets.
097: */
098: public void setUpdatableResults(boolean updatableResults) {
099: this .updatableResults = updatableResults;
100: }
101:
102: /**
103: * Specify the NativeJdbcExtractor to use for unwrapping
104: * CallableStatements, if any.
105: */
106: public void setNativeJdbcExtractor(
107: NativeJdbcExtractor nativeJdbcExtractor) {
108: this .nativeJdbcExtractor = nativeJdbcExtractor;
109: }
110:
111: /**
112: * Return a new CallableStatementCreator instance given this parameters.
113: * @param inParams List of parameters. May be <code>null</code>.
114: */
115: public CallableStatementCreator newCallableStatementCreator(
116: Map inParams) {
117: return new CallableStatementCreatorImpl(
118: inParams != null ? inParams : new HashMap());
119: }
120:
121: /**
122: * Return a new CallableStatementCreator instance given this parameter mapper.
123: * @param inParamMapper ParameterMapper implementation that will return a Map of parameters. May not be <code>null</code>.
124: */
125: public CallableStatementCreator newCallableStatementCreator(
126: ParameterMapper inParamMapper) {
127: return new CallableStatementCreatorImpl(inParamMapper);
128: }
129:
130: /**
131: * CallableStatementCreator implementation returned by this class.
132: */
133: private class CallableStatementCreatorImpl implements
134: CallableStatementCreator, SqlProvider, ParameterDisposer {
135:
136: private ParameterMapper inParameterMapper;
137:
138: private Map inParameters;
139:
140: /**
141: * Create a new CallableStatementCreatorImpl.
142: * @param inParamMapper ParameterMapper implementation for mapping input parameters.
143: * May not be <code>null</code>.
144: */
145: public CallableStatementCreatorImpl(
146: ParameterMapper inParamMapper) {
147: this .inParameterMapper = inParamMapper;
148: }
149:
150: /**
151: * Create a new CallableStatementCreatorImpl.
152: * @param inParams list of SqlParameter objects. May not be <code>null</code>.
153: */
154: public CallableStatementCreatorImpl(Map inParams) {
155: this .inParameters = inParams;
156: }
157:
158: public CallableStatement createCallableStatement(Connection con)
159: throws SQLException {
160: // If we were given a ParameterMapper - we must let the mapper do its thing to create the Map.
161: if (this .inParameterMapper != null) {
162: this .inParameters = this .inParameterMapper
163: .createMap(con);
164: } else {
165: if (this .inParameters == null) {
166: throw new InvalidDataAccessApiUsageException(
167: "A ParameterMapper or a Map of parameters must be provided");
168: }
169: }
170:
171: CallableStatement cs = null;
172: if (resultSetType == ResultSet.TYPE_FORWARD_ONLY
173: && !updatableResults) {
174: cs = con.prepareCall(callString);
175: } else {
176: cs = con.prepareCall(callString, resultSetType,
177: updatableResults ? ResultSet.CONCUR_UPDATABLE
178: : ResultSet.CONCUR_READ_ONLY);
179: }
180:
181: // Determine CallabeStatement to pass to custom types.
182: CallableStatement csToUse = cs;
183: if (nativeJdbcExtractor != null) {
184: csToUse = nativeJdbcExtractor
185: .getNativeCallableStatement(cs);
186: }
187:
188: int sqlColIndx = 1;
189: for (int i = 0; i < declaredParameters.size(); i++) {
190: SqlParameter declaredParameter = (SqlParameter) declaredParameters
191: .get(i);
192: if (!this .inParameters.containsKey(declaredParameter
193: .getName())
194: && !(declaredParameter instanceof ResultSetSupportingSqlParameter)) {
195: throw new InvalidDataAccessApiUsageException(
196: "Required input parameter '"
197: + declaredParameter.getName()
198: + "' is missing");
199: }
200: // The value may still be null.
201: Object inValue = this .inParameters
202: .get(declaredParameter.getName());
203: if (!(declaredParameter instanceof ResultSetSupportingSqlParameter)) {
204: StatementCreatorUtils.setParameterValue(csToUse,
205: sqlColIndx, declaredParameter, inValue);
206: } else {
207: // It's an output parameter: Skip SqlReturnResultSet parameters.
208: // It need not (but may be) supplied by the caller.
209: if (declaredParameter instanceof SqlOutParameter) {
210: if (declaredParameter.getTypeName() != null) {
211: cs.registerOutParameter(sqlColIndx,
212: declaredParameter.getSqlType(),
213: declaredParameter.getTypeName());
214: } else {
215: cs.registerOutParameter(sqlColIndx,
216: declaredParameter.getSqlType());
217: }
218: if (((SqlOutParameter) declaredParameter)
219: .isInputValueProvided()
220: || inValue != null) {
221: StatementCreatorUtils.setParameterValue(
222: csToUse, sqlColIndx,
223: declaredParameter, inValue);
224: }
225: }
226: }
227: if (!(declaredParameter instanceof SqlReturnResultSet)) {
228: sqlColIndx++;
229: }
230: }
231:
232: return cs;
233: }
234:
235: public String getSql() {
236: return callString;
237: }
238:
239: public void cleanupParameters() {
240: if (this .inParameters != null) {
241: StatementCreatorUtils
242: .cleanupParameters(this .inParameters.values());
243: }
244: }
245:
246: public String toString() {
247: StringBuffer buf = new StringBuffer(
248: "CallableStatementCreatorFactory.CallableStatementCreatorImpl: sql=[");
249: buf.append(callString).append("]; parameters=").append(
250: this.inParameters);
251: return buf.toString();
252: }
253: }
254:
255: }
|