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