001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.dbutils; 018 019 import java.beans.IntrospectionException; 020 import java.beans.Introspector; 021 import java.beans.PropertyDescriptor; 022 import java.lang.reflect.InvocationTargetException; 023 import java.lang.reflect.Method; 024 import java.sql.Connection; 025 import java.sql.ParameterMetaData; 026 import java.sql.PreparedStatement; 027 import java.sql.ResultSet; 028 import java.sql.SQLException; 029 import java.sql.Statement; 030 import java.sql.Types; 031 import java.util.Arrays; 032 033 import javax.sql.DataSource; 034 035 /** 036 * The base class for QueryRunner & AsyncQueryRunner. 037 * This class is thread safe. 038 * @since 1.4 (mostly extracted from QueryRunner) 039 */ 040 public abstract class AbstractQueryRunner { 041 /** 042 * Is {@link ParameterMetaData#getParameterType(int)} broken (have we tried it yet)? 043 */ 044 private volatile boolean pmdKnownBroken = false; 045 046 /** 047 * The DataSource to retrieve connections from. 048 */ 049 protected final DataSource ds; 050 051 /** 052 * Default constructor, sets pmdKnownBroken to false and ds to null. 053 */ 054 public AbstractQueryRunner() { 055 ds = null; 056 } 057 058 /** 059 * Constructor to allow workaround for Oracle drivers 060 * @param pmdKnownBroken Oracle drivers don't support {@link ParameterMetaData#getParameterType(int) }; 061 * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it, 062 * and if it breaks, we'll remember not to use it again. 063 */ 064 public AbstractQueryRunner(boolean pmdKnownBroken) { 065 this.pmdKnownBroken = pmdKnownBroken; 066 ds = null; 067 } 068 069 /** 070 * Constructor to provide a <code>DataSource</code>. 071 * Methods that do not take a <code>Connection</code> parameter will 072 * retrieve connections from this <code>DataSource</code>. 073 * 074 * @param ds The <code>DataSource</code> to retrieve connections from. 075 */ 076 public AbstractQueryRunner(DataSource ds) { 077 this.ds = ds; 078 } 079 080 /** 081 * Constructor to allow workaround for Oracle drivers. Methods that do not take a 082 * <code>Connection</code> parameter will retrieve connections from this 083 * <code>DataSource</code>. 084 * 085 * @param ds The <code>DataSource</code> to retrieve connections from. 086 * @param pmdKnownBroken Oracle drivers don't support {@link ParameterMetaData#getParameterType(int) }; 087 * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it, 088 * and if it breaks, we'll remember not to use it again. 089 */ 090 public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken) { 091 this.pmdKnownBroken = pmdKnownBroken; 092 this.ds = ds; 093 } 094 095 /** 096 * Returns the <code>DataSource</code> this runner is using. 097 * <code>QueryRunner</code> methods always call this method to get the 098 * <code>DataSource</code> so subclasses can provide specialized 099 * behavior. 100 * 101 * @return DataSource the runner is using 102 */ 103 public DataSource getDataSource() { 104 return this.ds; 105 } 106 107 /** 108 * Oracle drivers don't support {@link ParameterMetaData#getParameterType(int) }; 109 * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it, 110 * and if it breaks, we'll remember not to use it again. 111 * 112 * @return the flag to skip (or not) {@link ParameterMetaData#getParameterType(int) } 113 * @since 1.4 114 */ 115 public boolean isPmdKnownBroken() { 116 return pmdKnownBroken; 117 } 118 119 /** 120 * Factory method that creates and initializes a 121 * <code>PreparedStatement</code> object for the given SQL. 122 * <code>QueryRunner</code> methods always call this method to prepare 123 * statements for them. Subclasses can override this method to provide 124 * special PreparedStatement configuration if needed. This implementation 125 * simply calls <code>conn.prepareStatement(sql)</code>. 126 * 127 * @param conn The <code>Connection</code> used to create the 128 * <code>PreparedStatement</code> 129 * @param sql The SQL statement to prepare. 130 * @return An initialized <code>PreparedStatement</code>. 131 * @throws SQLException if a database access error occurs 132 */ 133 protected PreparedStatement prepareStatement(Connection conn, String sql) 134 throws SQLException { 135 136 return conn.prepareStatement(sql); 137 } 138 139 /** 140 * Factory method that creates and initializes a 141 * <code>Connection</code> object. <code>QueryRunner</code> methods 142 * always call this method to retrieve connections from its DataSource. 143 * Subclasses can override this method to provide 144 * special <code>Connection</code> configuration if needed. This 145 * implementation simply calls <code>ds.getConnection()</code>. 146 * 147 * @return An initialized <code>Connection</code>. 148 * @throws SQLException if a database access error occurs 149 * @since DbUtils 1.1 150 */ 151 protected Connection prepareConnection() throws SQLException { 152 if (this.getDataSource() == null) { 153 throw new SQLException("QueryRunner requires a DataSource to be " + 154 "invoked in this way, or a Connection should be passed in"); 155 } 156 return this.getDataSource().getConnection(); 157 } 158 159 /** 160 * Fill the <code>PreparedStatement</code> replacement parameters with 161 * the given objects. 162 * @param stmt PreparedStatement to fill 163 * @param params Query replacement parameters; <code>null</code> is a valid 164 * value to pass in. 165 * @throws SQLException if a database access error occurs 166 */ 167 public void fillStatement(PreparedStatement stmt, Object... params) throws SQLException { 168 169 // check the parameter count, if we can 170 ParameterMetaData pmd = null; 171 if (!pmdKnownBroken) { 172 pmd = stmt.getParameterMetaData(); 173 int stmtCount = pmd.getParameterCount(); 174 int paramsCount = params == null ? 0 : params.length; 175 176 if (stmtCount != paramsCount) { 177 throw new SQLException("Wrong number of parameters: expected " 178 + stmtCount + ", was given " + paramsCount); 179 } 180 } 181 182 // nothing to do here 183 if (params == null) { 184 return; 185 } 186 187 for (int i = 0; i < params.length; i++) { 188 if (params[i] != null) { 189 stmt.setObject(i + 1, params[i]); 190 } else { 191 // VARCHAR works with many drivers regardless 192 // of the actual column type. Oddly, NULL and 193 // OTHER don't work with Oracle's drivers. 194 int sqlType = Types.VARCHAR; 195 if (!pmdKnownBroken) { 196 try { 197 sqlType = pmd.getParameterType(i + 1); 198 } catch (SQLException e) { 199 pmdKnownBroken = true; 200 } 201 } 202 stmt.setNull(i + 1, sqlType); 203 } 204 } 205 } 206 207 /** 208 * Fill the <code>PreparedStatement</code> replacement parameters with the 209 * given object's bean property values. 210 * 211 * @param stmt 212 * PreparedStatement to fill 213 * @param bean 214 * a JavaBean object 215 * @param properties 216 * an ordered array of properties; this gives the order to insert 217 * values in the statement 218 * @throws SQLException 219 * if a database access error occurs 220 */ 221 public void fillStatementWithBean(PreparedStatement stmt, Object bean, 222 PropertyDescriptor[] properties) throws SQLException { 223 Object[] params = new Object[properties.length]; 224 for (int i = 0; i < properties.length; i++) { 225 PropertyDescriptor property = properties[i]; 226 Object value = null; 227 Method method = property.getReadMethod(); 228 if (method == null) { 229 throw new RuntimeException("No read method for bean property " 230 + bean.getClass() + " " + property.getName()); 231 } 232 try { 233 value = method.invoke(bean, new Object[0]); 234 } catch (InvocationTargetException e) { 235 throw new RuntimeException("Couldn't invoke method: " + method, e); 236 } catch (IllegalArgumentException e) { 237 throw new RuntimeException("Couldn't invoke method with 0 arguments: " + method, e); 238 } catch (IllegalAccessException e) { 239 throw new RuntimeException("Couldn't invoke method: " + method, e); 240 } 241 params[i] = value; 242 } 243 fillStatement(stmt, params); 244 } 245 246 /** 247 * Fill the <code>PreparedStatement</code> replacement parameters with the 248 * given object's bean property values. 249 * 250 * @param stmt PreparedStatement to fill 251 * @param bean A JavaBean object 252 * @param propertyNames An ordered array of property names (these should match the 253 * getters/setters); this gives the order to insert values in the 254 * statement 255 * @throws SQLException If a database access error occurs 256 */ 257 public void fillStatementWithBean(PreparedStatement stmt, Object bean, String... propertyNames) throws SQLException { 258 PropertyDescriptor[] descriptors; 259 try { 260 descriptors = Introspector.getBeanInfo(bean.getClass()) 261 .getPropertyDescriptors(); 262 } catch (IntrospectionException e) { 263 throw new RuntimeException("Couldn't introspect bean " + bean.getClass().toString(), e); 264 } 265 PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length]; 266 for (int i = 0; i < propertyNames.length; i++) { 267 String propertyName = propertyNames[i]; 268 if (propertyName == null) { 269 throw new NullPointerException("propertyName can't be null: " + i); 270 } 271 boolean found = false; 272 for (int j = 0; j < descriptors.length; j++) { 273 PropertyDescriptor descriptor = descriptors[j]; 274 if (propertyName.equals(descriptor.getName())) { 275 sorted[i] = descriptor; 276 found = true; 277 break; 278 } 279 } 280 if (!found) { 281 throw new RuntimeException("Couldn't find bean property: " 282 + bean.getClass() + " " + propertyName); 283 } 284 } 285 fillStatementWithBean(stmt, bean, sorted); 286 } 287 288 /** 289 * Throws a new exception with a more informative error message. 290 * 291 * @param cause The original exception that will be chained to the new 292 * exception when it's rethrown. 293 * 294 * @param sql The query that was executing when the exception happened. 295 * 296 * @param params The query replacement parameters; <code>null</code> is a 297 * valid value to pass in. 298 * 299 * @throws SQLException if a database access error occurs 300 */ 301 protected void rethrow(SQLException cause, String sql, Object... params) 302 throws SQLException { 303 304 String causeMessage = cause.getMessage(); 305 if (causeMessage == null) { 306 causeMessage = ""; 307 } 308 StringBuffer msg = new StringBuffer(causeMessage); 309 310 msg.append(" Query: "); 311 msg.append(sql); 312 msg.append(" Parameters: "); 313 314 if (params == null) { 315 msg.append("[]"); 316 } else { 317 msg.append(Arrays.deepToString(params)); 318 } 319 320 SQLException e = new SQLException(msg.toString(), cause.getSQLState(), 321 cause.getErrorCode()); 322 e.setNextException(cause); 323 324 throw e; 325 } 326 327 /** 328 * Wrap the <code>ResultSet</code> in a decorator before processing it. 329 * This implementation returns the <code>ResultSet</code> it is given 330 * without any decoration. 331 * 332 * <p> 333 * Often, the implementation of this method can be done in an anonymous 334 * inner class like this: 335 * </p> 336 * <pre> 337 * QueryRunner run = new QueryRunner() { 338 * protected ResultSet wrap(ResultSet rs) { 339 * return StringTrimmedResultSet.wrap(rs); 340 * } 341 * }; 342 * </pre> 343 * 344 * @param rs The <code>ResultSet</code> to decorate; never 345 * <code>null</code>. 346 * @return The <code>ResultSet</code> wrapped in some decorator. 347 */ 348 protected ResultSet wrap(ResultSet rs) { 349 return rs; 350 } 351 352 /** 353 * Close a <code>Connection</code>. This implementation avoids closing if 354 * null and does <strong>not</strong> suppress any exceptions. Subclasses 355 * can override to provide special handling like logging. 356 * @param conn Connection to close 357 * @throws SQLException if a database access error occurs 358 * @since DbUtils 1.1 359 */ 360 protected void close(Connection conn) throws SQLException { 361 DbUtils.close(conn); 362 } 363 364 /** 365 * Close a <code>Statement</code>. This implementation avoids closing if 366 * null and does <strong>not</strong> suppress any exceptions. Subclasses 367 * can override to provide special handling like logging. 368 * @param stmt Statement to close 369 * @throws SQLException if a database access error occurs 370 * @since DbUtils 1.1 371 */ 372 protected void close(Statement stmt) throws SQLException { 373 DbUtils.close(stmt); 374 } 375 376 /** 377 * Close a <code>ResultSet</code>. This implementation avoids closing if 378 * null and does <strong>not</strong> suppress any exceptions. Subclasses 379 * can override to provide special handling like logging. 380 * @param rs ResultSet to close 381 * @throws SQLException if a database access error occurs 382 * @since DbUtils 1.1 383 */ 384 protected void close(ResultSet rs) throws SQLException { 385 DbUtils.close(rs); 386 } 387 388 }