1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.dbutils.wrappers; 18 19 import java.io.InputStream; 20 import java.io.Reader; 21 import java.lang.reflect.InvocationHandler; 22 import java.lang.reflect.Method; 23 import java.math.BigDecimal; 24 import java.net.URL; 25 import java.sql.Blob; 26 import java.sql.Clob; 27 import java.sql.Date; 28 import java.sql.Ref; 29 import java.sql.ResultSet; 30 import java.sql.Time; 31 import java.sql.Timestamp; 32 import java.util.HashMap; 33 import java.util.Map; 34 35 import org.apache.commons.dbutils.ProxyFactory; 36 37 /** 38 * Decorates a <code>ResultSet</code> with checks for a SQL NULL value on each 39 * <code>getXXX</code> method. If a column value obtained by a 40 * <code>getXXX</code> method is not SQL NULL, the column value is returned. If 41 * the column value is SQL null, an alternate value is returned. The alternate 42 * value defaults to the Java <code>null</code> value, which can be overridden 43 * for instances of the class. 44 * 45 * <p> 46 * Usage example: 47 * <blockquote> 48 * <pre> 49 * Connection conn = // somehow get a connection 50 * Statement stmt = conn.createStatement(); 51 * ResultSet rs = stmt.executeQuery("SELECT col1, col2 FROM table1"); 52 * 53 * // Wrap the result set for SQL NULL checking 54 * SqlNullCheckedResultSet wrapper = new SqlNullCheckedResultSet(rs); 55 * wrapper.setNullString("---N/A---"); // Set null string 56 * wrapper.setNullInt(-999); // Set null integer 57 * rs = ProxyFactory.instance().createResultSet(wrapper); 58 * 59 * while (rs.next()) { 60 * // If col1 is SQL NULL, value returned will be "---N/A---" 61 * String col1 = rs.getString("col1"); 62 * // If col2 is SQL NULL, value returned will be -999 63 * int col2 = rs.getInt("col2"); 64 * } 65 * rs.close(); 66 * </pre> 67 * </blockquote> 68 * </p> 69 * <p>Unlike some other classes in DbUtils, this class is NOT thread-safe.</p> 70 */ 71 public class SqlNullCheckedResultSet implements InvocationHandler { 72 73 /** 74 * Maps normal method names (ie. "getBigDecimal") to the corresponding null 75 * Method object (ie. getNullBigDecimal). 76 */ 77 private static final Map nullMethods = new HashMap(); 78 79 static { 80 Method[] methods = SqlNullCheckedResultSet.class.getMethods(); 81 for (int i = 0; i < methods.length; i++) { 82 String methodName = methods[i].getName(); 83 84 if (methodName.startsWith("getNull")) { 85 String normalName = "get" + methodName.substring(7); 86 nullMethods.put(normalName, methods[i]); 87 } 88 } 89 } 90 91 /** 92 * The factory to create proxies with. 93 */ 94 private static final ProxyFactory factory = ProxyFactory.instance(); 95 96 /** 97 * Wraps the <code>ResultSet</code> in an instance of this class. This is 98 * equivalent to: 99 * <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 return this.nullBytes; 216 } 217 218 /** 219 * Returns the value when a SQL null is encountered as the result of 220 * invoking a <code>getCharacterStream</code> method. 221 * 222 * @return the value 223 */ 224 public Reader getNullCharacterStream() { 225 return this.nullCharacterStream; 226 } 227 228 /** 229 * Returns the value when a SQL null is encountered as the result of 230 * invoking a <code>getClob</code> method. 231 * 232 * @return the value 233 */ 234 public Clob getNullClob() { 235 return this.nullClob; 236 } 237 238 /** 239 * Returns the value when a SQL null is encountered as the result of 240 * invoking a <code>getDate</code> method. 241 * 242 * @return the value 243 */ 244 public Date getNullDate() { 245 return this.nullDate; 246 } 247 248 /** 249 * Returns the value when a SQL null is encountered as the result of 250 * invoking a <code>getDouble</code> method. 251 * 252 * @return the value 253 */ 254 public double getNullDouble() { 255 return this.nullDouble; 256 } 257 258 /** 259 * Returns the value when a SQL null is encountered as the result of 260 * invoking a <code>getFloat</code> method. 261 * 262 * @return the value 263 */ 264 public float getNullFloat() { 265 return this.nullFloat; 266 } 267 268 /** 269 * Returns the value when a SQL null is encountered as the result of 270 * invoking a <code>getInt</code> method. 271 * 272 * @return the value 273 */ 274 public int getNullInt() { 275 return this.nullInt; 276 } 277 278 /** 279 * Returns the value when a SQL null is encountered as the result of 280 * invoking a <code>getLong</code> method. 281 * 282 * @return the value 283 */ 284 public long getNullLong() { 285 return this.nullLong; 286 } 287 288 /** 289 * Returns the value when a SQL null is encountered as the result of 290 * invoking a <code>getObject</code> method. 291 * 292 * @return the value 293 */ 294 public Object getNullObject() { 295 return this.nullObject; 296 } 297 298 /** 299 * Returns the value when a SQL null is encountered as the result of 300 * invoking a <code>getRef</code> method. 301 * 302 * @return the value 303 */ 304 public Ref getNullRef() { 305 return this.nullRef; 306 } 307 308 /** 309 * Returns the value when a SQL null is encountered as the result of 310 * invoking a <code>getShort</code> method. 311 * 312 * @return the value 313 */ 314 public short getNullShort() { 315 return this.nullShort; 316 } 317 318 /** 319 * Returns the value when a SQL null is encountered as the result of 320 * invoking a <code>getString</code> method. 321 * 322 * @return the value 323 */ 324 public String getNullString() { 325 return this.nullString; 326 } 327 328 /** 329 * Returns the value when a SQL null is encountered as the result of 330 * invoking a <code>getTime</code> method. 331 * 332 * @return the value 333 */ 334 public Time getNullTime() { 335 return this.nullTime; 336 } 337 338 /** 339 * Returns the value when a SQL null is encountered as the result of 340 * invoking a <code>getTimestamp</code> method. 341 * 342 * @return the value 343 */ 344 public Timestamp getNullTimestamp() { 345 return this.nullTimestamp; 346 } 347 348 /** 349 * Returns the value when a SQL null is encountered as the result of 350 * invoking a <code>getURL</code> method. 351 * 352 * @return the value 353 */ 354 public URL getNullURL() { 355 return this.nullURL; 356 } 357 358 /** 359 * Intercepts calls to <code>get*</code> methods and calls the appropriate 360 * <code>getNull*</code> method if the <code>ResultSet</code> returned 361 * <code>null</code>. 362 * 363 * @throws Throwable 364 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 365 */ 366 public Object invoke(Object proxy, Method method, Object[] args) 367 throws Throwable { 368 369 Object result = method.invoke(this.rs, args); 370 371 Method nullMethod = (Method) nullMethods.get(method.getName()); 372 373 // Check nullMethod != null first so that we don't call wasNull() 374 // before a true getter method was invoked on the ResultSet. 375 return (nullMethod != null && this.rs.wasNull()) 376 ? nullMethod.invoke(this, (Object[]) null) 377 : result; 378 } 379 380 /** 381 * Sets the value to return when a SQL null is encountered as the result of 382 * invoking a <code>getAsciiStream</code> method. 383 * 384 * @param nullAsciiStream the value 385 */ 386 public void setNullAsciiStream(InputStream nullAsciiStream) { 387 this.nullAsciiStream = nullAsciiStream; 388 } 389 390 /** 391 * Sets the value to return when a SQL null is encountered as the result of 392 * invoking a <code>getBigDecimal</code> method. 393 * 394 * @param nullBigDecimal the value 395 */ 396 public void setNullBigDecimal(BigDecimal nullBigDecimal) { 397 this.nullBigDecimal = nullBigDecimal; 398 } 399 400 /** 401 * Sets the value to return when a SQL null is encountered as the result of 402 * invoking a <code>getBinaryStream</code> method. 403 * 404 * @param nullBinaryStream the value 405 */ 406 public void setNullBinaryStream(InputStream nullBinaryStream) { 407 this.nullBinaryStream = nullBinaryStream; 408 } 409 410 /** 411 * Sets the value to return when a SQL null is encountered as the result of 412 * invoking a <code>getBlob</code> method. 413 * 414 * @param nullBlob the value 415 */ 416 public void setNullBlob(Blob nullBlob) { 417 this.nullBlob = nullBlob; 418 } 419 420 /** 421 * Sets the value to return when a SQL null is encountered as the result of 422 * invoking a <code>getBoolean</code> method. 423 * 424 * @param nullBoolean the value 425 */ 426 public void setNullBoolean(boolean nullBoolean) { 427 this.nullBoolean = nullBoolean; 428 } 429 430 /** 431 * Sets the value to return when a SQL null is encountered as the result of 432 * invoking a <code>getByte</code> method. 433 * 434 * @param nullByte the value 435 */ 436 public void setNullByte(byte nullByte) { 437 this.nullByte = nullByte; 438 } 439 440 /** 441 * Sets the value to return when a SQL null is encountered as the result of 442 * invoking a <code>getBytes</code> method. 443 * 444 * @param nullBytes the value 445 */ 446 public void setNullBytes(byte[] nullBytes) { 447 this.nullBytes = nullBytes; 448 } 449 450 /** 451 * Sets the value to return when a SQL null is encountered as the result of 452 * invoking a <code>getCharacterStream</code> method. 453 * 454 * @param nullCharacterStream the value 455 */ 456 public void setNullCharacterStream(Reader nullCharacterStream) { 457 this.nullCharacterStream = nullCharacterStream; 458 } 459 460 /** 461 * Sets the value to return when a SQL null is encountered as the result of 462 * invoking a <code>getClob</code> method. 463 * 464 * @param nullClob the value 465 */ 466 public void setNullClob(Clob nullClob) { 467 this.nullClob = nullClob; 468 } 469 470 /** 471 * Sets the value to return when a SQL null is encountered as the result of 472 * invoking a <code>getDate</code> method. 473 * 474 * @param nullDate the value 475 */ 476 public void setNullDate(Date nullDate) { 477 this.nullDate = nullDate; 478 } 479 480 /** 481 * Sets the value to return when a SQL null is encountered as the result of 482 * invoking a <code>getDouble</code> method. 483 * 484 * @param nullDouble the value 485 */ 486 public void setNullDouble(double nullDouble) { 487 this.nullDouble = nullDouble; 488 } 489 490 /** 491 * Sets the value to return when a SQL null is encountered as the result of 492 * invoking a <code>getFloat</code> method. 493 * 494 * @param nullFloat the value 495 */ 496 public void setNullFloat(float nullFloat) { 497 this.nullFloat = nullFloat; 498 } 499 500 /** 501 * Sets the value to return when a SQL null is encountered as the result of 502 * invoking a <code>getInt</code> method. 503 * 504 * @param nullInt the value 505 */ 506 public void setNullInt(int nullInt) { 507 this.nullInt = nullInt; 508 } 509 510 /** 511 * Sets the value to return when a SQL null is encountered as the result of 512 * invoking a <code>getLong</code> method. 513 * 514 * @param nullLong the value 515 */ 516 public void setNullLong(long nullLong) { 517 this.nullLong = nullLong; 518 } 519 520 /** 521 * Sets the value to return when a SQL null is encountered as the result of 522 * invoking a <code>getObject</code> method. 523 * 524 * @param nullObject the value 525 */ 526 public void setNullObject(Object nullObject) { 527 this.nullObject = nullObject; 528 } 529 530 /** 531 * Sets the value to return when a SQL null is encountered as the result of 532 * invoking a <code>getRef</code> method. 533 * 534 * @param nullRef the value 535 */ 536 public void setNullRef(Ref nullRef) { 537 this.nullRef = nullRef; 538 } 539 540 /** 541 * Sets the value to return when a SQL null is encountered as the result of 542 * invoking a <code>getShort</code> method. 543 * 544 * @param nullShort the value 545 */ 546 public void setNullShort(short nullShort) { 547 this.nullShort = nullShort; 548 } 549 550 /** 551 * Sets the value to return when a SQL null is encountered as the result of 552 * invoking a <code>getString</code> method. 553 * 554 * @param nullString the value 555 */ 556 public void setNullString(String nullString) { 557 this.nullString = nullString; 558 } 559 560 /** 561 * Sets the value to return when a SQL null is encountered as the result of 562 * invoking a <code>getTime</code> method. 563 * 564 * @param nullTime the value 565 */ 566 public void setNullTime(Time nullTime) { 567 this.nullTime = nullTime; 568 } 569 570 /** 571 * Sets the value to return when a SQL null is encountered as the result of 572 * invoking a <code>getTimestamp</code> method. 573 * 574 * @param nullTimestamp the value 575 */ 576 public void setNullTimestamp(Timestamp nullTimestamp) { 577 this.nullTimestamp = nullTimestamp; 578 } 579 580 /** 581 * Sets the value to return when a SQL null is encountered as the result of 582 * invoking a <code>getURL</code> method. 583 * 584 * @param nullURL the value 585 */ 586 public void setNullURL(URL nullURL) { 587 this.nullURL = nullURL; 588 } 589 590 }