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 org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import java.io.StringWriter;
023: import java.math.BigDecimal;
024: import java.sql.DatabaseMetaData;
025: import java.sql.PreparedStatement;
026: import java.sql.SQLException;
027: import java.sql.Types;
028: import java.util.Arrays;
029: import java.util.Calendar;
030: import java.util.Collection;
031: import java.util.Iterator;
032:
033: /**
034: * Utility methods for PreparedStatementSetter/Creator and CallableStatementCreator
035: * implementations, providing sophisticated parameter management (including support
036: * for LOB values).
037: *
038: * <p>Used by PreparedStatementCreatorFactory and CallableStatementCreatorFactory,
039: * but also available for direct use in custom setter/creator implementations.
040: *
041: * @author Thomas Risberg
042: * @author Juergen Hoeller
043: * @since 1.1
044: * @see PreparedStatementSetter
045: * @see PreparedStatementCreator
046: * @see CallableStatementCreator
047: * @see PreparedStatementCreatorFactory
048: * @see CallableStatementCreatorFactory
049: * @see SqlParameter
050: * @see SqlTypeValue
051: * @see org.springframework.jdbc.core.support.SqlLobValue
052: */
053: public abstract class StatementCreatorUtils {
054:
055: private static final Log logger = LogFactory
056: .getLog(StatementCreatorUtils.class);
057:
058: /**
059: * Set the value for a parameter. The method used is based on the SQL type
060: * of the parameter and we can handle complex types like arrays and LOBs.
061: * @param ps the prepared statement or callable statement
062: * @param paramIndex index of the parameter we are setting
063: * @param param the parameter as it is declared including type
064: * @param inValue the value to set
065: * @throws SQLException if thrown by PreparedStatement methods
066: */
067: public static void setParameterValue(PreparedStatement ps,
068: int paramIndex, SqlParameter param, Object inValue)
069: throws SQLException {
070:
071: setParameterValueInternal(ps, paramIndex, param.getSqlType(),
072: param.getTypeName(), param.getScale(), inValue);
073: }
074:
075: /**
076: * Set the value for a parameter. The method used is based on the SQL type
077: * of the parameter and we can handle complex types like arrays and LOBs.
078: * @param ps the prepared statement or callable statement
079: * @param paramIndex index of the parameter we are setting
080: * @param sqlType the SQL type of the parameter
081: * @param inValue the value to set (plain value or a SqlTypeValue)
082: * @throws SQLException if thrown by PreparedStatement methods
083: * @see SqlTypeValue
084: */
085: public static void setParameterValue(PreparedStatement ps,
086: int paramIndex, int sqlType, Object inValue)
087: throws SQLException {
088:
089: setParameterValueInternal(ps, paramIndex, sqlType, null, null,
090: inValue);
091: }
092:
093: /**
094: * Set the value for a parameter. The method used is based on the SQL type
095: * of the parameter and we can handle complex types like arrays and LOBs.
096: * @param ps the prepared statement or callable statement
097: * @param paramIndex index of the parameter we are setting
098: * @param sqlType the SQL type of the parameter
099: * @param typeName the type name of the parameter
100: * (optional, only used for SQL NULL and SqlTypeValue)
101: * @param inValue the value to set (plain value or a SqlTypeValue)
102: * @throws SQLException if thrown by PreparedStatement methods
103: * @see SqlTypeValue
104: */
105: public static void setParameterValue(PreparedStatement ps,
106: int paramIndex, int sqlType, String typeName, Object inValue)
107: throws SQLException {
108:
109: setParameterValueInternal(ps, paramIndex, sqlType, typeName,
110: null, inValue);
111: }
112:
113: /**
114: * Set the value for a parameter. The method used is based on the SQL type
115: * of the parameter and we can handle complex types like arrays and LOBs.
116: * @param ps the prepared statement or callable statement
117: * @param paramIndex index of the parameter we are setting
118: * @param sqlType the SQL type of the parameter
119: * @param typeName the type name of the parameter
120: * (optional, only used for SQL NULL and SqlTypeValue)
121: * @param scale the number of digits after the decimal point
122: * (for DECIMAL and NUMERIC types)
123: * @param inValue the value to set (plain value or a SqlTypeValue)
124: * @throws SQLException if thrown by PreparedStatement methods
125: * @see SqlTypeValue
126: */
127: private static void setParameterValueInternal(PreparedStatement ps,
128: int paramIndex, int sqlType, String typeName,
129: Integer scale, Object inValue) throws SQLException {
130:
131: if (logger.isDebugEnabled()) {
132: logger
133: .debug("Setting SQL statement parameter value: column index "
134: + paramIndex
135: + ", parameter value ["
136: + inValue
137: + "], value class ["
138: + (inValue != null ? inValue.getClass()
139: .getName() : "null")
140: + "], SQL type "
141: + (sqlType == SqlTypeValue.TYPE_UNKNOWN ? "unknown"
142: : Integer.toString(sqlType)));
143: }
144:
145: if (inValue == null) {
146: if (sqlType == SqlTypeValue.TYPE_UNKNOWN) {
147: boolean useSetObject = false;
148: try {
149: DatabaseMetaData dbmd = ps.getConnection()
150: .getMetaData();
151: String databaseProductName = dbmd
152: .getDatabaseProductName();
153: String jdbcDriverName = dbmd.getDriverName();
154: useSetObject = (databaseProductName
155: .indexOf("Informix") != -1 || jdbcDriverName
156: .indexOf("Apache Derby Embedded") != -1);
157: } catch (Throwable ex) {
158: logger.debug(
159: "Could not check database or driver name",
160: ex);
161: }
162: if (useSetObject) {
163: ps.setObject(paramIndex, null);
164: } else {
165: ps.setNull(paramIndex, Types.NULL);
166: }
167: } else if (typeName != null) {
168: ps.setNull(paramIndex, sqlType, typeName);
169: } else {
170: ps.setNull(paramIndex, sqlType);
171: }
172: }
173:
174: else { // inValue != null
175: if (inValue instanceof SqlTypeValue) {
176: ((SqlTypeValue) inValue).setTypeValue(ps, paramIndex,
177: sqlType, typeName);
178: } else if (sqlType == Types.VARCHAR) {
179: ps.setString(paramIndex, inValue.toString());
180: } else if (sqlType == Types.DECIMAL
181: || sqlType == Types.NUMERIC) {
182: if (inValue instanceof BigDecimal) {
183: ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
184: } else if (scale != null) {
185: ps.setObject(paramIndex, inValue, sqlType, scale
186: .intValue());
187: } else {
188: ps.setObject(paramIndex, inValue, sqlType);
189: }
190: } else if (sqlType == Types.DATE) {
191: if (inValue instanceof java.util.Date) {
192: if (inValue instanceof java.sql.Date) {
193: ps.setDate(paramIndex, (java.sql.Date) inValue);
194: } else {
195: ps.setDate(paramIndex, new java.sql.Date(
196: ((java.util.Date) inValue).getTime()));
197: }
198: } else if (inValue instanceof Calendar) {
199: Calendar cal = (Calendar) inValue;
200: ps.setDate(paramIndex, new java.sql.Date(cal
201: .getTime().getTime()), cal);
202: } else {
203: ps.setObject(paramIndex, inValue, Types.DATE);
204: }
205: } else if (sqlType == Types.TIME) {
206: if (inValue instanceof java.util.Date) {
207: if (inValue instanceof java.sql.Time) {
208: ps.setTime(paramIndex, (java.sql.Time) inValue);
209: } else {
210: ps.setTime(paramIndex, new java.sql.Time(
211: ((java.util.Date) inValue).getTime()));
212: }
213: } else if (inValue instanceof Calendar) {
214: Calendar cal = (Calendar) inValue;
215: ps.setTime(paramIndex, new java.sql.Time(cal
216: .getTime().getTime()), cal);
217: } else {
218: ps.setObject(paramIndex, inValue, Types.TIME);
219: }
220: } else if (sqlType == Types.TIMESTAMP) {
221: if (inValue instanceof java.util.Date) {
222: if (inValue instanceof java.sql.Timestamp) {
223: ps.setTimestamp(paramIndex,
224: (java.sql.Timestamp) inValue);
225: } else {
226: ps.setTimestamp(paramIndex,
227: new java.sql.Timestamp(
228: ((java.util.Date) inValue)
229: .getTime()));
230: }
231: } else if (inValue instanceof Calendar) {
232: Calendar cal = (Calendar) inValue;
233: ps.setTimestamp(paramIndex, new java.sql.Timestamp(
234: cal.getTime().getTime()), cal);
235: } else {
236: ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
237: }
238: } else if (sqlType == SqlTypeValue.TYPE_UNKNOWN) {
239: if (isStringValue(inValue)) {
240: ps.setString(paramIndex, inValue.toString());
241: } else if (isDateValue(inValue)) {
242: ps.setTimestamp(paramIndex, new java.sql.Timestamp(
243: ((java.util.Date) inValue).getTime()));
244: } else if (inValue instanceof Calendar) {
245: Calendar cal = (Calendar) inValue;
246: ps.setTimestamp(paramIndex, new java.sql.Timestamp(
247: cal.getTime().getTime()));
248: } else {
249: // Fall back to generic setObject call without SQL type specified.
250: ps.setObject(paramIndex, inValue);
251: }
252: } else {
253: // Fall back to generic setObject call with SQL type specified.
254: ps.setObject(paramIndex, inValue, sqlType);
255: }
256: }
257: }
258:
259: /**
260: * Check whether the given value can be treated as a String value.
261: */
262: private static boolean isStringValue(Object inValue) {
263: // Consider any CharSequence (including JDK 1.5's StringBuilder) as String.
264: return (inValue instanceof CharSequence || inValue instanceof StringWriter);
265: }
266:
267: /**
268: * Check whether the given value is a <code>java.util.Date</code>
269: * (but not one of the JDBC-specific subclasses).
270: */
271: private static boolean isDateValue(Object inValue) {
272: return (inValue instanceof java.util.Date && !(inValue instanceof java.sql.Date
273: || inValue instanceof java.sql.Time || inValue instanceof java.sql.Timestamp));
274: }
275:
276: /**
277: * Clean up all resources held by parameter values which were passed to an
278: * execute method. This is for example important for closing LOB values.
279: * @param paramValues parameter values supplied. May be <code>null</code>.
280: * @see DisposableSqlTypeValue#cleanup()
281: * @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
282: */
283: public static void cleanupParameters(Object[] paramValues) {
284: if (paramValues != null) {
285: cleanupParameters(Arrays.asList(paramValues));
286: }
287: }
288:
289: /**
290: * Clean up all resources held by parameter values which were passed to an
291: * execute method. This is for example important for closing LOB values.
292: * @param paramValues parameter values supplied. May be <code>null</code>.
293: * @see DisposableSqlTypeValue#cleanup()
294: * @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
295: */
296: public static void cleanupParameters(Collection paramValues) {
297: if (paramValues != null) {
298: for (Iterator it = paramValues.iterator(); it.hasNext();) {
299: Object inValue = it.next();
300: if (inValue instanceof DisposableSqlTypeValue) {
301: ((DisposableSqlTypeValue) inValue).cleanup();
302: }
303: }
304: }
305: }
306:
307: }
|