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 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 }