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 */ 017package org.apache.commons.dbutils; 018 019import static java.sql.DriverManager.registerDriver; 020 021import java.io.PrintWriter; 022import java.lang.reflect.Constructor; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.sql.Connection; 026import java.sql.Driver; 027import java.sql.DriverPropertyInfo; 028import java.sql.ResultSet; 029import java.sql.SQLException; 030import java.sql.SQLFeatureNotSupportedException; 031import java.sql.Statement; 032import java.util.logging.Logger; 033import java.util.Properties; 034 035/** 036 * A collection of JDBC helper methods. This class is thread safe. 037 */ 038public final class DbUtils { 039 040 /** 041 * Default constructor. 042 * 043 * Utility classes should not have a public or default constructor, 044 * but this one preserves retro-compatibility. 045 * 046 * @since 1.4 047 */ 048 public DbUtils() { 049 // do nothing 050 } 051 052 /** 053 * Close a {@code Connection}, avoid closing if null. 054 * 055 * @param conn Connection to close. 056 * @throws SQLException if a database access error occurs 057 */ 058 public static void close(final Connection conn) throws SQLException { 059 if (conn != null) { 060 conn.close(); 061 } 062 } 063 064 /** 065 * Close a {@code ResultSet}, avoid closing if null. 066 * 067 * @param rs ResultSet to close. 068 * @throws SQLException if a database access error occurs 069 */ 070 public static void close(final ResultSet rs) throws SQLException { 071 if (rs != null) { 072 rs.close(); 073 } 074 } 075 076 /** 077 * Close a {@code Statement}, avoid closing if null. 078 * 079 * @param stmt Statement to close. 080 * @throws SQLException if a database access error occurs 081 */ 082 public static void close(final Statement stmt) throws SQLException { 083 if (stmt != null) { 084 stmt.close(); 085 } 086 } 087 088 /** 089 * Close a {@code Connection}, avoid closing if null and hide 090 * any SQLExceptions that occur. 091 * 092 * @param conn Connection to close. 093 */ 094 public static void closeQuietly(final Connection conn) { 095 try { 096 close(conn); 097 } catch (final SQLException e) { // NOPMD 098 // quiet 099 } 100 } 101 102 /** 103 * Close a {@code Connection}, {@code Statement} and 104 * {@code ResultSet}. Avoid closing if null and hide any 105 * SQLExceptions that occur. 106 * 107 * @param conn Connection to close. 108 * @param stmt Statement to close. 109 * @param rs ResultSet to close. 110 */ 111 public static void closeQuietly(final Connection conn, final Statement stmt, 112 final ResultSet rs) { 113 114 try { 115 closeQuietly(rs); 116 } finally { 117 try { 118 closeQuietly(stmt); 119 } finally { 120 closeQuietly(conn); 121 } 122 } 123 124 } 125 126 /** 127 * Close a {@code ResultSet}, avoid closing if null and hide any 128 * SQLExceptions that occur. 129 * 130 * @param rs ResultSet to close. 131 */ 132 public static void closeQuietly(final ResultSet rs) { 133 try { 134 close(rs); 135 } catch (final SQLException e) { // NOPMD 136 // quiet 137 } 138 } 139 140 /** 141 * Close a {@code Statement}, avoid closing if null and hide 142 * any SQLExceptions that occur. 143 * 144 * @param stmt Statement to close. 145 */ 146 public static void closeQuietly(final Statement stmt) { 147 try { 148 close(stmt); 149 } catch (final SQLException e) { // NOPMD 150 // quiet 151 } 152 } 153 154 /** 155 * Commits a {@code Connection} then closes it, avoid closing if null. 156 * 157 * @param conn Connection to close. 158 * @throws SQLException if a database access error occurs 159 */ 160 public static void commitAndClose(final Connection conn) throws SQLException { 161 if (conn != null) { 162 try { 163 conn.commit(); 164 } finally { 165 conn.close(); 166 } 167 } 168 } 169 170 /** 171 * Commits a {@code Connection} then closes it, avoid closing if null 172 * and hide any SQLExceptions that occur. 173 * 174 * @param conn Connection to close. 175 */ 176 public static void commitAndCloseQuietly(final Connection conn) { 177 try { 178 commitAndClose(conn); 179 } catch (final SQLException e) { // NOPMD 180 // quiet 181 } 182 } 183 184 /** 185 * Loads and registers a database driver class. 186 * If this succeeds, it returns true, else it returns false. 187 * 188 * @param driverClassName of driver to load 189 * @return boolean {@code true} if the driver was found, otherwise {@code false} 190 */ 191 public static boolean loadDriver(final String driverClassName) { 192 return loadDriver(DbUtils.class.getClassLoader(), driverClassName); 193 } 194 195 /** 196 * Loads and registers a database driver class. 197 * If this succeeds, it returns true, else it returns false. 198 * 199 * @param classLoader the class loader used to load the driver class 200 * @param driverClassName of driver to load 201 * @return boolean {@code true} if the driver was found, otherwise {@code false} 202 * @since 1.4 203 */ 204 public static boolean loadDriver(final ClassLoader classLoader, final String driverClassName) { 205 try { 206 final Class<?> loadedClass = classLoader.loadClass(driverClassName); 207 208 if (!Driver.class.isAssignableFrom(loadedClass)) { 209 return false; 210 } 211 212 @SuppressWarnings("unchecked") // guarded by previous check 213 final 214 Class<Driver> driverClass = (Class<Driver>) loadedClass; 215 final Constructor<Driver> driverConstructor = driverClass.getConstructor(); 216 217 // make Constructor accessible if it is private 218 @SuppressWarnings("deprecation") 219 // TODO This is deprecated in Java9 and canAccess() should be used. Adding suppression for building on 220 // later JDKs without a warning. 221 final boolean isConstructorAccessible = driverConstructor.isAccessible(); 222 if (!isConstructorAccessible) { 223 driverConstructor.setAccessible(true); 224 } 225 226 try { 227 final Driver driver = driverConstructor.newInstance(); 228 registerDriver(new DriverProxy(driver)); 229 } finally { 230 driverConstructor.setAccessible(isConstructorAccessible); 231 } 232 233 return true; 234 } catch (final RuntimeException e) { 235 return false; 236 } catch (final Exception e) { 237 return false; 238 } 239 } 240 241 /** 242 * Print the stack trace for a SQLException to STDERR. 243 * 244 * @param e SQLException to print stack trace of 245 */ 246 public static void printStackTrace(final SQLException e) { 247 printStackTrace(e, new PrintWriter(System.err)); 248 } 249 250 /** 251 * Print the stack trace for a SQLException to a 252 * specified PrintWriter. 253 * 254 * @param e SQLException to print stack trace of 255 * @param pw PrintWriter to print to 256 */ 257 public static void printStackTrace(final SQLException e, final PrintWriter pw) { 258 259 SQLException next = e; 260 while (next != null) { 261 next.printStackTrace(pw); 262 next = next.getNextException(); 263 if (next != null) { 264 pw.println("Next SQLException:"); 265 } 266 } 267 } 268 269 /** 270 * Print warnings on a Connection to STDERR. 271 * 272 * @param conn Connection to print warnings from 273 */ 274 public static void printWarnings(final Connection conn) { 275 printWarnings(conn, new PrintWriter(System.err)); 276 } 277 278 /** 279 * Print warnings on a Connection to a specified PrintWriter. 280 * 281 * @param conn Connection to print warnings from 282 * @param pw PrintWriter to print to 283 */ 284 public static void printWarnings(final Connection conn, final PrintWriter pw) { 285 if (conn != null) { 286 try { 287 printStackTrace(conn.getWarnings(), pw); 288 } catch (final SQLException e) { 289 printStackTrace(e, pw); 290 } 291 } 292 } 293 294 /** 295 * Rollback any changes made on the given connection. 296 * @param conn Connection to rollback. A null value is legal. 297 * @throws SQLException if a database access error occurs 298 */ 299 public static void rollback(final Connection conn) throws SQLException { 300 if (conn != null) { 301 conn.rollback(); 302 } 303 } 304 305 /** 306 * Performs a rollback on the {@code Connection} then closes it, 307 * avoid closing if null. 308 * 309 * @param conn Connection to rollback. A null value is legal. 310 * @throws SQLException if a database access error occurs 311 * @since DbUtils 1.1 312 */ 313 public static void rollbackAndClose(final Connection conn) throws SQLException { 314 if (conn != null) { 315 try { 316 conn.rollback(); 317 } finally { 318 conn.close(); 319 } 320 } 321 } 322 323 /** 324 * Performs a rollback on the {@code Connection} then closes it, 325 * avoid closing if null and hide any SQLExceptions that occur. 326 * 327 * @param conn Connection to rollback. A null value is legal. 328 * @since DbUtils 1.1 329 */ 330 public static void rollbackAndCloseQuietly(final Connection conn) { 331 try { 332 rollbackAndClose(conn); 333 } catch (final SQLException e) { // NOPMD 334 // quiet 335 } 336 } 337 338 /** 339 * Simple {@link Driver} proxy class that proxies a JDBC Driver loaded dynamically. 340 * 341 * @since 1.6 342 */ 343 static final class DriverProxy implements Driver { 344 345 private boolean parentLoggerSupported = true; 346 347 /** 348 * The adapted JDBC Driver loaded dynamically. 349 */ 350 private final Driver adapted; 351 352 /** 353 * Creates a new JDBC Driver that adapts a JDBC Driver loaded dynamically. 354 * 355 * @param adapted the adapted JDBC Driver loaded dynamically. 356 */ 357 public DriverProxy(final Driver adapted) { 358 this.adapted = adapted; 359 } 360 361 /** 362 * {@inheritDoc} 363 */ 364 @Override 365 public boolean acceptsURL(final String url) throws SQLException { 366 return adapted.acceptsURL(url); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public Connection connect(final String url, final Properties info) throws SQLException { 374 return adapted.connect(url, info); 375 } 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override 381 public int getMajorVersion() { 382 return adapted.getMajorVersion(); 383 } 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override 389 public int getMinorVersion() { 390 return adapted.getMinorVersion(); 391 } 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override 397 public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) throws SQLException { 398 return adapted.getPropertyInfo(url, info); 399 } 400 401 /** 402 * {@inheritDoc} 403 */ 404 @Override 405 public boolean jdbcCompliant() { 406 return adapted.jdbcCompliant(); 407 } 408 409 /** 410 * Java 1.7 method. 411 */ 412 @Override 413 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 414 if (parentLoggerSupported) { 415 try { 416 final Method method = adapted.getClass().getMethod("getParentLogger"); 417 return (Logger)method.invoke(adapted); 418 } catch (final NoSuchMethodException e) { 419 parentLoggerSupported = false; 420 throw new SQLFeatureNotSupportedException(e); 421 } catch (final IllegalAccessException e) { 422 parentLoggerSupported = false; 423 throw new SQLFeatureNotSupportedException(e); 424 } catch (final InvocationTargetException e) { 425 parentLoggerSupported = false; 426 throw new SQLFeatureNotSupportedException(e); 427 } 428 } 429 throw new SQLFeatureNotSupportedException(); 430 } 431 432 } 433 434}