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:
018: package org.apache.commons.beanutils;
019:
020: import java.io.Serializable;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.util.ArrayList;
024: import java.util.List;
025:
026: /**
027: * <p>Implementation of {@link DynaClass} that creates an in-memory collection
028: * of {@link DynaBean}s representing the results of an SQL query. Once the
029: * {@link DynaClass} instance has been created, the JDBC <code>ResultSet</code>
030: * and <code>Statement</code> on which it is based can be closed, and the
031: * underlying <code>Connection</code> can be returned to its connection pool
032: * (if you are using one).</p>
033: *
034: * <p>The normal usage pattern is something like:</p>
035: * <pre>
036: * Connection conn = ...; // Acquire connection from pool
037: * Statement stmt = conn.createStatement();
038: * ResultSet rs = stmt.executeQuery("SELECT ...");
039: * RowSetDynaClass rsdc = new RowSetDynaClass(rs);
040: * rs.close();
041: * stmt.close();
042: * ...; // Return connection to pool
043: * List rows = rsdc.getRows();
044: * ...; // Process the rows as desired
045: * </pre>
046: *
047: * <p>Each column in the result set will be represented as a {@link DynaBean}
048: * property of the corresponding name (optionally forced to lower case
049: * for portability). There will be one {@link DynaBean} in the
050: * <code>List</code> returned by <code>getRows()</code> for each
051: * row in the original <code>ResultSet</code>.</p>
052: *
053: * <p>In general, instances of {@link RowSetDynaClass} can be serialized
054: * and deserialized, which will automatically include the list of
055: * {@link DynaBean}s representing the data content. The only exception
056: * to this rule would be when the underlying property values that were
057: * copied from the <code>ResultSet</code> originally cannot themselves
058: * be serialized. Therefore, a {@link RowSetDynaClass} makes a very
059: * convenient mechanism for transporting data sets to remote Java-based
060: * application components.</p>
061: *
062: * @author Craig R. McClanahan
063: * @version $Revision: 556233 $ $Date: 2007-07-14 07:37:06 +0100 (Sat, 14 Jul 2007) $
064: */
065:
066: public class RowSetDynaClass extends JDBCDynaClass implements
067: DynaClass, Serializable {
068:
069: // ----------------------------------------------------- Instance variables
070:
071: /**
072: * <p>Limits the size of the returned list. The call to
073: * <code>getRows()</code> will return at most limit number of rows.
074: * If less than or equal to 0, does not limit the size of the result.
075: */
076: protected int limit = -1;
077:
078: /**
079: * <p>The list of {@link DynaBean}s representing the contents of
080: * the original <code>ResultSet</code> on which this
081: * {@link RowSetDynaClass} was based.</p>
082: */
083: protected List rows = new ArrayList();
084:
085: // ----------------------------------------------------------- Constructors
086:
087: /**
088: * <p>Construct a new {@link RowSetDynaClass} for the specified
089: * <code>ResultSet</code>. The property names corresponding
090: * to column names in the result set will be lower cased.</p>
091: *
092: * @param resultSet The result set to be wrapped
093: *
094: * @exception NullPointerException if <code>resultSet</code>
095: * is <code>null</code>
096: * @exception SQLException if the metadata for this result set
097: * cannot be introspected
098: */
099: public RowSetDynaClass(ResultSet resultSet) throws SQLException {
100:
101: this (resultSet, true, -1);
102:
103: }
104:
105: /**
106: * <p>Construct a new {@link RowSetDynaClass} for the specified
107: * <code>ResultSet</code>. The property names corresponding
108: * to column names in the result set will be lower cased.</p>
109: *
110: * If <code>limit</code> is not less than 0, max <code>limit</code>
111: * number of rows will be copied into the list.
112: *
113: * @param resultSet The result set to be wrapped
114: * @param limit The maximum for the size of the result.
115: *
116: * @exception NullPointerException if <code>resultSet</code>
117: * is <code>null</code>
118: * @exception SQLException if the metadata for this result set
119: * cannot be introspected
120: */
121: public RowSetDynaClass(ResultSet resultSet, int limit)
122: throws SQLException {
123:
124: this (resultSet, true, limit);
125:
126: }
127:
128: /**
129: * <p>Construct a new {@link RowSetDynaClass} for the specified
130: * <code>ResultSet</code>. The property names corresponding
131: * to the column names in the result set will be lower cased or not,
132: * depending on the specified <code>lowerCase</code> value.</p>
133: *
134: * If <code>limit</code> is not less than 0, max <code>limit</code>
135: * number of rows will be copied into the resultset.
136: *
137: *
138: * @param resultSet The result set to be wrapped
139: * @param lowerCase Should property names be lower cased?
140: *
141: * @exception NullPointerException if <code>resultSet</code>
142: * is <code>null</code>
143: * @exception SQLException if the metadata for this result set
144: * cannot be introspected
145: */
146: public RowSetDynaClass(ResultSet resultSet, boolean lowerCase)
147: throws SQLException {
148: this (resultSet, lowerCase, -1);
149:
150: }
151:
152: /**
153: * <p>Construct a new {@link RowSetDynaClass} for the specified
154: * <code>ResultSet</code>. The property names corresponding
155: * to the column names in the result set will be lower cased or not,
156: * depending on the specified <code>lowerCase</code> value.</p>
157: *
158: * <p><strong>WARNING</strong> - If you specify <code>false</code>
159: * for <code>lowerCase</code>, the returned property names will
160: * exactly match the column names returned by your JDBC driver.
161: * Because different drivers might return column names in different
162: * cases, the property names seen by your application will vary
163: * depending on which JDBC driver you are using.</p>
164: *
165: * @param resultSet The result set to be wrapped
166: * @param lowerCase Should property names be lower cased?
167: * @param limit Maximum limit for the <code>List</code> of {@link DynaBean}
168: *
169: * @exception NullPointerException if <code>resultSet</code>
170: * is <code>null</code>
171: * @exception SQLException if the metadata for this result set
172: * cannot be introspected
173: */
174: public RowSetDynaClass(ResultSet resultSet, boolean lowerCase,
175: int limit) throws SQLException {
176:
177: if (resultSet == null) {
178: throw new NullPointerException();
179: }
180: this .lowerCase = lowerCase;
181: this .limit = limit;
182: introspect(resultSet);
183: copy(resultSet);
184:
185: }
186:
187: /**
188: * <p>Return a <code>List</code> containing the {@link DynaBean}s that
189: * represent the contents of each <code>Row</code> from the
190: * <code>ResultSet</code> that was the basis of this
191: * {@link RowSetDynaClass} instance. These {@link DynaBean}s are
192: * disconnected from the database itself, so there is no problem with
193: * modifying the contents of the list, or the values of the properties
194: * of these {@link DynaBean}s. However, it is the application's
195: * responsibility to persist any such changes back to the database,
196: * if it so desires.</p>
197: *
198: * @return A <code>List</code> of {@link DynaBean} instances
199: */
200: public List getRows() {
201:
202: return (this .rows);
203:
204: }
205:
206: // ------------------------------------------------------ Protected Methods
207:
208: /**
209: * <p>Copy the column values for each row in the specified
210: * <code>ResultSet</code> into a newly created {@link DynaBean}, and add
211: * this bean to the list of {@link DynaBean}s that will later by
212: * returned by a call to <code>getRows()</code>.</p>
213: *
214: * @param resultSet The <code>ResultSet</code> whose data is to be
215: * copied
216: *
217: * @exception SQLException if an error is encountered copying the data
218: */
219: protected void copy(ResultSet resultSet) throws SQLException {
220:
221: int cnt = 0;
222: while (resultSet.next() && (limit < 0 || cnt++ < limit)) {
223: DynaBean bean = createDynaBean();
224: for (int i = 0; i < properties.length; i++) {
225: String name = properties[i].getName();
226: Object value = getObject(resultSet, name);
227: bean.set(name, value);
228: }
229: rows.add(bean);
230: }
231:
232: }
233:
234: /**
235: * <p>Create and return a new {@link DynaBean} instance to be used for
236: * representing a row in the underlying result set.</p>
237: *
238: * @return A new <code>DynaBean</code> instance
239: */
240: protected DynaBean createDynaBean() {
241:
242: return (new BasicDynaBean(this));
243:
244: }
245:
246: }
|