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: */
070: public class SqlNullCheckedResultSet implements InvocationHandler {
071:
072: /**
073: * Maps normal method names (ie. "getBigDecimal") to the corresponding null
074: * Method object (ie. getNullBigDecimal).
075: */
076: private static final Map nullMethods = new HashMap();
077:
078: static {
079: Method[] methods = SqlNullCheckedResultSet.class.getMethods();
080: for (int i = 0; i < methods.length; i++) {
081: String methodName = methods[i].getName();
082:
083: if (methodName.startsWith("getNull")) {
084: String normalName = "get" + methodName.substring(7);
085: nullMethods.put(normalName, methods[i]);
086: }
087: }
088: }
089:
090: /**
091: * The factory to create proxies with.
092: */
093: private static final ProxyFactory factory = ProxyFactory.instance();
094:
095: /**
096: * Wraps the <code>ResultSet</code> in an instance of this class. This is
097: * equivalent to:
098: * <pre>
099: * 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()) ? nullMethod
375: .invoke(this , (Object[]) null) : result;
376: }
377:
378: /**
379: * Sets the value to return when a SQL null is encountered as the result of
380: * invoking a <code>getAsciiStream</code> method.
381: *
382: * @param nullAsciiStream the value
383: */
384: public void setNullAsciiStream(InputStream nullAsciiStream) {
385: this .nullAsciiStream = nullAsciiStream;
386: }
387:
388: /**
389: * Sets the value to return when a SQL null is encountered as the result of
390: * invoking a <code>getBigDecimal</code> method.
391: *
392: * @param nullBigDecimal the value
393: */
394: public void setNullBigDecimal(BigDecimal nullBigDecimal) {
395: this .nullBigDecimal = nullBigDecimal;
396: }
397:
398: /**
399: * Sets the value to return when a SQL null is encountered as the result of
400: * invoking a <code>getBinaryStream</code> method.
401: *
402: * @param nullBinaryStream the value
403: */
404: public void setNullBinaryStream(InputStream nullBinaryStream) {
405: this .nullBinaryStream = nullBinaryStream;
406: }
407:
408: /**
409: * Sets the value to return when a SQL null is encountered as the result of
410: * invoking a <code>getBlob</code> method.
411: *
412: * @param nullBlob the value
413: */
414: public void setNullBlob(Blob nullBlob) {
415: this .nullBlob = nullBlob;
416: }
417:
418: /**
419: * Sets the value to return when a SQL null is encountered as the result of
420: * invoking a <code>getBoolean</code> method.
421: *
422: * @param nullBoolean the value
423: */
424: public void setNullBoolean(boolean nullBoolean) {
425: this .nullBoolean = nullBoolean;
426: }
427:
428: /**
429: * Sets the value to return when a SQL null is encountered as the result of
430: * invoking a <code>getByte</code> method.
431: *
432: * @param nullByte the value
433: */
434: public void setNullByte(byte nullByte) {
435: this .nullByte = nullByte;
436: }
437:
438: /**
439: * Sets the value to return when a SQL null is encountered as the result of
440: * invoking a <code>getBytes</code> method.
441: *
442: * @param nullBytes the value
443: */
444: public void setNullBytes(byte[] nullBytes) {
445: this .nullBytes = nullBytes;
446: }
447:
448: /**
449: * Sets the value to return when a SQL null is encountered as the result of
450: * invoking a <code>getCharacterStream</code> method.
451: *
452: * @param nullCharacterStream the value
453: */
454: public void setNullCharacterStream(Reader nullCharacterStream) {
455: this .nullCharacterStream = nullCharacterStream;
456: }
457:
458: /**
459: * Sets the value to return when a SQL null is encountered as the result of
460: * invoking a <code>getClob</code> method.
461: *
462: * @param nullClob the value
463: */
464: public void setNullClob(Clob nullClob) {
465: this .nullClob = nullClob;
466: }
467:
468: /**
469: * Sets the value to return when a SQL null is encountered as the result of
470: * invoking a <code>getDate</code> method.
471: *
472: * @param nullDate the value
473: */
474: public void setNullDate(Date nullDate) {
475: this .nullDate = nullDate;
476: }
477:
478: /**
479: * Sets the value to return when a SQL null is encountered as the result of
480: * invoking a <code>getDouble</code> method.
481: *
482: * @param nullDouble the value
483: */
484: public void setNullDouble(double nullDouble) {
485: this .nullDouble = nullDouble;
486: }
487:
488: /**
489: * Sets the value to return when a SQL null is encountered as the result of
490: * invoking a <code>getFloat</code> method.
491: *
492: * @param nullFloat the value
493: */
494: public void setNullFloat(float nullFloat) {
495: this .nullFloat = nullFloat;
496: }
497:
498: /**
499: * Sets the value to return when a SQL null is encountered as the result of
500: * invoking a <code>getInt</code> method.
501: *
502: * @param nullInt the value
503: */
504: public void setNullInt(int nullInt) {
505: this .nullInt = nullInt;
506: }
507:
508: /**
509: * Sets the value to return when a SQL null is encountered as the result of
510: * invoking a <code>getLong</code> method.
511: *
512: * @param nullLong the value
513: */
514: public void setNullLong(long nullLong) {
515: this .nullLong = nullLong;
516: }
517:
518: /**
519: * Sets the value to return when a SQL null is encountered as the result of
520: * invoking a <code>getObject</code> method.
521: *
522: * @param nullObject the value
523: */
524: public void setNullObject(Object nullObject) {
525: this .nullObject = nullObject;
526: }
527:
528: /**
529: * Sets the value to return when a SQL null is encountered as the result of
530: * invoking a <code>getRef</code> method.
531: *
532: * @param nullRef the value
533: */
534: public void setNullRef(Ref nullRef) {
535: this .nullRef = nullRef;
536: }
537:
538: /**
539: * Sets the value to return when a SQL null is encountered as the result of
540: * invoking a <code>getShort</code> method.
541: *
542: * @param nullShort the value
543: */
544: public void setNullShort(short nullShort) {
545: this .nullShort = nullShort;
546: }
547:
548: /**
549: * Sets the value to return when a SQL null is encountered as the result of
550: * invoking a <code>getString</code> method.
551: *
552: * @param nullString the value
553: */
554: public void setNullString(String nullString) {
555: this .nullString = nullString;
556: }
557:
558: /**
559: * Sets the value to return when a SQL null is encountered as the result of
560: * invoking a <code>getTime</code> method.
561: *
562: * @param nullTime the value
563: */
564: public void setNullTime(Time nullTime) {
565: this .nullTime = nullTime;
566: }
567:
568: /**
569: * Sets the value to return when a SQL null is encountered as the result of
570: * invoking a <code>getTimestamp</code> method.
571: *
572: * @param nullTimestamp the value
573: */
574: public void setNullTimestamp(Timestamp nullTimestamp) {
575: this .nullTimestamp = nullTimestamp;
576: }
577:
578: /**
579: * Sets the value to return when a SQL null is encountered as the result of
580: * invoking a <code>getURL</code> method.
581: *
582: * @param nullURL the value
583: */
584: public void setNullURL(URL nullURL) {
585: this.nullURL = nullURL;
586: }
587:
588: }
|