View Javadoc

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<String,Method> nullMethods = new HashMap<String,Method>();
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         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 }