1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.beanutils;
  17. import java.io.Serializable;
  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. /**
  23. * <p>Implementation of {@link DynaClass} that creates an in-memory collection
  24. * of {@link DynaBean}s representing the results of an SQL query. Once the
  25. * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code>
  26. * and <code>Statement</code> on which it is based can be closed, and the
  27. * underlying <code>Connection</code> can be returned to its connection pool
  28. * (if you are using one).</p>
  29. *
  30. * <p>The normal usage pattern is something like:</p>
  31. * <pre>
  32. * Connection conn = ...; // Acquire connection from pool
  33. * Statement stmt = conn.createStatement();
  34. * ResultSet rs = stmt.executeQuery("SELECT ...");
  35. * RowSetDynaClass rsdc = new RowSetDynaClass(rs);
  36. * rs.close();
  37. * stmt.close();
  38. * ...; // Return connection to pool
  39. * List rows = rsdc.getRows();
  40. * ...; // Process the rows as desired
  41. * </pre>
  42. *
  43. * <p>Each column in the result set will be represented as a {@link DynaBean}
  44. * property of the corresponding name (optionally forced to lower case
  45. * for portability). There will be one {@link DynaBean} in the
  46. * <code>List</code> returned by <code>getRows()</code> for each
  47. * row in the original <code>ResultSet</code>.</p>
  48. *
  49. * <p>In general, instances of {@link RowSetDynaClass} can be serialized
  50. * and deserialized, which will automatically include the list of
  51. * {@link DynaBean}s representing the data content. The only exception
  52. * to this rule would be when the underlying property values that were
  53. * copied from the <code>ResultSet</code> originally cannot themselves
  54. * be serialized. Therefore, a {@link RowSetDynaClass} makes a very
  55. * convenient mechanism for transporting data sets to remote Java-based
  56. * application components.</p>
  57. *
  58. * @author Craig R. McClanahan
  59. * @version $Revision: 1.9 $ $Date: 2004/02/28 13:18:34 $
  60. */
  61. public class RowSetDynaClass extends JDBCDynaClass implements DynaClass, Serializable {
  62. // ----------------------------------------------------- Instance variables
  63. /**
  64. * <p>Limits the size of the returned list. The call to
  65. * <code>getRows()</code> will return at most limit number of rows.
  66. * If less than or equal to 0, does not limit the size of the result.
  67. */
  68. protected int limit = -1;
  69. /**
  70. * <p>The list of {@link DynaBean}s representing the contents of
  71. * the original <code>ResultSet</code> on which this
  72. * {@link RowSetDynaClass} was based.</p>
  73. */
  74. protected List rows = new ArrayList();
  75. // ----------------------------------------------------------- Constructors
  76. /**
  77. * <p>Construct a new {@link RowSetDynaClass} for the specified
  78. * <code>ResultSet</code>. The property names corresponding
  79. * to column names in the result set will be lower cased.</p>
  80. *
  81. * @param resultSet The result set to be wrapped
  82. *
  83. * @exception NullPointerException if <code>resultSet</code>
  84. * is <code>null</code>
  85. * @exception SQLException if the metadata for this result set
  86. * cannot be introspected
  87. */
  88. public RowSetDynaClass(ResultSet resultSet) throws SQLException {
  89. this(resultSet, true, -1);
  90. }
  91. /**
  92. * <p>Construct a new {@link RowSetDynaClass} for the specified
  93. * <code>ResultSet</code>. The property names corresponding
  94. * to column names in the result set will be lower cased.</p>
  95. *
  96. * If <code>limit</code> is not less than 0, max <code>limit</code>
  97. * number of rows will be copied into the list.
  98. *
  99. * @param resultSet The result set to be wrapped
  100. * @param limit The maximum for the size of the result.
  101. *
  102. * @exception NullPointerException if <code>resultSet</code>
  103. * is <code>null</code>
  104. * @exception SQLException if the metadata for this result set
  105. * cannot be introspected
  106. */
  107. public RowSetDynaClass(ResultSet resultSet, int limit) throws SQLException {
  108. this(resultSet, true, limit);
  109. }
  110. /**
  111. * <p>Construct a new {@link RowSetDynaClass} for the specified
  112. * <code>ResultSet</code>. The property names corresponding
  113. * to the column names in the result set will be lower cased or not,
  114. * depending on the specified <code>lowerCase</code> value.</p>
  115. *
  116. * If <code>limit</code> is not less than 0, max <code>limit</code>
  117. * number of rows will be copied into the resultset.
  118. *
  119. *
  120. * @param resultSet The result set to be wrapped
  121. * @param lowerCase Should property names be lower cased?
  122. *
  123. * @exception NullPointerException if <code>resultSet</code>
  124. * is <code>null</code>
  125. * @exception SQLException if the metadata for this result set
  126. * cannot be introspected
  127. */
  128. public RowSetDynaClass(ResultSet resultSet, boolean lowerCase)
  129. throws SQLException {
  130. this(resultSet, lowerCase, -1);
  131. }
  132. /**
  133. * <p>Construct a new {@link RowSetDynaClass} for the specified
  134. * <code>ResultSet</code>. The property names corresponding
  135. * to the column names in the result set will be lower cased or not,
  136. * depending on the specified <code>lowerCase</code> value.</p>
  137. *
  138. * <p><strong>WARNING</strong> - If you specify <code>false</code>
  139. * for <code>lowerCase</code>, the returned property names will
  140. * exactly match the column names returned by your JDBC driver.
  141. * Because different drivers might return column names in different
  142. * cases, the property names seen by your application will vary
  143. * depending on which JDBC driver you are using.</p>
  144. *
  145. * @param resultSet The result set to be wrapped
  146. * @param lowerCase Should property names be lower cased?
  147. *
  148. * @exception NullPointerException if <code>resultSet</code>
  149. * is <code>null</code>
  150. * @exception SQLException if the metadata for this result set
  151. * cannot be introspected
  152. */
  153. public RowSetDynaClass(ResultSet resultSet, boolean lowerCase, int limit)
  154. throws SQLException {
  155. if (resultSet == null) {
  156. throw new NullPointerException();
  157. }
  158. this.lowerCase = lowerCase;
  159. this.limit = limit;
  160. introspect(resultSet);
  161. copy(resultSet);
  162. }
  163. /**
  164. * <p>Return a <code>List</code> containing the {@link DynaBean}s that
  165. * represent the contents of each <code>Row</code> from the
  166. * <code>ResultSet</code> that was the basis of this
  167. * {@link RowSetDynaClass} instance. These {@link DynaBean}s are
  168. * disconnected from the database itself, so there is no problem with
  169. * modifying the contents of the list, or the values of the properties
  170. * of these {@link DynaBean}s. However, it is the application's
  171. * responsibility to persist any such changes back to the database,
  172. * if it so desires.</p>
  173. */
  174. public List getRows() {
  175. return (this.rows);
  176. }
  177. // ------------------------------------------------------ Protected Methods
  178. /**
  179. * <p>Copy the column values for each row in the specified
  180. * <code>ResultSet</code> into a newly created {@link DynaBean}, and add
  181. * this bean to the list of {@link DynaBean}s that will later by
  182. * returned by a call to <code>getRows()</code>.</p>
  183. *
  184. * @param resultSet The <code>ResultSet</code> whose data is to be
  185. * copied
  186. *
  187. * @exception SQLException if an error is encountered copying the data
  188. */
  189. protected void copy(ResultSet resultSet) throws SQLException {
  190. int cnt = 0;
  191. while (resultSet.next() && (limit < 0 || cnt++ < limit) ) {
  192. DynaBean bean = createDynaBean();
  193. for (int i = 0; i < properties.length; i++) {
  194. String name = properties[i].getName();
  195. bean.set(name, resultSet.getObject(name));
  196. }
  197. rows.add(bean);
  198. }
  199. }
  200. /**
  201. * <p>Create and return a new {@link DynaBean} instance to be used for
  202. * representing a row in the underlying result set.</p>
  203. */
  204. protected DynaBean createDynaBean() {
  205. return (new BasicDynaBean(this));
  206. }
  207. }