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 }