    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: 690380 $ $Date: 2008-08-29 21:04:38 +0100 (Fri, 29 Aug 2008) $
    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            if (resultSet == null) {
    134                throw new NullPointerException();
    135            }
    136            this.resultSet = resultSet;
    137            this.lowerCase = lowerCase;
    138            introspect(resultSet);
    139
    140        }
    141
    142
    143        // ----------------------------------------------------- Instance Variables
    144
    145
    146        /**
    147         * <p>The <code>ResultSet</code> we are wrapping.</p>
    148         */
    149        protected ResultSet resultSet = null;
    150
    151
    152        // --------------------------------------------------------- Public Methods
    153
    154
    155        /**
    156         * <p>Return an <code>Iterator</code> of {@link DynaBean} instances for
    157         * each row of the wrapped <code>ResultSet</code>, in "forward" order.
    158         * Unless the underlying result set supports scrolling, this method
    159         * should be called only once.</p>
    160         * @return An <code>Iterator</code> of {@link DynaBean} instances
    161         */
    162        public Iterator iterator() {
    163
    164            return (new ResultSetIterator(this));
    165
    166        }
    167
    168
    169        /**
    170         * Get a value from the {@link ResultSet} for the specified
    171         * property name.
    172         *
    173         * @param name The property name
    174         * @return The value
    175         * @throws SQLException if an error occurs
    176         * @since 1.8.0
    177         */
    178        public Object getObjectFromResultSet(String name) throws SQLException {
    179            return getObject(getResultSet(), name);
    180        }
    181
    182        // -------------------------------------------------------- Package Methods
    183
    184
    185        /**
    186         * <p>Return the result set we are wrapping.</p>
    187         */
    188        ResultSet getResultSet() {
    189
    190            return (this.resultSet);
    191
    192        }
    193
    194
    195        // ------------------------------------------------------ Protected Methods
    196
    197        /**
    198         * <p>Loads the class of the given name which by default uses the class loader used
    199         * to load this library.
    200         * Dervations of this class could implement alternative class loading policies such as
    201         * using custom ClassLoader or using the Threads's context class loader etc.
    202         * </p>
    203         * @param className The name of the class to load
    204         * @return The loaded class
    205         * @throws SQLException if the class cannot be loaded
    206         */
    207        protected Class loadClass(String className) throws SQLException {
    208
    209            try {
    210                return getClass().getClassLoader().loadClass(className);
    211            }
    212            catch (Exception e) {
    213                throw new SQLException("Cannot load column class '" +
    214                                       className + "': " + e);
    215            }
    216        }
    217    }