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.wrappers; 018 019 import java.io.InputStream; 020 import java.io.Reader; 021 import java.lang.reflect.InvocationHandler; 022 import java.lang.reflect.Method; 023 import java.math.BigDecimal; 024 import java.net.URL; 025 import java.sql.Blob; 026 import java.sql.Clob; 027 import java.sql.Date; 028 import java.sql.Ref; 029 import java.sql.ResultSet; 030 import java.sql.Time; 031 import java.sql.Timestamp; 032 import java.util.HashMap; 033 import java.util.Map; 034 035 import org.apache.commons.dbutils.ProxyFactory; 036 037 /** 038 * Decorates a <code>ResultSet</code> with checks for a SQL NULL value on each 039 * <code>getXXX</code> method. If a column value obtained by a 040 * <code>getXXX</code> method is not SQL NULL, the column value is returned. If 041 * the column value is SQL null, an alternate value is returned. The alternate 042 * value defaults to the Java <code>null</code> value, which can be overridden 043 * for instances of the class. 044 * 045 * <p> 046 * Usage example: 047 * <blockquote> 048 * <pre> 049 * Connection conn = // somehow get a connection 050 * Statement stmt = conn.createStatement(); 051 * ResultSet rs = stmt.executeQuery("SELECT col1, col2 FROM table1"); 052 * 053 * // Wrap the result set for SQL NULL checking 054 * SqlNullCheckedResultSet wrapper = new SqlNullCheckedResultSet(rs); 055 * wrapper.setNullString("---N/A---"); // Set null string 056 * wrapper.setNullInt(-999); // Set null integer 057 * rs = ProxyFactory.instance().createResultSet(wrapper); 058 * 059 * while (rs.next()) { 060 * // If col1 is SQL NULL, value returned will be "---N/A---" 061 * String col1 = rs.getString("col1"); 062 * // If col2 is SQL NULL, value returned will be -999 063 * int col2 = rs.getInt("col2"); 064 * } 065 * rs.close(); 066 * </pre> 067 * </blockquote> 068 * </p> 069 * <p>Unlike some other classes in DbUtils, this class is NOT thread-safe.</p> 070 */ 071 public class SqlNullCheckedResultSet implements InvocationHandler { 072 073 /** 074 * Maps normal method names (ie. "getBigDecimal") to the corresponding null 075 * Method object (ie. getNullBigDecimal). 076 */ 077 private static final Map<String,Method> nullMethods = new HashMap<String,Method>(); 078 079 static { 080 Method[] methods = SqlNullCheckedResultSet.class.getMethods(); 081 for (int i = 0; i < methods.length; i++) { 082 String methodName = methods[i].getName(); 083 084 if (methodName.startsWith("getNull")) { 085 String normalName = "get" + methodName.substring(7); 086 nullMethods.put(normalName, methods[i]); 087 } 088 } 089 } 090 091 /** 092 * The factory to create proxies with. 093 */ 094 private static final ProxyFactory factory = ProxyFactory.instance(); 095 096 /** 097 * Wraps the <code>ResultSet</code> in an instance of this class. This is 098 * equivalent to: 099 * <pre> 100 * ProxyFactory.instance().createResultSet(new SqlNullCheckedResultSet(rs)); 101 * </pre> 102 * 103 * @param rs The <code>ResultSet</code> to wrap. 104 * @return wrapped ResultSet 105 */ 106 public static ResultSet wrap(ResultSet rs) { 107 return factory.createResultSet(new SqlNullCheckedResultSet(rs)); 108 } 109 110 private InputStream nullAsciiStream = null; 111 private BigDecimal nullBigDecimal = null; 112 private InputStream nullBinaryStream = null; 113 private Blob nullBlob = null; 114 private boolean nullBoolean = false; 115 private byte nullByte = 0; 116 private byte[] nullBytes = null; 117 private Reader nullCharacterStream = null; 118 private Clob nullClob = null; 119 private Date nullDate = null; 120 private double nullDouble = 0.0; 121 private float nullFloat = 0.0f; 122 private int nullInt = 0; 123 private long nullLong = 0; 124 private Object nullObject = null; 125 private Ref nullRef = null; 126 private short nullShort = 0; 127 private String nullString = null; 128 private Time nullTime = null; 129 private Timestamp nullTimestamp = null; 130 private URL nullURL = null; 131 132 /** 133 * The wrapped result. 134 */ 135 private final ResultSet rs; 136 137 /** 138 * Constructs a new instance of 139 * <code>SqlNullCheckedResultSet</code> 140 * to wrap the specified <code>ResultSet</code>. 141 * @param rs ResultSet to wrap 142 */ 143 public SqlNullCheckedResultSet(ResultSet rs) { 144 super(); 145 this.rs = rs; 146 } 147 148 /** 149 * Returns the value when a SQL null is encountered as the result of 150 * invoking a <code>getAsciiStream</code> method. 151 * 152 * @return the value 153 */ 154 public InputStream getNullAsciiStream() { 155 return this.nullAsciiStream; 156 } 157 158 /** 159 * Returns the value when a SQL null is encountered as the result of 160 * invoking a <code>getBigDecimal</code> method. 161 * 162 * @return the value 163 */ 164 public BigDecimal getNullBigDecimal() { 165 return this.nullBigDecimal; 166 } 167 168 /** 169 * Returns the value when a SQL null is encountered as the result of 170 * invoking a <code>getBinaryStream</code> method. 171 * 172 * @return the value 173 */ 174 public InputStream getNullBinaryStream() { 175 return this.nullBinaryStream; 176 } 177 178 /** 179 * Returns the value when a SQL null is encountered as the result of 180 * invoking a <code>getBlob</code> method. 181 * 182 * @return the value 183 */ 184 public Blob getNullBlob() { 185 return this.nullBlob; 186 } 187 188 /** 189 * Returns the value when a SQL null is encountered as the result of 190 * invoking a <code>getBoolean</code> method. 191 * 192 * @return the value 193 */ 194 public boolean getNullBoolean() { 195 return this.nullBoolean; 196 } 197 198 /** 199 * Returns the value when a SQL null is encountered as the result of 200 * invoking a <code>getByte</code> method. 201 * 202 * @return the value 203 */ 204 public byte getNullByte() { 205 return this.nullByte; 206 } 207 208 /** 209 * Returns the value when a SQL null is encountered as the result of 210 * invoking a <code>getBytes</code> method. 211 * 212 * @return the value 213 */ 214 public byte[] getNullBytes() { 215 if (this.nullBytes == null) return null; 216 byte[] copy = new byte[this.nullBytes.length]; 217 System.arraycopy(this.nullBytes, 0, copy, 0, this.nullBytes.length); 218 return copy; 219 } 220 221 /** 222 * Returns the value when a SQL null is encountered as the result of 223 * invoking a <code>getCharacterStream</code> method. 224 * 225 * @return the value 226 */ 227 public Reader getNullCharacterStream() { 228 return this.nullCharacterStream; 229 } 230 231 /** 232 * Returns the value when a SQL null is encountered as the result of 233 * invoking a <code>getClob</code> method. 234 * 235 * @return the value 236 */ 237 public Clob getNullClob() { 238 return this.nullClob; 239 } 240 241 /** 242 * Returns the value when a SQL null is encountered as the result of 243 * invoking a <code>getDate</code> method. 244 * 245 * @return the value 246 */ 247 public Date getNullDate() { 248 return this.nullDate; 249 } 250 251 /** 252 * Returns the value when a SQL null is encountered as the result of 253 * invoking a <code>getDouble</code> method. 254 * 255 * @return the value 256 */ 257 public double getNullDouble() { 258 return this.nullDouble; 259 } 260 261 /** 262 * Returns the value when a SQL null is encountered as the result of 263 * invoking a <code>getFloat</code> method. 264 * 265 * @return the value 266 */ 267 public float getNullFloat() { 268 return this.nullFloat; 269 } 270 271 /** 272 * Returns the value when a SQL null is encountered as the result of 273 * invoking a <code>getInt</code> method. 274 * 275 * @return the value 276 */ 277 public int getNullInt() { 278 return this.nullInt; 279 } 280 281 /** 282 * Returns the value when a SQL null is encountered as the result of 283 * invoking a <code>getLong</code> method. 284 * 285 * @return the value 286 */ 287 public long getNullLong() { 288 return this.nullLong; 289 } 290 291 /** 292 * Returns the value when a SQL null is encountered as the result of 293 * invoking a <code>getObject</code> method. 294 * 295 * @return the value 296 */ 297 public Object getNullObject() { 298 return this.nullObject; 299 } 300 301 /** 302 * Returns the value when a SQL null is encountered as the result of 303 * invoking a <code>getRef</code> method. 304 * 305 * @return the value 306 */ 307 public Ref getNullRef() { 308 return this.nullRef; 309 } 310 311 /** 312 * Returns the value when a SQL null is encountered as the result of 313 * invoking a <code>getShort</code> method. 314 * 315 * @return the value 316 */ 317 public short getNullShort() { 318 return this.nullShort; 319 } 320 321 /** 322 * Returns the value when a SQL null is encountered as the result of 323 * invoking a <code>getString</code> method. 324 * 325 * @return the value 326 */ 327 public String getNullString() { 328 return this.nullString; 329 } 330 331 /** 332 * Returns the value when a SQL null is encountered as the result of 333 * invoking a <code>getTime</code> method. 334 * 335 * @return the value 336 */ 337 public Time getNullTime() { 338 return this.nullTime; 339 } 340 341 /** 342 * Returns the value when a SQL null is encountered as the result of 343 * invoking a <code>getTimestamp</code> method. 344 * 345 * @return the value 346 */ 347 public Timestamp getNullTimestamp() { 348 return this.nullTimestamp; 349 } 350 351 /** 352 * Returns the value when a SQL null is encountered as the result of 353 * invoking a <code>getURL</code> method. 354 * 355 * @return the value 356 */ 357 public URL getNullURL() { 358 return this.nullURL; 359 } 360 361 /** 362 * Intercepts calls to <code>get*</code> methods and calls the appropriate 363 * <code>getNull*</code> method if the <code>ResultSet</code> returned 364 * <code>null</code>. 365 * 366 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 367 * @param proxy Not used; all method calls go to the internal result set 368 * @param method The method to invoke on the result set 369 * @param args The arguments to pass to the result set 370 * @return null checked result 371 * @throws Throwable error 372 */ 373 public Object invoke(Object proxy, Method method, Object[] args) 374 throws Throwable { 375 376 Object result = method.invoke(this.rs, args); 377 378 Method nullMethod = (Method) nullMethods.get(method.getName()); 379 380 // Check nullMethod != null first so that we don't call wasNull() 381 // before a true getter method was invoked on the ResultSet. 382 return (nullMethod != null && this.rs.wasNull()) 383 ? nullMethod.invoke(this, (Object[]) null) 384 : result; 385 } 386 387 /** 388 * Sets the value to return when a SQL null is encountered as the result of 389 * invoking a <code>getAsciiStream</code> method. 390 * 391 * @param nullAsciiStream the value 392 */ 393 public void setNullAsciiStream(InputStream nullAsciiStream) { 394 this.nullAsciiStream = nullAsciiStream; 395 } 396 397 /** 398 * Sets the value to return when a SQL null is encountered as the result of 399 * invoking a <code>getBigDecimal</code> method. 400 * 401 * @param nullBigDecimal the value 402 */ 403 public void setNullBigDecimal(BigDecimal nullBigDecimal) { 404 this.nullBigDecimal = nullBigDecimal; 405 } 406 407 /** 408 * Sets the value to return when a SQL null is encountered as the result of 409 * invoking a <code>getBinaryStream</code> method. 410 * 411 * @param nullBinaryStream the value 412 */ 413 public void setNullBinaryStream(InputStream nullBinaryStream) { 414 this.nullBinaryStream = nullBinaryStream; 415 } 416 417 /** 418 * Sets the value to return when a SQL null is encountered as the result of 419 * invoking a <code>getBlob</code> method. 420 * 421 * @param nullBlob the value 422 */ 423 public void setNullBlob(Blob nullBlob) { 424 this.nullBlob = nullBlob; 425 } 426 427 /** 428 * Sets the value to return when a SQL null is encountered as the result of 429 * invoking a <code>getBoolean</code> method. 430 * 431 * @param nullBoolean the value 432 */ 433 public void setNullBoolean(boolean nullBoolean) { 434 this.nullBoolean = nullBoolean; 435 } 436 437 /** 438 * Sets the value to return when a SQL null is encountered as the result of 439 * invoking a <code>getByte</code> method. 440 * 441 * @param nullByte the value 442 */ 443 public void setNullByte(byte nullByte) { 444 this.nullByte = nullByte; 445 } 446 447 /** 448 * Sets the value to return when a SQL null is encountered as the result of 449 * invoking a <code>getBytes</code> method. 450 * 451 * @param nullBytes the value 452 */ 453 public void setNullBytes(byte[] nullBytes) { 454 byte[] copy = new byte[nullBytes.length]; 455 System.arraycopy(nullBytes, 0, copy, 0, nullBytes.length); 456 this.nullBytes = copy; 457 } 458 459 /** 460 * Sets the value to return when a SQL null is encountered as the result of 461 * invoking a <code>getCharacterStream</code> method. 462 * 463 * @param nullCharacterStream the value 464 */ 465 public void setNullCharacterStream(Reader nullCharacterStream) { 466 this.nullCharacterStream = nullCharacterStream; 467 } 468 469 /** 470 * Sets the value to return when a SQL null is encountered as the result of 471 * invoking a <code>getClob</code> method. 472 * 473 * @param nullClob the value 474 */ 475 public void setNullClob(Clob nullClob) { 476 this.nullClob = nullClob; 477 } 478 479 /** 480 * Sets the value to return when a SQL null is encountered as the result of 481 * invoking a <code>getDate</code> method. 482 * 483 * @param nullDate the value 484 */ 485 public void setNullDate(Date nullDate) { 486 this.nullDate = nullDate; 487 } 488 489 /** 490 * Sets the value to return when a SQL null is encountered as the result of 491 * invoking a <code>getDouble</code> method. 492 * 493 * @param nullDouble the value 494 */ 495 public void setNullDouble(double nullDouble) { 496 this.nullDouble = nullDouble; 497 } 498 499 /** 500 * Sets the value to return when a SQL null is encountered as the result of 501 * invoking a <code>getFloat</code> method. 502 * 503 * @param nullFloat the value 504 */ 505 public void setNullFloat(float nullFloat) { 506 this.nullFloat = nullFloat; 507 } 508 509 /** 510 * Sets the value to return when a SQL null is encountered as the result of 511 * invoking a <code>getInt</code> method. 512 * 513 * @param nullInt the value 514 */ 515 public void setNullInt(int nullInt) { 516 this.nullInt = nullInt; 517 } 518 519 /** 520 * Sets the value to return when a SQL null is encountered as the result of 521 * invoking a <code>getLong</code> method. 522 * 523 * @param nullLong the value 524 */ 525 public void setNullLong(long nullLong) { 526 this.nullLong = nullLong; 527 } 528 529 /** 530 * Sets the value to return when a SQL null is encountered as the result of 531 * invoking a <code>getObject</code> method. 532 * 533 * @param nullObject the value 534 */ 535 public void setNullObject(Object nullObject) { 536 this.nullObject = nullObject; 537 } 538 539 /** 540 * Sets the value to return when a SQL null is encountered as the result of 541 * invoking a <code>getRef</code> method. 542 * 543 * @param nullRef the value 544 */ 545 public void setNullRef(Ref nullRef) { 546 this.nullRef = nullRef; 547 } 548 549 /** 550 * Sets the value to return when a SQL null is encountered as the result of 551 * invoking a <code>getShort</code> method. 552 * 553 * @param nullShort the value 554 */ 555 public void setNullShort(short nullShort) { 556 this.nullShort = nullShort; 557 } 558 559 /** 560 * Sets the value to return when a SQL null is encountered as the result of 561 * invoking a <code>getString</code> method. 562 * 563 * @param nullString the value 564 */ 565 public void setNullString(String nullString) { 566 this.nullString = nullString; 567 } 568 569 /** 570 * Sets the value to return when a SQL null is encountered as the result of 571 * invoking a <code>getTime</code> method. 572 * 573 * @param nullTime the value 574 */ 575 public void setNullTime(Time nullTime) { 576 this.nullTime = nullTime; 577 } 578 579 /** 580 * Sets the value to return when a SQL null is encountered as the result of 581 * invoking a <code>getTimestamp</code> method. 582 * 583 * @param nullTimestamp the value 584 */ 585 public void setNullTimestamp(Timestamp nullTimestamp) { 586 this.nullTimestamp = nullTimestamp; 587 } 588 589 /** 590 * Sets the value to return when a SQL null is encountered as the result of 591 * invoking a <code>getURL</code> method. 592 * 593 * @param nullURL the value 594 */ 595 public void setNullURL(URL nullURL) { 596 this.nullURL = nullURL; 597 } 598 599 }