    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
    019package org.apache.commons.beanutils;
    020
    021
    022import java.sql.ResultSet;
    023import java.sql.SQLException;
    024import 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 * @version $Id$
    081 */
    082
    083public class ResultSetDynaClass extends JDBCDynaClass implements DynaClass {
    084
    085
    086    // ----------------------------------------------------------- Constructors
    087
    088
    089    /**
    090     * <p>Construct a new ResultSetDynaClass for the specified
    091     * <code>ResultSet</code>.  The property names corresponding
    092     * to column names in the result set will be lower cased.</p>
    093     *
    094     * @param resultSet The result set to be wrapped
    095     *
    096     * @throws NullPointerException if <code>resultSet</code>
    097     *  is <code>null</code>
    098     * @throws SQLException if the metadata for this result set
    099     *  cannot be introspected
    100     */
    101    public ResultSetDynaClass(final ResultSet resultSet) throws SQLException {
    102
    103        this(resultSet, true);
    104
    105    }
    106
    107
    108    /**
    109     * <p>Construct a new ResultSetDynaClass for the specified
    110     * <code>ResultSet</code>.  The property names corresponding
    111     * to the column names in the result set will be lower cased or not,
    112     * depending on the specified <code>lowerCase</code> value.</p>
    113     *
    114     * <p><strong>WARNING</strong> - If you specify <code>false</code>
    115     * for <code>lowerCase</code>, the returned property names will
    116     * exactly match the column names returned by your JDBC driver.
    117     * Because different drivers might return column names in different
    118     * cases, the property names seen by your application will vary
    119     * depending on which JDBC driver you are using.</p>
    120     *
    121     * @param resultSet The result set to be wrapped
    122     * @param lowerCase Should property names be lower cased?
    123     *
    124     * @throws NullPointerException if <code>resultSet</code>
    125     *  is <code>null</code>
    126     * @throws SQLException if the metadata for this result set
    127     *  cannot be introspected
    128     */
    129    public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase)
    130        throws SQLException {
    131
    132        this(resultSet, lowerCase, false);
    133
    134    }
    135
    136
    137    /**
    138     * <p>Construct a new ResultSetDynaClass for the specified
    139     * <code>ResultSet</code>.  The property names corresponding
    140     * to the column names in the result set will be lower cased or not,
    141     * depending on the specified <code>lowerCase</code> value.</p>
    142     *
    143     * <p><strong>WARNING</strong> - If you specify <code>false</code>
    144     * for <code>lowerCase</code>, the returned property names will
    145     * exactly match the column names returned by your JDBC driver.
    146     * Because different drivers might return column names in different
    147     * cases, the property names seen by your application will vary
    148     * depending on which JDBC driver you are using.</p>
    149     *
    150     * @param resultSet The result set to be wrapped
    151     * @param lowerCase Should property names be lower cased?
    152     * @param useColumnLabel true if the column label should be used, otherwise false
    153     *
    154     * @throws NullPointerException if <code>resultSet</code>
    155     *  is <code>null</code>
    156     * @throws SQLException if the metadata for this result set
    157     *  cannot be introspected
    158     * @since 1.8.3
    159     */
    160    public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel)
    161        throws SQLException {
    162
    163        if (resultSet == null) {
    164            throw new NullPointerException();
    165        }
    166        this.resultSet = resultSet;
    167        this.lowerCase = lowerCase;
    168        setUseColumnLabel(useColumnLabel);
    169        introspect(resultSet);
    170
    171    }
    172
    173
    174    // ----------------------------------------------------- Instance Variables
    175
    176
    177    /**
    178     * <p>The <code>ResultSet</code> we are wrapping.</p>
    179     */
    180    protected ResultSet resultSet = null;
    181
    182
    183    // --------------------------------------------------------- Public Methods
    184
    185
    186    /**
    187     * <p>Return an <code>Iterator</code> of {@link DynaBean} instances for
    188     * each row of the wrapped <code>ResultSet</code>, in "forward" order.
    189     * Unless the underlying result set supports scrolling, this method
    190     * should be called only once.</p>
    191     * @return An <code>Iterator</code> of {@link DynaBean} instances
    192     */
    193    public Iterator<DynaBean> iterator() {
    194
    195        return (new ResultSetIterator(this));
    196
    197    }
    198
    199
    200    /**
    201     * Get a value from the {@link ResultSet} for the specified
    202     * property name.
    203     *
    204     * @param name The property name
    205     * @return The value
    206     * @throws SQLException if an error occurs
    207     * @since 1.8.0
    208     */
    209    public Object getObjectFromResultSet(final String name) throws SQLException {
    210        return getObject(getResultSet(), name);
    211    }
    212
    213    // -------------------------------------------------------- Package Methods
    214
    215
    216    /**
    217     * <p>Return the result set we are wrapping.</p>
    218     */
    219    ResultSet getResultSet() {
    220
    221        return (this.resultSet);
    222
    223    }
    224
    225
    226    // ------------------------------------------------------ Protected Methods
    227
    228    /**
    229     * <p>Loads the class of the given name which by default uses the class loader used
    230     * to load this library.
    231     * Dervations of this class could implement alternative class loading policies such as
    232     * using custom ClassLoader or using the Threads's context class loader etc.
    233     * </p>
    234     * @param className The name of the class to load
    235     * @return The loaded class
    236     * @throws SQLException if the class cannot be loaded
    237     */
    238    @Override
    239    protected Class<?> loadClass(final String className) throws SQLException {
    240
    241        try {
    242            return getClass().getClassLoader().loadClass(className);
    243        }
    244        catch (final Exception e) {
    245            throw new SQLException("Cannot load column class '" +
    246                                   className + "': " + e);
    247        }
    248    }
    249}