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.namedparam;
018:
019: import java.util.HashMap;
020: import java.util.List;
021: import java.util.Map;
022:
023: import javax.sql.DataSource;
024:
025: import org.springframework.dao.DataAccessException;
026: import org.springframework.dao.support.DataAccessUtils;
027: import org.springframework.jdbc.core.ColumnMapRowMapper;
028: import org.springframework.jdbc.core.JdbcOperations;
029: import org.springframework.jdbc.core.JdbcTemplate;
030: import org.springframework.jdbc.core.PreparedStatementCallback;
031: import org.springframework.jdbc.core.PreparedStatementCreator;
032: import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
033: import org.springframework.jdbc.core.ResultSetExtractor;
034: import org.springframework.jdbc.core.RowCallbackHandler;
035: import org.springframework.jdbc.core.RowMapper;
036: import org.springframework.jdbc.core.SingleColumnRowMapper;
037: import org.springframework.jdbc.core.SqlRowSetResultSetExtractor;
038: import org.springframework.jdbc.support.KeyHolder;
039: import org.springframework.jdbc.support.rowset.SqlRowSet;
040: import org.springframework.util.Assert;
041:
042: /**
043: * Template class with a basic set of JDBC operations, allowing the use
044: * of named parameters rather than traditional '?' placeholders.
045: *
046: * <p>This class delegates to a wrapped {@link #getJdbcOperations() JdbcTemplate}
047: * once the substitution from named parameters to JDBC style '?' placeholders is
048: * done at execution time. It also allows for expanding a {@link java.util.List}
049: * of values to the appropriate number of placeholders.
050: *
051: * <p>The underlying {@link org.springframework.jdbc.core.JdbcTemplate} is
052: * exposed to allow for convenient access to the traditional
053: * {@link org.springframework.jdbc.core.JdbcTemplate} methods.
054: *
055: * @author Thomas Risberg
056: * @author Juergen Hoeller
057: * @since 2.0
058: * @see NamedParameterJdbcOperations
059: * @see org.springframework.jdbc.core.JdbcTemplate
060: */
061: public class NamedParameterJdbcTemplate implements
062: NamedParameterJdbcOperations {
063:
064: /** The JdbcTemplate we are wrapping */
065: private final JdbcOperations classicJdbcTemplate;
066:
067: /** Map of original SQL String to ParsedSql representation */
068: private final Map parsedSqlCache = new HashMap();
069:
070: /**
071: * Create a new NamedParameterJdbcTemplate for the given {@link DataSource}.
072: * <p>Creates a classic Spring {@link org.springframework.jdbc.core.JdbcTemplate} and wraps it.
073: * @param dataSource the JDBC DataSource to access
074: */
075: public NamedParameterJdbcTemplate(DataSource dataSource) {
076: Assert.notNull(dataSource,
077: "The [dataSource] argument cannot be null.");
078: this .classicJdbcTemplate = new JdbcTemplate(dataSource);
079: }
080:
081: /**
082: * Create a new NamedParameterJdbcTemplate for the given classic
083: * Spring {@link org.springframework.jdbc.core.JdbcTemplate}.
084: * @param classicJdbcTemplate the classic Spring JdbcTemplate to wrap
085: */
086: public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
087: Assert.notNull(classicJdbcTemplate,
088: "JdbcTemplate must not be null");
089: this .classicJdbcTemplate = classicJdbcTemplate;
090: }
091:
092: /**
093: * Expose the classic Spring JdbcTemplate to allow invocation of
094: * less commonly used methods.
095: */
096: public JdbcOperations getJdbcOperations() {
097: return this .classicJdbcTemplate;
098: }
099:
100: public Object execute(String sql, SqlParameterSource paramSource,
101: PreparedStatementCallback action)
102: throws DataAccessException {
103:
104: return getJdbcOperations().execute(
105: getPreparedStatementCreator(sql, paramSource), action);
106: }
107:
108: public Object execute(String sql, Map paramMap,
109: PreparedStatementCallback action)
110: throws DataAccessException {
111: return execute(sql, new MapSqlParameterSource(paramMap), action);
112: }
113:
114: public Object query(String sql, SqlParameterSource paramSource,
115: ResultSetExtractor rse) throws DataAccessException {
116:
117: return getJdbcOperations().query(
118: getPreparedStatementCreator(sql, paramSource), rse);
119: }
120:
121: public Object query(String sql, Map paramMap, ResultSetExtractor rse)
122: throws DataAccessException {
123: return query(sql, new MapSqlParameterSource(paramMap), rse);
124: }
125:
126: public void query(String sql, SqlParameterSource paramSource,
127: RowCallbackHandler rch) throws DataAccessException {
128:
129: getJdbcOperations().query(
130: getPreparedStatementCreator(sql, paramSource), rch);
131: }
132:
133: public void query(String sql, Map paramMap, RowCallbackHandler rch)
134: throws DataAccessException {
135: query(sql, new MapSqlParameterSource(paramMap), rch);
136: }
137:
138: public List query(String sql, SqlParameterSource paramSource,
139: RowMapper rowMapper) throws DataAccessException {
140:
141: return getJdbcOperations().query(
142: getPreparedStatementCreator(sql, paramSource),
143: rowMapper);
144: }
145:
146: public List query(String sql, Map paramMap, RowMapper rowMapper)
147: throws DataAccessException {
148: return query(sql, new MapSqlParameterSource(paramMap),
149: rowMapper);
150: }
151:
152: public Object queryForObject(String sql,
153: SqlParameterSource paramSource, RowMapper rowMapper)
154: throws DataAccessException {
155:
156: List results = getJdbcOperations().query(
157: getPreparedStatementCreator(sql, paramSource),
158: rowMapper);
159: return DataAccessUtils.requiredSingleResult(results);
160: }
161:
162: public Object queryForObject(String sql, Map paramMap,
163: RowMapper rowMapper) throws DataAccessException {
164: return queryForObject(sql, new MapSqlParameterSource(paramMap),
165: rowMapper);
166: }
167:
168: public Object queryForObject(String sql,
169: SqlParameterSource paramSource, Class requiredType)
170: throws DataAccessException {
171:
172: return queryForObject(sql, paramSource,
173: new SingleColumnRowMapper(requiredType));
174: }
175:
176: public Object queryForObject(String sql, Map paramMap,
177: Class requiredType) throws DataAccessException {
178: return queryForObject(sql, paramMap, new SingleColumnRowMapper(
179: requiredType));
180: }
181:
182: public Map queryForMap(String sql, SqlParameterSource paramSource)
183: throws DataAccessException {
184: return (Map) queryForObject(sql, paramSource,
185: new ColumnMapRowMapper());
186: }
187:
188: public Map queryForMap(String sql, Map paramMap)
189: throws DataAccessException {
190: return (Map) queryForObject(sql, paramMap,
191: new ColumnMapRowMapper());
192: }
193:
194: public long queryForLong(String sql, SqlParameterSource paramSource)
195: throws DataAccessException {
196: Number number = (Number) queryForObject(sql, paramSource,
197: Number.class);
198: return (number != null ? number.longValue() : 0);
199: }
200:
201: public long queryForLong(String sql, Map paramMap)
202: throws DataAccessException {
203: return queryForLong(sql, new MapSqlParameterSource(paramMap));
204: }
205:
206: public int queryForInt(String sql, SqlParameterSource paramSource)
207: throws DataAccessException {
208: Number number = (Number) queryForObject(sql, paramSource,
209: Number.class);
210: return (number != null ? number.intValue() : 0);
211: }
212:
213: public int queryForInt(String sql, Map paramMap)
214: throws DataAccessException {
215: return queryForInt(sql, new MapSqlParameterSource(paramMap));
216: }
217:
218: public List queryForList(String sql,
219: SqlParameterSource paramSource, Class elementType)
220: throws DataAccessException {
221: return query(sql, paramSource, new SingleColumnRowMapper(
222: elementType));
223: }
224:
225: public List queryForList(String sql, Map paramMap, Class elementType)
226: throws DataAccessException {
227: return queryForList(sql, new MapSqlParameterSource(paramMap),
228: elementType);
229: }
230:
231: public List queryForList(String sql, SqlParameterSource paramSource)
232: throws DataAccessException {
233: return query(sql, paramSource, new ColumnMapRowMapper());
234: }
235:
236: public List queryForList(String sql, Map paramMap)
237: throws DataAccessException {
238: return queryForList(sql, new MapSqlParameterSource(paramMap));
239: }
240:
241: public SqlRowSet queryForRowSet(String sql,
242: SqlParameterSource paramSource) throws DataAccessException {
243: return (SqlRowSet) getJdbcOperations().query(
244: getPreparedStatementCreator(sql, paramSource),
245: new SqlRowSetResultSetExtractor());
246: }
247:
248: public SqlRowSet queryForRowSet(String sql, Map paramMap)
249: throws DataAccessException {
250: return queryForRowSet(sql, new MapSqlParameterSource(paramMap));
251: }
252:
253: public int update(String sql, SqlParameterSource paramSource)
254: throws DataAccessException {
255: return getJdbcOperations().update(
256: getPreparedStatementCreator(sql, paramSource));
257: }
258:
259: public int update(String sql, Map paramMap)
260: throws DataAccessException {
261: return update(sql, new MapSqlParameterSource(paramMap));
262: }
263:
264: public int update(String sql, SqlParameterSource paramSource,
265: KeyHolder generatedKeyHolder) throws DataAccessException {
266:
267: return update(sql, paramSource, generatedKeyHolder, null);
268: }
269:
270: public int update(String sql, SqlParameterSource paramSource,
271: KeyHolder generatedKeyHolder, String[] keyColumnNames)
272: throws DataAccessException {
273:
274: ParsedSql parsedSql = getParsedSql(sql);
275: String sqlToUse = NamedParameterUtils
276: .substituteNamedParameters(parsedSql, paramSource);
277: Object[] params = NamedParameterUtils.buildValueArray(
278: parsedSql, paramSource, null);
279: int[] paramTypes = NamedParameterUtils.buildSqlTypeArray(
280: parsedSql, paramSource);
281: PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(
282: sqlToUse, paramTypes);
283: if (keyColumnNames != null) {
284: pscf.setGeneratedKeysColumnNames(keyColumnNames);
285: } else {
286: pscf.setReturnGeneratedKeys(true);
287: }
288: return getJdbcOperations().update(
289: pscf.newPreparedStatementCreator(params),
290: generatedKeyHolder);
291: }
292:
293: /**
294: * Build a PreparedStatementCreator based on the given SQL and named parameters.
295: * <p>Note: Not used for the <code>update</code> variant with generated key handling.
296: * @param sql SQL to execute
297: * @param paramSource container of arguments to bind
298: * @return the corresponding PreparedStatementCreator
299: */
300: protected PreparedStatementCreator getPreparedStatementCreator(
301: String sql, SqlParameterSource paramSource) {
302: ParsedSql parsedSql = getParsedSql(sql);
303: String sqlToUse = NamedParameterUtils
304: .substituteNamedParameters(parsedSql, paramSource);
305: Object[] params = NamedParameterUtils.buildValueArray(
306: parsedSql, paramSource, null);
307: int[] paramTypes = NamedParameterUtils.buildSqlTypeArray(
308: parsedSql, paramSource);
309: PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(
310: sqlToUse, paramTypes);
311: return pscf.newPreparedStatementCreator(params);
312: }
313:
314: /**
315: * Obtain a parsed representation of the given SQL statement.
316: * @param sql the original SQL
317: * @return a representation of the parsed SQL statement
318: */
319: protected ParsedSql getParsedSql(String sql) {
320: synchronized (this .parsedSqlCache) {
321: ParsedSql parsedSql = (ParsedSql) this.parsedSqlCache
322: .get(sql);
323: if (parsedSql == null) {
324: parsedSql = NamedParameterUtils.parseSqlStatement(sql);
325: this.parsedSqlCache.put(sql, parsedSql);
326: }
327: return parsedSql;
328: }
329: }
330:
331: }
|