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    }