    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 */
    017package org.apache.commons.dbutils.handlers;
    018
    019import java.sql.ResultSet;
    020import java.sql.SQLException;
    021
    022import org.apache.commons.dbutils.RowProcessor;
    023
    024/**
    025 * <p>
    026 * {@code ResultSetHandler} implementation that returns a Map of Beans.
    027 * {@code ResultSet} rows are converted into Beans which are then stored in
    028 * a Map under the given key.
    029 * </p>
    030 * <p>
    031 * If you had a Person table with a primary key column called ID, you could
    032 * retrieve rows from the table like this:
    033 *
    034 * <pre>
    035 * ResultSetHandler&lt;Map&lt;Long, Person&gt;&gt; h = new BeanMapHandler&lt;Long, Person&gt;(Person.class, &quot;id&quot;);
    036 * Map&lt;Long, Person&gt; found = queryRunner.query(&quot;select id, name, age from person&quot;, h);
    037 * Person jane = found.get(1L); // jane's id is 1
    038 * String janesName = jane.getName();
    039 * Integer janesAge = jane.getAge();
    040 * </pre>
    041 *
    042 * Note that the "id" passed to BeanMapHandler can be in any case. The data type
    043 * returned for id is dependent upon how your JDBC driver converts SQL column
    044 * types from the Person table into Java types. The "name" and "age" columns are
    045 * converted according to their property descriptors by DbUtils.
    046 * &lt;/p&gt;
    047 * <p>
    048 * This class is thread safe.
    049 * &lt;/p&gt;
    050 *
    051 * @param <K>
    052 *            the type of keys maintained by the returned map
    053 * @param <V>
    054 *            the type of the bean
    055 * @see org.apache.commons.dbutils.ResultSetHandler
    056 * @since 1.5
    057 */
    058public class BeanMapHandler<K, V> extends AbstractKeyedHandler<K, V> {
    059
    060    /**
    061     * The Class of beans produced by this handler.
    062     */
    063    private final Class<V> type;
    064
    065    /**
    066     * The RowProcessor implementation to use when converting rows into Objects.
    067     */
    068    private final RowProcessor convert;
    069
    070    /**
    071     * The column index to retrieve key values from. Defaults to 1.
    072     */
    073    private final int columnIndex;
    074
    075    /**
    076     * The column name to retrieve key values from. Either columnName or
    077     * columnIndex will be used but never both.
    078     */
    079    private final String columnName;
    080
    081    /**
    082     * Creates a new instance of BeanMapHandler. The value of the first column
    083     * of each row will be a key in the Map.
    084     *
    085     * @param type
    086     *            The Class that objects returned from {@code createRow()}
    087     *            are created from.
    088     */
    089    public BeanMapHandler(final Class<V> type) {
    090        this(type, ArrayHandler.ROW_PROCESSOR, 1, null);
    091    }
    092
    093    /**
    094     * Creates a new instance of BeanMapHandler.
    095     *
    096     * @param type
    097     *            The Class that objects returned from {@code createRow()}
    098     *            are created from.
    099     * @param columnIndex
    100     *            The values to use as keys in the Map are retrieved from the
    101     *            column at this index.
    102     */
    103    public BeanMapHandler(final Class<V> type, final int columnIndex) {
    104        this(type, ArrayHandler.ROW_PROCESSOR, columnIndex, null);
    105    }
    106
    107    /**
    108     * Creates a new instance of BeanMapHandler. The value of the first column
    109     * of each row will be a key in the Map.
    110     *
    111     * @param type
    112     *            The Class that objects returned from {@code createRow()}
    113     *            are created from.
    114     * @param convert
    115     *            The {@code RowProcessor} implementation to use when
    116     *            converting rows into Beans
    117     */
    118    public BeanMapHandler(final Class<V> type, final RowProcessor convert) {
    119        this(type, convert, 1, null);
    120    }
    121
    122    /**
    123     * Private Helper
    124     *
    125     * @param convert
    126     *            The {@code RowProcessor} implementation to use when
    127     *            converting rows into Beans
    128     * @param columnIndex
    129     *            The values to use as keys in the Map are retrieved from the
    130     *            column at this index.
    131     * @param columnName
    132     *            The values to use as keys in the Map are retrieved from the
    133     *            column with this name.
    134     */
    135    private BeanMapHandler(final Class<V> type, final RowProcessor convert,
    136            final int columnIndex, final String columnName) {
    137        this.type = type;
    138        this.convert = convert;
    139        this.columnIndex = columnIndex;
    140        this.columnName = columnName;
    141    }
    142
    143    /**
    144     * Creates a new instance of BeanMapHandler.
    145     *
    146     * @param type
    147     *            The Class that objects returned from {@code createRow()}
    148     *            are created from.
    149     * @param columnName
    150     *            The values to use as keys in the Map are retrieved from the
    151     *            column with this name.
    152     */
    153    public BeanMapHandler(final Class<V> type, final String columnName) {
    154        this(type, ArrayHandler.ROW_PROCESSOR, 1, columnName);
    155    }
    156
    157    /**
    158     * This factory method is called by {@code handle()} to retrieve the
    159     * key value from the current {@code ResultSet} row.
    160     * @param resultSet ResultSet to create a key from
    161     *
    162     * @return K from the configured key column name/index
    163     *
    164     * @throws SQLException if a database access error occurs
    165     * @throws ClassCastException if the class datatype does not match the column type
    166     *
    167     * @see org.apache.commons.dbutils.handlers.AbstractKeyedHandler#createKey(ResultSet)
    168     */
    169    // We assume that the user has picked the correct type to match the column
    170    // so getObject will return the appropriate type and the cast will succeed.
    171    @SuppressWarnings("unchecked")
    172    @Override
    173    protected K createKey(final ResultSet resultSet) throws SQLException {
    174        return columnName == null ?
    175               (K) resultSet.getObject(columnIndex) :
    176               (K) resultSet.getObject(columnName);
    177    }
    178
    179    @Override
    180    protected V createRow(final ResultSet resultSet) throws SQLException {
    181        return this.convert.toBean(resultSet, type);
    182    }
    183
    184}