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}