001/* 002 * Copyright 2002-2012 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 * https://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 017package org.springframework.jdbc.object; 018 019import java.util.Map; 020import javax.sql.DataSource; 021 022import org.springframework.dao.DataAccessException; 023import org.springframework.dao.InvalidDataAccessApiUsageException; 024import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException; 025import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 026import org.springframework.jdbc.core.namedparam.NamedParameterUtils; 027import org.springframework.jdbc.core.namedparam.ParsedSql; 028import 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} methods, 034 * analogous to the {@code execute} 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} classes that ship with the Spring 041 * Framework, {@code SqlQuery} 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 */ 050public 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 /** 066 * Constructor to allow use as a JavaBean. DataSource and SQL 067 * must be supplied before compilation and use. 068 * @see #setDataSource 069 * @see #setSql 070 */ 071 public SqlUpdate() { 072 } 073 074 /** 075 * Constructs an update object with a given DataSource and SQL. 076 * @param ds DataSource to use to obtain connections 077 * @param sql SQL statement to execute 078 */ 079 public SqlUpdate(DataSource ds, String sql) { 080 setDataSource(ds); 081 setSql(sql); 082 } 083 084 /** 085 * Construct an update object with a given DataSource, SQL 086 * and anonymous parameters. 087 * @param ds DataSource to use to obtain connections 088 * @param sql SQL statement to execute 089 * @param types SQL types of the parameters, as defined in the 090 * {@code java.sql.Types} class 091 * @see java.sql.Types 092 */ 093 public SqlUpdate(DataSource ds, String sql, int[] types) { 094 setDataSource(ds); 095 setSql(sql); 096 setTypes(types); 097 } 098 099 /** 100 * Construct an update object with a given DataSource, SQL, 101 * anonymous parameters and specifying the maximum number of rows 102 * that may be affected. 103 * @param ds DataSource to use to obtain connections 104 * @param sql SQL statement to execute 105 * @param types SQL types of the parameters, as defined in the 106 * {@code java.sql.Types} class 107 * @param maxRowsAffected the maximum number of rows that may 108 * be affected by the update 109 * @see java.sql.Types 110 */ 111 public SqlUpdate(DataSource ds, String sql, int[] types, int maxRowsAffected) { 112 setDataSource(ds); 113 setSql(sql); 114 setTypes(types); 115 this.maxRowsAffected = maxRowsAffected; 116 } 117 118 119 /** 120 * Set the maximum number of rows that may be affected by this update. 121 * The default value is 0, which does not limit the number of rows affected. 122 * @param maxRowsAffected the maximum number of rows that can be affected by 123 * this update without this class's update method considering it an error 124 */ 125 public void setMaxRowsAffected(int maxRowsAffected) { 126 this.maxRowsAffected = maxRowsAffected; 127 } 128 129 /** 130 * Set the <i>exact</i> number of rows that must be affected by this update. 131 * The default value is 0, which allows any number of rows to be affected. 132 * <p>This is an alternative to setting the <i>maximum</i> number of rows 133 * that may be affected. 134 * @param requiredRowsAffected the exact number of rows that must be affected 135 * by this update without this class's update method considering it an error 136 */ 137 public void setRequiredRowsAffected(int requiredRowsAffected) { 138 this.requiredRowsAffected = requiredRowsAffected; 139 } 140 141 /** 142 * Check the given number of affected rows against the 143 * specified maximum number or required number. 144 * @param rowsAffected the number of affected rows 145 * @throws JdbcUpdateAffectedIncorrectNumberOfRowsException 146 * if the actually affected rows are out of bounds 147 * @see #setMaxRowsAffected 148 * @see #setRequiredRowsAffected 149 */ 150 protected void checkRowsAffected(int rowsAffected) throws JdbcUpdateAffectedIncorrectNumberOfRowsException { 151 if (this.maxRowsAffected > 0 && rowsAffected > this.maxRowsAffected) { 152 throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(getSql(), this.maxRowsAffected, rowsAffected); 153 } 154 if (this.requiredRowsAffected > 0 && rowsAffected != this.requiredRowsAffected) { 155 throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(getSql(), this.requiredRowsAffected, rowsAffected); 156 } 157 } 158 159 160 /** 161 * Generic method to execute the update given parameters. 162 * All other update methods invoke this method. 163 * @param params array of parameters objects 164 * @return the number of rows affected by the update 165 */ 166 public int update(Object... params) throws DataAccessException { 167 validateParameters(params); 168 int rowsAffected = getJdbcTemplate().update(newPreparedStatementCreator(params)); 169 checkRowsAffected(rowsAffected); 170 return rowsAffected; 171 } 172 173 /** 174 * Method to execute the update given arguments and 175 * retrieve the generated keys using a KeyHolder. 176 * @param params array of parameter objects 177 * @param generatedKeyHolder KeyHolder that will hold the generated keys 178 * @return the number of rows affected by the update 179 */ 180 public int update(Object[] params, KeyHolder generatedKeyHolder) throws DataAccessException { 181 if (!isReturnGeneratedKeys() && getGeneratedKeysColumnNames() == null) { 182 throw new InvalidDataAccessApiUsageException( 183 "The update method taking a KeyHolder should only be used when generated keys have " + 184 "been configured by calling either 'setReturnGeneratedKeys' or " + 185 "'setGeneratedKeysColumnNames'."); 186 } 187 validateParameters(params); 188 int rowsAffected = getJdbcTemplate().update(newPreparedStatementCreator(params), generatedKeyHolder); 189 checkRowsAffected(rowsAffected); 190 return rowsAffected; 191 } 192 193 /** 194 * Convenience method to execute an update with no parameters. 195 */ 196 public int update() throws DataAccessException { 197 return update((Object[]) null); 198 } 199 200 /** 201 * Convenient method to execute an update given one int arg. 202 */ 203 public int update(int p1) throws DataAccessException { 204 return update(new Object[] {p1}); 205 } 206 207 /** 208 * Convenient method to execute an update given two int args. 209 */ 210 public int update(int p1, int p2) throws DataAccessException { 211 return update(new Object[] {p1, p2}); 212 } 213 214 /** 215 * Convenient method to execute an update given one long arg. 216 */ 217 public int update(long p1) throws DataAccessException { 218 return update(new Object[] {p1}); 219 } 220 221 /** 222 * Convenient method to execute an update given two long args. 223 */ 224 public int update(long p1, long p2) throws DataAccessException { 225 return update(new Object[] {p1, p2}); 226 } 227 228 /** 229 * Convenient method to execute an update given one String arg. 230 */ 231 public int update(String p) throws DataAccessException { 232 return update(new Object[] {p}); 233 } 234 235 /** 236 * Convenient method to execute an update given two String args. 237 */ 238 public int update(String p1, String p2) throws DataAccessException { 239 return update(new Object[] {p1, p2}); 240 } 241 242 /** 243 * Generic method to execute the update given named parameters. 244 * All other update methods invoke this method. 245 * @param paramMap Map of parameter name to parameter object, 246 * matching named parameters specified in the SQL statement 247 * @return the number of rows affected by the update 248 */ 249 public int updateByNamedParam(Map<String, ?> paramMap) throws DataAccessException { 250 validateNamedParameters(paramMap); 251 ParsedSql parsedSql = getParsedSql(); 252 MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap); 253 String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource); 254 Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters()); 255 int rowsAffected = getJdbcTemplate().update(newPreparedStatementCreator(sqlToUse, params)); 256 checkRowsAffected(rowsAffected); 257 return rowsAffected; 258 } 259 260 /** 261 * Method to execute the update given arguments and 262 * retrieve the generated keys using a KeyHolder. 263 * @param paramMap Map of parameter name to parameter object, 264 * matching named parameters specified in the SQL statement 265 * @param generatedKeyHolder KeyHolder that will hold the generated keys 266 * @return the number of rows affected by the update 267 */ 268 public int updateByNamedParam(Map<String, ?> paramMap, KeyHolder generatedKeyHolder) throws DataAccessException { 269 validateNamedParameters(paramMap); 270 ParsedSql parsedSql = getParsedSql(); 271 MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap); 272 String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource); 273 Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters()); 274 int rowsAffected = getJdbcTemplate().update(newPreparedStatementCreator(sqlToUse, params), generatedKeyHolder); 275 checkRowsAffected(rowsAffected); 276 return rowsAffected; 277 } 278 279}