    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    }