1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.jdbc.core;
  17. import java.sql.CallableStatement;
  18. import java.sql.Connection;
  19. import java.sql.ResultSet;
  20. import java.sql.SQLException;
  21. import java.util.HashMap;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import org.springframework.dao.InvalidDataAccessApiUsageException;
  26. /**
  27. * Helper class that can efficiently create multiple CallableStatementCreator
  28. * objects with different parameters based on a SQL statement and a single
  29. * set of parameter declarations.
  30. * @author Rod Johnson
  31. * @author Thomas Risberg
  32. */
  33. public class CallableStatementCreatorFactory {
  34. /** The SQL call string, which won't change when the parameters change. */
  35. private final String callString;
  36. /** List of SqlParameter objects. May not be null. */
  37. private final List declaredParameters;
  38. private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
  39. private boolean updatableResults = false;
  40. /**
  41. * Create a new factory. Will need to add parameters
  42. * via the addParameter() method or have no parameters.
  43. */
  44. public CallableStatementCreatorFactory(String callString) {
  45. this.callString = callString;
  46. this.declaredParameters = new LinkedList();
  47. }
  48. /**
  49. * Create a new factory with sql and the given parameters.
  50. * @param callString the SQL call string
  51. * @param declaredParameters list of SqlParameter objects
  52. */
  53. public CallableStatementCreatorFactory(String callString, List declaredParameters) {
  54. this.callString = callString;
  55. this.declaredParameters = declaredParameters;
  56. }
  57. /**
  58. * Add a new declared parameter.
  59. * Order of parameter addition is significant.
  60. */
  61. public void addParameter(SqlParameter param) {
  62. this.declaredParameters.add(param);
  63. }
  64. /**
  65. * Set whether to use prepared statements that return a
  66. * specific type of ResultSet.
  67. * @param resultSetType the ResultSet type
  68. * @see java.sql.ResultSet#TYPE_FORWARD_ONLY
  69. * @see java.sql.ResultSet#TYPE_SCROLL_INSENSITIVE
  70. * @see java.sql.ResultSet#TYPE_SCROLL_SENSITIVE
  71. */
  72. public void setResultSetType(int resultSetType) {
  73. this.resultSetType = resultSetType;
  74. }
  75. /**
  76. * Set whether to use prepared statements capable of returning
  77. * updatable ResultSets.
  78. */
  79. public void setUpdatableResults(boolean updatableResults) {
  80. this.updatableResults = updatableResults;
  81. }
  82. /**
  83. * Return a new CallableStatementCreator instance given this parameters.
  84. * @param inParams List of parameters. May be null.
  85. */
  86. public CallableStatementCreator newCallableStatementCreator(Map inParams) {
  87. return new CallableStatementCreatorImpl(inParams != null ? inParams : new HashMap());
  88. }
  89. /**
  90. * Return a new CallableStatementCreator instance given this parameter mapper.
  91. * @param inParamMapper ParameterMapper implementation that will return a Map of parameters. May not be null.
  92. */
  93. public CallableStatementCreator newCallableStatementCreator(ParameterMapper inParamMapper) {
  94. return new CallableStatementCreatorImpl(inParamMapper);
  95. }
  96. /**
  97. * CallableStatementCreator implementation returned by this class.
  98. */
  99. private class CallableStatementCreatorImpl implements CallableStatementCreator, SqlProvider {
  100. private final ParameterMapper inParameterMapper;
  101. private Map inParameters;
  102. /**
  103. * Create a new CallableStatementCreatorImpl.
  104. * @param inParams list of SqlParameter objects. May not be null
  105. */
  106. public CallableStatementCreatorImpl(Map inParams) {
  107. this.inParameterMapper = null;
  108. this.inParameters = inParams;
  109. }
  110. /**
  111. * Create a new CallableStatementCreatorImpl.
  112. * @param inParamMapper ParameterMapper implementation for mapping input parameters.
  113. * May not be null.
  114. */
  115. public CallableStatementCreatorImpl(ParameterMapper inParamMapper) {
  116. this.inParameterMapper = inParamMapper;
  117. this.inParameters = null;
  118. }
  119. public CallableStatement createCallableStatement(Connection con) throws SQLException {
  120. // If we were given a ParameterMapper - we must let the mapper do its thing to create the Map.
  121. if (this.inParameterMapper != null) {
  122. this.inParameters = this.inParameterMapper.createMap(con);
  123. }
  124. else {
  125. if (this.inParameters == null) {
  126. throw new InvalidDataAccessApiUsageException("A ParameterMapper or a Map of parameters must be provided");
  127. }
  128. }
  129. CallableStatement cs = null;
  130. if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {
  131. cs = con.prepareCall(callString);
  132. }
  133. else {
  134. cs = con.prepareCall(callString, resultSetType,
  135. updatableResults ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY);
  136. }
  137. int sqlColIndx = 1;
  138. for (int i = 0; i < declaredParameters.size(); i++) {
  139. SqlParameter p = (SqlParameter) declaredParameters.get(i);
  140. if (!this.inParameters.containsKey(p.getName()) && !(p instanceof SqlOutParameter) &&
  141. !(p instanceof SqlReturnResultSet)) {
  142. throw new InvalidDataAccessApiUsageException("Required input parameter '" + p.getName() + "' is missing");
  143. }
  144. // the value may still be null
  145. Object in = this.inParameters.get(p.getName());
  146. if (!(p instanceof SqlOutParameter) && !(p instanceof SqlReturnResultSet)) {
  147. // input parameters must be supplied
  148. if (in == null && p.getTypeName() != null) {
  149. cs.setNull(sqlColIndx, p.getSqlType(), p.getTypeName());
  150. }
  151. else
  152. if (in != null) {
  153. cs.setObject(sqlColIndx, in, p.getSqlType());
  154. }
  155. else {
  156. cs.setNull(sqlColIndx, p.getSqlType());
  157. }
  158. }
  159. else {
  160. // It's an output parameter. Skip SqlReturnResultSet parameters
  161. // It need not (but may be) supplied by the caller.
  162. if (p instanceof SqlOutParameter) {
  163. if (p.getTypeName() != null) {
  164. cs.registerOutParameter(sqlColIndx, p.getSqlType(), p.getTypeName());
  165. }
  166. else {
  167. cs.registerOutParameter(sqlColIndx, p.getSqlType());
  168. }
  169. if (in != null) {
  170. cs.setObject(sqlColIndx, in, p.getSqlType());
  171. }
  172. }
  173. }
  174. if (!(p instanceof SqlReturnResultSet)) {
  175. sqlColIndx++;
  176. }
  177. }
  178. return cs;
  179. }
  180. public String getSql() {
  181. return callString;
  182. }
  183. public String toString() {
  184. StringBuffer buf = new StringBuffer("CallableStatementCreatorFactory.CallableStatementCreatorImpl: sql=[");
  185. buf.append(callString);
  186. buf.append("]: params=[");
  187. if (inParameters != null) {
  188. buf.append(inParameters.toString());
  189. }
  190. buf.append(']');
  191. return buf.toString();
  192. }
  193. }
  194. }