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.object;
018:
019: import java.util.Map;
020:
021: import javax.sql.DataSource;
022:
023: import org.springframework.dao.DataAccessException;
024: import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
025: import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
026: import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
027: import org.springframework.jdbc.core.namedparam.ParsedSql;
028: import org.springframework.jdbc.support.KeyHolder;
029:
030: /**
031: * Reusable operation object representing a SQL update.
032: *
033: * <p>This class provides a number of <code>update</code> methods,
034: * analogous to the <code>execute</code> methods of query objects.
035: *
036: * <p>This class is concrete. Although it can be subclassed (for example
037: * to add a custom update method) it can easily be parameterized by setting
038: * SQL and declaring parameters.
039: *
040: * <p>Like all <code>RdbmsOperation</code> classes that ship with the Spring
041: * Framework, <code>SqlQuery</code> instances are thread-safe after their
042: * initialization is complete. That is, after they are constructed and configured
043: * via their setter methods, they can be used safely from multiple threads.
044: *
045: * @author Rod Johnson
046: * @author Thomas Risberg
047: * @author Juergen Hoeller
048: * @see SqlQuery
049: */
050: public class SqlUpdate extends SqlOperation {
051:
052: /**
053: * Maximum number of rows the update may affect. If more are
054: * affected, an exception will be thrown. Ignored if 0.
055: */
056: private int maxRowsAffected = 0;
057:
058: /**
059: * An exact number of rows that must be affected.
060: * Ignored if 0.
061: */
062: private int requiredRowsAffected = 0;
063:
064: /**
065: * Constructor to allow use as a JavaBean. DataSource and SQL
066: * must be supplied before compilation and use.
067: * @see #setDataSource
068: * @see #setSql
069: */
070: public SqlUpdate() {
071: }
072:
073: /**
074: * Constructs an update object with a given DataSource and SQL.
075: * @param ds DataSource to use to obtain connections
076: * @param sql SQL statement to execute
077: */
078: public SqlUpdate(DataSource ds, String sql) {
079: setDataSource(ds);
080: setSql(sql);
081: }
082:
083: /**
084: * Construct an update object with a given DataSource, SQL
085: * and anonymous parameters.
086: * @param ds DataSource to use to obtain connections
087: * @param sql SQL statement to execute
088: * @param types SQL types of the parameters, as defined in the
089: * <code>java.sql.Types</code> class
090: * @see java.sql.Types
091: */
092: public SqlUpdate(DataSource ds, String sql, int[] types) {
093: setDataSource(ds);
094: setSql(sql);
095: setTypes(types);
096: }
097:
098: /**
099: * Construct an update object with a given DataSource, SQL,
100: * anonymous parameters and specifying the maximum number of rows
101: * that may be affected.
102: * @param ds DataSource to use to obtain connections
103: * @param sql SQL statement to execute
104: * @param types SQL types of the parameters, as defined in the
105: * <code>java.sql.Types</code> class
106: * @param maxRowsAffected the maximum number of rows that may
107: * be affected by the update
108: * @see java.sql.Types
109: */
110: public SqlUpdate(DataSource ds, String sql, int[] types,
111: int maxRowsAffected) {
112: setDataSource(ds);
113: setSql(sql);
114: setTypes(types);
115: this .maxRowsAffected = maxRowsAffected;
116: }
117:
118: /**
119: * Set the maximum number of rows that may be affected by this update.
120: * The default value is 0, which does not limit the number of rows affected.
121: * @param maxRowsAffected the maximum number of rows that can be affected by
122: * this update without this class's update method considering it an error
123: */
124: public void setMaxRowsAffected(int maxRowsAffected) {
125: this .maxRowsAffected = maxRowsAffected;
126: }
127:
128: /**
129: * Set the <i>exact</i> number of rows that must be affected by this update.
130: * The default value is 0, which allows any number of rows to be affected.
131: * <p>This is an alternative to setting the <i>maximum</i> number of rows
132: * that may be affected.
133: * @param requiredRowsAffected the exact number of rows that must be affected
134: * by this update without this class's update method considering it an error
135: */
136: public void setRequiredRowsAffected(int requiredRowsAffected) {
137: this .requiredRowsAffected = requiredRowsAffected;
138: }
139:
140: /**
141: * Check the given number of affected rows against the
142: * specified maximum number or required number.
143: * @param rowsAffected the number of affected rows
144: * @throws JdbcUpdateAffectedIncorrectNumberOfRowsException
145: * if the actually affected rows are out of bounds
146: * @see #setMaxRowsAffected
147: * @see #setRequiredRowsAffected
148: */
149: protected void checkRowsAffected(int rowsAffected)
150: throws JdbcUpdateAffectedIncorrectNumberOfRowsException {
151: if (this .maxRowsAffected > 0
152: && rowsAffected > this .maxRowsAffected) {
153: throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
154: getSql(), this .maxRowsAffected, rowsAffected);
155: }
156: if (this .requiredRowsAffected > 0
157: && rowsAffected != this .requiredRowsAffected) {
158: throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
159: getSql(), this .requiredRowsAffected, rowsAffected);
160: }
161: }
162:
163: /**
164: * Generic method to execute the update given parameters.
165: * All other update methods invoke this method.
166: * @param params array of parameters objects
167: * @return the number of rows affected by the update
168: */
169: public int update(Object[] params) throws DataAccessException {
170: validateParameters(params);
171: int rowsAffected = getJdbcTemplate().update(
172: newPreparedStatementCreator(params));
173: checkRowsAffected(rowsAffected);
174: return rowsAffected;
175: }
176:
177: /**
178: * Method to execute the update given arguments and
179: * retrieve the generated keys using a KeyHolder.
180: * @param params array of parameter objects
181: * @param generatedKeyHolder KeyHolder that will hold the generated keys
182: * @return the number of rows affected by the update
183: */
184: public int update(Object[] params, KeyHolder generatedKeyHolder)
185: throws DataAccessException {
186: validateParameters(params);
187: int rowsAffected = getJdbcTemplate()
188: .update(newPreparedStatementCreator(params),
189: generatedKeyHolder);
190: checkRowsAffected(rowsAffected);
191: return rowsAffected;
192: }
193:
194: /**
195: * Convenience method to execute an update with no parameters.
196: */
197: public int update() throws DataAccessException {
198: return update((Object[]) null);
199: }
200:
201: /**
202: * Convenient method to execute an update given one int arg.
203: */
204: public int update(int p1) throws DataAccessException {
205: return update(new Object[] { new Integer(p1) });
206: }
207:
208: /**
209: * Convenient method to execute an update given two int args.
210: */
211: public int update(int p1, int p2) throws DataAccessException {
212: return update(new Object[] { new Integer(p1), new Integer(p2) });
213: }
214:
215: /**
216: * Convenient method to execute an update given one long arg.
217: */
218: public int update(long p1) throws DataAccessException {
219: return update(new Object[] { new Long(p1) });
220: }
221:
222: /**
223: * Convenient method to execute an update given two long args.
224: */
225: public int update(long p1, long p2) throws DataAccessException {
226: return update(new Object[] { new Long(p1), new Long(p2) });
227: }
228:
229: /**
230: * Convenient method to execute an update given one String arg.
231: */
232: public int update(String p) throws DataAccessException {
233: return update(new Object[] { p });
234: }
235:
236: /**
237: * Convenient method to execute an update given two String args.
238: */
239: public int update(String p1, String p2) throws DataAccessException {
240: return update(new Object[] { p1, p2 });
241: }
242:
243: /**
244: * Generic method to execute the update given named parameters.
245: * All other update methods invoke this method.
246: * @param paramMap Map of parameter name to parameter object,
247: * matching named parameters specified in the SQL statement
248: * @return the number of rows affected by the update
249: */
250: public int updateByNamedParam(Map paramMap)
251: throws DataAccessException {
252: validateNamedParameters(paramMap);
253: ParsedSql parsedSql = getParsedSql();
254: MapSqlParameterSource paramSource = new MapSqlParameterSource(
255: paramMap);
256: String sqlToUse = NamedParameterUtils
257: .substituteNamedParameters(parsedSql, paramSource);
258: Object[] params = NamedParameterUtils.buildValueArray(
259: parsedSql, paramSource, getDeclaredParameters());
260: int rowsAffected = getJdbcTemplate().update(
261: newPreparedStatementCreator(sqlToUse, params));
262: checkRowsAffected(rowsAffected);
263: return rowsAffected;
264: }
265:
266: /**
267: * Method to execute the update given arguments and
268: * retrieve the generated keys using a KeyHolder.
269: * @param paramMap Map of parameter name to parameter object,
270: * matching named parameters specified in the SQL statement
271: * @param generatedKeyHolder KeyHolder that will hold the generated keys
272: * @return the number of rows affected by the update
273: */
274: public int updateByNamedParam(Map paramMap,
275: KeyHolder generatedKeyHolder) throws DataAccessException {
276: validateNamedParameters(paramMap);
277: ParsedSql parsedSql = getParsedSql();
278: MapSqlParameterSource paramSource = new MapSqlParameterSource(
279: paramMap);
280: String sqlToUse = NamedParameterUtils
281: .substituteNamedParameters(parsedSql, paramSource);
282: Object[] params = NamedParameterUtils.buildValueArray(
283: parsedSql, paramSource, getDeclaredParameters());
284: int rowsAffected = getJdbcTemplate().update(
285: newPreparedStatementCreator(sqlToUse, params),
286: generatedKeyHolder);
287: checkRowsAffected(rowsAffected);
288: return rowsAffected;
289: }
290:
291: }
|