1. /*
  2. * $Header: /home/cvs/jakarta-commons/dbutils/src/java/org/apache/commons/dbutils/wrappers/SqlNullCheckedResultSet.java,v 1.3 2003/11/09 18:18:04 dgraham Exp $
  3. * $Revision: 1.3 $
  4. * $Date: 2003/11/09 18:18:04 $
  5. *
  6. * ====================================================================
  7. *
  8. * The Apache Software License, Version 1.1
  9. *
  10. * Copyright (c) 2003 The Apache Software Foundation. All rights
  11. * reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * 1. Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * 2. Redistributions in binary form must reproduce the above copyright
  21. * notice, this list of conditions and the following disclaimer in
  22. * the documentation and/or other materials provided with the
  23. * distribution.
  24. *
  25. * 3. The end-user documentation included with the redistribution, if
  26. * any, must include the following acknowledgement:
  27. * "This product includes software developed by the
  28. * Apache Software Foundation (http://www.apache.org/)."
  29. * Alternately, this acknowledgement may appear in the software itself,
  30. * if and wherever such third-party acknowledgements normally appear.
  31. *
  32. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  33. * Foundation" must not be used to endorse or promote products derived
  34. * from this software without prior written permission. For written
  35. * permission, please contact apache@apache.org.
  36. *
  37. * 5. Products derived from this software may not be called "Apache"
  38. * nor may "Apache" appear in their names without prior written
  39. * permission of the Apache Software Foundation.
  40. *
  41. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  42. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  43. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  44. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  45. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  46. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  47. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  48. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  49. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  50. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  51. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  52. * SUCH DAMAGE.
  53. * ====================================================================
  54. *
  55. * This software consists of voluntary contributions made by many
  56. * individuals on behalf of the Apache Software Foundation. For more
  57. * information on the Apache Software Foundation, please see
  58. * <http://www.apache.org/>.
  59. */
  60. package org.apache.commons.dbutils.wrappers;
  61. import java.io.InputStream;
  62. import java.io.Reader;
  63. import java.lang.reflect.InvocationHandler;
  64. import java.lang.reflect.Method;
  65. import java.math.BigDecimal;
  66. import java.net.URL;
  67. import java.sql.Blob;
  68. import java.sql.Clob;
  69. import java.sql.Date;
  70. import java.sql.Ref;
  71. import java.sql.ResultSet;
  72. import java.sql.Time;
  73. import java.sql.Timestamp;
  74. import java.util.HashMap;
  75. import java.util.Map;
  76. import org.apache.commons.dbutils.ProxyFactory;
  77. /**
  78. * Decorates a <code>ResultSet</code> with checks for a SQL NULL value on each
  79. * <code>getXXX</code> method. If a column value obtained by a
  80. * <code>getXXX</code> method is not SQL NULL, the column value is returned. If
  81. * the column value is SQL null, an alternate value is returned. The alternate
  82. * value defaults to the Java <code>null</code> value, which can be overridden
  83. * for instances of the class.
  84. *
  85. * <p>
  86. * Usage example:
  87. * <blockquote>
  88. * <pre>
  89. * Connection conn = // somehow get a connection
  90. * Statement stmt = conn.createStatement();
  91. * ResultSet rs = stmt.executeQuery("SELECT col1, col2 FROM table1");
  92. *
  93. * // Wrap the result set for SQL NULL checking
  94. * SqlNullCheckedResultSet wrapper = new SqlNullCheckedResultSet(rs);
  95. * wrapper.setNullString("---N/A---"); // Set null string
  96. * wrapper.setNullInt(-999); // Set null integer
  97. * rs = ProxyFactory.instance().createResultSet(wrapper);
  98. *
  99. * while (rs.next()) {
  100. * // If col1 is SQL NULL, value returned will be "---N/A---"
  101. * String col1 = rs.getString("col1");
  102. * // If col2 is SQL NULL, value returned will be -999
  103. * int col2 = rs.getInt("col2");
  104. * }
  105. * rs.close();
  106. * </pre>
  107. * </blockquote>
  108. * </p>
  109. *
  110. * @author <a href="stevencaswell@apache.org">Steven Caswell</a>
  111. * @author David Graham
  112. * @version $Id: SqlNullCheckedResultSet.java,v 1.3 2003/11/09 18:18:04 dgraham Exp $
  113. */
  114. public class SqlNullCheckedResultSet implements InvocationHandler {
  115. /**
  116. * Maps normal method names (ie. "getBigDecimal") to the corresponding null
  117. * Method object (ie. getNullBigDecimal).
  118. */
  119. private static final Map nullMethods = new HashMap();
  120. static {
  121. Method[] methods = SqlNullCheckedResultSet.class.getMethods();
  122. for (int i = 0; i < methods.length; i++) {
  123. String methodName = methods[i].getName();
  124. if (methodName.startsWith("getNull")) {
  125. String normalName = "get" + methodName.substring(7);
  126. nullMethods.put(normalName, methods[i]);
  127. }
  128. }
  129. }
  130. /**
  131. * The factory to create proxies with.
  132. */
  133. private static final ProxyFactory factory = ProxyFactory.instance();
  134. /**
  135. * Wraps the <code>ResultSet</code> in an instance of this class. This is
  136. * equivalent to:
  137. * <pre>
  138. * ProxyFactory.instance().createResultSet(new SqlNullCheckedResultSet(rs));
  139. * </pre>
  140. *
  141. * @param rs The <code>ResultSet</code> to wrap.
  142. */
  143. public static ResultSet wrap(ResultSet rs) {
  144. return factory.createResultSet(new SqlNullCheckedResultSet(rs));
  145. }
  146. private InputStream nullAsciiStream = null;
  147. private BigDecimal nullBigDecimal = null;
  148. private InputStream nullBinaryStream = null;
  149. private Blob nullBlob = null;
  150. private boolean nullBoolean = false;
  151. private byte nullByte = 0;
  152. private byte[] nullBytes = null;
  153. private Reader nullCharacterStream = null;
  154. private Clob nullClob = null;
  155. private Date nullDate = null;
  156. private double nullDouble = 0.0;
  157. private float nullFloat = 0.0f;
  158. private int nullInt = 0;
  159. private long nullLong = 0;
  160. private Object nullObject = null;
  161. private Ref nullRef = null;
  162. private short nullShort = 0;
  163. private String nullString = null;
  164. private Time nullTime = null;
  165. private Timestamp nullTimestamp = null;
  166. private URL nullURL = null;
  167. /**
  168. * The wrapped result.
  169. */
  170. private final ResultSet rs;
  171. /**
  172. * Constructs a new instance of
  173. * <code>SqlNullCheckedResultSet</code>
  174. * to wrap the specified <code>ResultSet</code>.
  175. */
  176. public SqlNullCheckedResultSet(ResultSet rs) {
  177. super();
  178. this.rs = rs;
  179. }
  180. /**
  181. * Returns the value when a SQL null is encountered as the result of
  182. * invoking a <code>getAsciiStream</code> method.
  183. *
  184. * @return the value
  185. */
  186. public InputStream getNullAsciiStream() {
  187. return this.nullAsciiStream;
  188. }
  189. /**
  190. * Returns the value when a SQL null is encountered as the result of
  191. * invoking a <code>getBigDecimal</code> method.
  192. *
  193. * @return the value
  194. */
  195. public BigDecimal getNullBigDecimal() {
  196. return this.nullBigDecimal;
  197. }
  198. /**
  199. * Returns the value when a SQL null is encountered as the result of
  200. * invoking a <code>getBinaryStream</code> method.
  201. *
  202. * @return the value
  203. */
  204. public InputStream getNullBinaryStream() {
  205. return this.nullBinaryStream;
  206. }
  207. /**
  208. * Returns the value when a SQL null is encountered as the result of
  209. * invoking a <code>getBlob</code> method.
  210. *
  211. * @return the value
  212. */
  213. public Blob getNullBlob() {
  214. return this.nullBlob;
  215. }
  216. /**
  217. * Returns the value when a SQL null is encountered as the result of
  218. * invoking a <code>getBoolean</code> method.
  219. *
  220. * @return the value
  221. */
  222. public boolean getNullBoolean() {
  223. return this.nullBoolean;
  224. }
  225. /**
  226. * Returns the value when a SQL null is encountered as the result of
  227. * invoking a <code>getByte</code> method.
  228. *
  229. * @return the value
  230. */
  231. public byte getNullByte() {
  232. return this.nullByte;
  233. }
  234. /**
  235. * Returns the value when a SQL null is encountered as the result of
  236. * invoking a <code>getBytes</code> method.
  237. *
  238. * @return the value
  239. */
  240. public byte[] getNullBytes() {
  241. return this.nullBytes;
  242. }
  243. /**
  244. * Returns the value when a SQL null is encountered as the result of
  245. * invoking a <code>getCharacterStream</code> method.
  246. *
  247. * @return the value
  248. */
  249. public Reader getNullCharacterStream() {
  250. return this.nullCharacterStream;
  251. }
  252. /**
  253. * Returns the value when a SQL null is encountered as the result of
  254. * invoking a <code>getClob</code> method.
  255. *
  256. * @return the value
  257. */
  258. public Clob getNullClob() {
  259. return this.nullClob;
  260. }
  261. /**
  262. * Returns the value when a SQL null is encountered as the result of
  263. * invoking a <code>getDate</code> method.
  264. *
  265. * @return the value
  266. */
  267. public Date getNullDate() {
  268. return this.nullDate;
  269. }
  270. /**
  271. * Returns the value when a SQL null is encountered as the result of
  272. * invoking a <code>getDouble</code> method.
  273. *
  274. * @return the value
  275. */
  276. public double getNullDouble() {
  277. return this.nullDouble;
  278. }
  279. /**
  280. * Returns the value when a SQL null is encountered as the result of
  281. * invoking a <code>getFloat</code> method.
  282. *
  283. * @return the value
  284. */
  285. public float getNullFloat() {
  286. return this.nullFloat;
  287. }
  288. /**
  289. * Returns the value when a SQL null is encountered as the result of
  290. * invoking a <code>getInt</code> method.
  291. *
  292. * @return the value
  293. */
  294. public int getNullInt() {
  295. return this.nullInt;
  296. }
  297. /**
  298. * Returns the value when a SQL null is encountered as the result of
  299. * invoking a <code>getLong</code> method.
  300. *
  301. * @return the value
  302. */
  303. public long getNullLong() {
  304. return this.nullLong;
  305. }
  306. /**
  307. * Returns the value when a SQL null is encountered as the result of
  308. * invoking a <code>getObject</code> method.
  309. *
  310. * @return the value
  311. */
  312. public Object getNullObject() {
  313. return this.nullObject;
  314. }
  315. /**
  316. * Returns the value when a SQL null is encountered as the result of
  317. * invoking a <code>getRef</code> method.
  318. *
  319. * @return the value
  320. */
  321. public Ref getNullRef() {
  322. return this.nullRef;
  323. }
  324. /**
  325. * Returns the value when a SQL null is encountered as the result of
  326. * invoking a <code>getShort</code> method.
  327. *
  328. * @return the value
  329. */
  330. public short getNullShort() {
  331. return this.nullShort;
  332. }
  333. /**
  334. * Returns the value when a SQL null is encountered as the result of
  335. * invoking a <code>getString</code> method.
  336. *
  337. * @return the value
  338. */
  339. public String getNullString() {
  340. return this.nullString;
  341. }
  342. /**
  343. * Returns the value when a SQL null is encountered as the result of
  344. * invoking a <code>getTime</code> method.
  345. *
  346. * @return the value
  347. */
  348. public Time getNullTime() {
  349. return this.nullTime;
  350. }
  351. /**
  352. * Returns the value when a SQL null is encountered as the result of
  353. * invoking a <code>getTimestamp</code> method.
  354. *
  355. * @return the value
  356. */
  357. public Timestamp getNullTimestamp() {
  358. return this.nullTimestamp;
  359. }
  360. /**
  361. * Returns the value when a SQL null is encountered as the result of
  362. * invoking a <code>getURL</code> method.
  363. *
  364. * @return the value
  365. */
  366. public URL getNullURL() {
  367. return this.nullURL;
  368. }
  369. /**
  370. * Intercepts calls to <code>get*</code> methods and calls the appropriate
  371. * <code>getNull*</code> method if the <code>ResultSet</code> returned
  372. * <code>null</code>.
  373. *
  374. * @throws Throwable
  375. * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
  376. */
  377. public Object invoke(Object proxy, Method method, Object[] args)
  378. throws Throwable {
  379. Object result = method.invoke(this.rs, args);
  380. Method nullMethod = (Method) nullMethods.get(method.getName());
  381. // Check nullMethod != null first so that we don't call wasNull()
  382. // before a true getter method was invoked on the ResultSet.
  383. return (nullMethod != null && this.rs.wasNull())
  384. ? nullMethod.invoke(this, null)
  385. : result;
  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. * Sets the value to return when a SQL null is encountered as the result of
  398. * invoking a <code>getBigDecimal</code> method.
  399. *
  400. * @param nullBigDecimal the value
  401. */
  402. public void setNullBigDecimal(BigDecimal nullBigDecimal) {
  403. this.nullBigDecimal = nullBigDecimal;
  404. }
  405. /**
  406. * Sets the value to return when a SQL null is encountered as the result of
  407. * invoking a <code>getBinaryStream</code> method.
  408. *
  409. * @param nullBinaryStream the value
  410. */
  411. public void setNullBinaryStream(InputStream nullBinaryStream) {
  412. this.nullBinaryStream = nullBinaryStream;
  413. }
  414. /**
  415. * Sets the value to return when a SQL null is encountered as the result of
  416. * invoking a <code>getBlob</code> method.
  417. *
  418. * @param nullBlob the value
  419. */
  420. public void setNullBlob(Blob nullBlob) {
  421. this.nullBlob = nullBlob;
  422. }
  423. /**
  424. * Sets the value to return when a SQL null is encountered as the result of
  425. * invoking a <code>getBoolean</code> method.
  426. *
  427. * @param nullBoolean the value
  428. */
  429. public void setNullBoolean(boolean nullBoolean) {
  430. this.nullBoolean = nullBoolean;
  431. }
  432. /**
  433. * Sets the value to return when a SQL null is encountered as the result of
  434. * invoking a <code>getByte</code> method.
  435. *
  436. * @param nullByte the value
  437. */
  438. public void setNullByte(byte nullByte) {
  439. this.nullByte = nullByte;
  440. }
  441. /**
  442. * Sets the value to return when a SQL null is encountered as the result of
  443. * invoking a <code>getBytes</code> method.
  444. *
  445. * @param nullBytes the value
  446. */
  447. public void setNullBytes(byte[] nullBytes) {
  448. this.nullBytes = nullBytes;
  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. * 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. * 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. * Sets the value to return when a SQL null is encountered as the result of
  479. * invoking a <code>getDouble</code> method.
  480. *
  481. * @param nullDouble the value
  482. */
  483. public void setNullDouble(double nullDouble) {
  484. this.nullDouble = nullDouble;
  485. }
  486. /**
  487. * Sets the value to return when a SQL null is encountered as the result of
  488. * invoking a <code>getFloat</code> method.
  489. *
  490. * @param nullFloat the value
  491. */
  492. public void setNullFloat(float nullFloat) {
  493. this.nullFloat = nullFloat;
  494. }
  495. /**
  496. * Sets the value to return when a SQL null is encountered as the result of
  497. * invoking a <code>getInt</code> method.
  498. *
  499. * @param nullInt the value
  500. */
  501. public void setNullInt(int nullInt) {
  502. this.nullInt = nullInt;
  503. }
  504. /**
  505. * Sets the value to return when a SQL null is encountered as the result of
  506. * invoking a <code>getLong</code> method.
  507. *
  508. * @param nullLong the value
  509. */
  510. public void setNullLong(long nullLong) {
  511. this.nullLong = nullLong;
  512. }
  513. /**
  514. * Sets the value to return when a SQL null is encountered as the result of
  515. * invoking a <code>getObject</code> method.
  516. *
  517. * @param nullObject the value
  518. */
  519. public void setNullObject(Object nullObject) {
  520. this.nullObject = nullObject;
  521. }
  522. /**
  523. * Sets the value to return when a SQL null is encountered as the result of
  524. * invoking a <code>getRef</code> method.
  525. *
  526. * @param nullRef the value
  527. */
  528. public void setNullRef(Ref nullRef) {
  529. this.nullRef = nullRef;
  530. }
  531. /**
  532. * Sets the value to return when a SQL null is encountered as the result of
  533. * invoking a <code>getShort</code> method.
  534. *
  535. * @param nullShort the value
  536. */
  537. public void setNullShort(short nullShort) {
  538. this.nullShort = nullShort;
  539. }
  540. /**
  541. * Sets the value to return when a SQL null is encountered as the result of
  542. * invoking a <code>getString</code> method.
  543. *
  544. * @param nullString the value
  545. */
  546. public void setNullString(String nullString) {
  547. this.nullString = nullString;
  548. }
  549. /**
  550. * Sets the value to return when a SQL null is encountered as the result of
  551. * invoking a <code>getTime</code> method.
  552. *
  553. * @param nullTime the value
  554. */
  555. public void setNullTime(Time nullTime) {
  556. this.nullTime = nullTime;
  557. }
  558. /**
  559. * Sets the value to return when a SQL null is encountered as the result of
  560. * invoking a <code>getTimestamp</code> method.
  561. *
  562. * @param nullTimestamp the value
  563. */
  564. public void setNullTimestamp(Timestamp nullTimestamp) {
  565. this.nullTimestamp = nullTimestamp;
  566. }
  567. /**
  568. * Sets the value to return when a SQL null is encountered as the result of
  569. * invoking a <code>getURL</code> method.
  570. *
  571. * @param nullURL the value
  572. */
  573. public void setNullURL(URL nullURL) {
  574. this.nullURL = nullURL;
  575. }
  576. }