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
019 package org.apache.commons.beanutils;
020
021
022 import java.sql.ResultSet;
023 import java.sql.SQLException;
024 import java.util.Iterator;
025
026
027 /**
028 * <p>Implementation of <code>DynaClass</code> for DynaBeans that wrap the
029 * <code>java.sql.Row</code> objects of a <code>java.sql.ResultSet</code>.
030 * The normal usage pattern is something like:</p>
031 * <pre>
032 * ResultSet rs = ...;
033 * ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
034 * Iterator rows = rsdc.iterator();
035 * while (rows.hasNext()) {
036 * DynaBean row = (DynaBean) rows.next();
037 * ... process this row ...
038 * }
039 * rs.close();
040 * </pre>
041 *
042 * <p>Each column in the result set will be represented as a DynaBean
043 * property of the corresponding name (optionally forced to lower case
044 * for portability).</p>
045 *
046 * <p><strong>WARNING</strong> - Any {@link DynaBean} instance returned by
047 * this class, or from the <code>Iterator</code> returned by the
048 * <code>iterator()</code> method, is directly linked to the row that the
049 * underlying result set is currently positioned at. This has the following
050 * implications:</p>
051 * <ul>
052 * <li>Once you retrieve a different {@link DynaBean} instance, you should
053 * no longer use any previous instance.</li>
054 * <li>Changing the position of the underlying result set will change the
055 * data that the {@link DynaBean} references.</li>
056 * <li>Once the underlying result set is closed, the {@link DynaBean}
057 * instance may no longer be used.</li>
058 * </ul>
059 *
060 * <p>Any database data that you wish to utilize outside the context of the
061 * current row of an open result set must be copied. For example, you could
062 * use the following code to create standalone copies of the information in
063 * a result set:</p>
064 * <pre>
065 * ArrayList results = new ArrayList(); // To hold copied list
066 * ResultSetDynaClass rsdc = ...;
067 * DynaProperty[] properties = rsdc.getDynaProperties();
068 * BasicDynaClass bdc =
069 * new BasicDynaClass("foo", BasicDynaBean.class,
070 * rsdc.getDynaProperties());
071 * Iterator rows = rsdc.iterator();
072 * while (rows.hasNext()) {
073 * DynaBean oldRow = (DynaBean) rows.next();
074 * DynaBean newRow = bdc.newInstance();
075 * PropertyUtils.copyProperties(newRow, oldRow);
076 * results.add(newRow);
077 * }
078 * </pre>
079 *
080 * @author Craig R. McClanahan
081 * @version $Revision: 926685 $ $Date: 2010-03-23 17:59:08 +0000 (Tue, 23 Mar 2010) $
082 */
083
084 public class ResultSetDynaClass extends JDBCDynaClass implements DynaClass {
085
086
087 // ----------------------------------------------------------- Constructors
088
089
090 /**
091 * <p>Construct a new ResultSetDynaClass for the specified
092 * <code>ResultSet</code>. The property names corresponding
093 * to column names in the result set will be lower cased.</p>
094 *
095 * @param resultSet The result set to be wrapped
096 *
097 * @exception NullPointerException if <code>resultSet</code>
098 * is <code>null</code>
099 * @exception SQLException if the metadata for this result set
100 * cannot be introspected
101 */
102 public ResultSetDynaClass(ResultSet resultSet) throws SQLException {
103
104 this(resultSet, true);
105
106 }
107
108
109 /**
110 * <p>Construct a new ResultSetDynaClass for the specified
111 * <code>ResultSet</code>. The property names corresponding
112 * to the column names in the result set will be lower cased or not,
113 * depending on the specified <code>lowerCase</code> value.</p>
114 *
115 * <p><strong>WARNING</strong> - If you specify <code>false</code>
116 * for <code>lowerCase</code>, the returned property names will
117 * exactly match the column names returned by your JDBC driver.
118 * Because different drivers might return column names in different
119 * cases, the property names seen by your application will vary
120 * depending on which JDBC driver you are using.</p>
121 *
122 * @param resultSet The result set to be wrapped
123 * @param lowerCase Should property names be lower cased?
124 *
125 * @exception NullPointerException if <code>resultSet</code>
126 * is <code>null</code>
127 * @exception SQLException if the metadata for this result set
128 * cannot be introspected
129 */
130 public ResultSetDynaClass(ResultSet resultSet, boolean lowerCase)
131 throws SQLException {
132
133 this(resultSet, lowerCase, false);
134
135 }
136
137
138 /**
139 * <p>Construct a new ResultSetDynaClass for the specified
140 * <code>ResultSet</code>. The property names corresponding
141 * to the column names in the result set will be lower cased or not,
142 * depending on the specified <code>lowerCase</code> value.</p>
143 *
144 * <p><strong>WARNING</strong> - If you specify <code>false</code>
145 * for <code>lowerCase</code>, the returned property names will
146 * exactly match the column names returned by your JDBC driver.
147 * Because different drivers might return column names in different
148 * cases, the property names seen by your application will vary
149 * depending on which JDBC driver you are using.</p>
150 *
151 * @param resultSet The result set to be wrapped
152 * @param lowerCase Should property names be lower cased?
153 * @param useColumnLabel true if the column label should be used, otherwise false
154 *
155 * @exception NullPointerException if <code>resultSet</code>
156 * is <code>null</code>
157 * @exception SQLException if the metadata for this result set
158 * cannot be introspected
159 * @since 1.8.3
160 */
161 public ResultSetDynaClass(ResultSet resultSet, boolean lowerCase, boolean useColumnLabel)
162 throws SQLException {
163
164 if (resultSet == null) {
165 throw new NullPointerException();
166 }
167 this.resultSet = resultSet;
168 this.lowerCase = lowerCase;
169 setUseColumnLabel(useColumnLabel);
170 introspect(resultSet);
171
172 }
173
174
175 // ----------------------------------------------------- Instance Variables
176
177
178 /**
179 * <p>The <code>ResultSet</code> we are wrapping.</p>
180 */
181 protected ResultSet resultSet = null;
182
183
184 // --------------------------------------------------------- Public Methods
185
186
187 /**
188 * <p>Return an <code>Iterator</code> of {@link DynaBean} instances for
189 * each row of the wrapped <code>ResultSet</code>, in "forward" order.
190 * Unless the underlying result set supports scrolling, this method
191 * should be called only once.</p>
192 * @return An <code>Iterator</code> of {@link DynaBean} instances
193 */
194 public Iterator iterator() {
195
196 return (new ResultSetIterator(this));
197
198 }
199
200
201 /**
202 * Get a value from the {@link ResultSet} for the specified
203 * property name.
204 *
205 * @param name The property name
206 * @return The value
207 * @throws SQLException if an error occurs
208 * @since 1.8.0
209 */
210 public Object getObjectFromResultSet(String name) throws SQLException {
211 return getObject(getResultSet(), name);
212 }
213
214 // -------------------------------------------------------- Package Methods
215
216
217 /**
218 * <p>Return the result set we are wrapping.</p>
219 */
220 ResultSet getResultSet() {
221
222 return (this.resultSet);
223
224 }
225
226
227 // ------------------------------------------------------ Protected Methods
228
229 /**
230 * <p>Loads the class of the given name which by default uses the class loader used
231 * to load this library.
232 * Dervations of this class could implement alternative class loading policies such as
233 * using custom ClassLoader or using the Threads's context class loader etc.
234 * </p>
235 * @param className The name of the class to load
236 * @return The loaded class
237 * @throws SQLException if the class cannot be loaded
238 */
239 protected Class loadClass(String className) throws SQLException {
240
241 try {
242 return getClass().getClassLoader().loadClass(className);
243 }
244 catch (Exception e) {
245 throw new SQLException("Cannot load column class '" +
246 className + "': " + e);
247 }
248 }
249 }