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