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</code> implementation that returns a Map of Beans.
027 * <code>ResultSet</code> 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 BeanMapdHandler&lt;Long, Person&gt;(Person.class, &quot;id&quot;);
036 * Map&amp;ltLong, 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 * </p>
047 * <p>
048 * This class is thread safe.
049 * </p>
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 DbUtils 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()</code>
087     *            are created from.
088     */
089    public BeanMapHandler(Class<V> type) {
090        this(type, ArrayHandler.ROW_PROCESSOR, 1, null);
091    }
092
093    /**
094     * Creates a new instance of BeanMapHandler. The value of the first column
095     * of each row will be a key in the Map.
096     *
097     * @param type
098     *            The Class that objects returned from <code>createRow()</code>
099     *            are created from.
100     * @param convert
101     *            The <code>RowProcessor</code> implementation to use when
102     *            converting rows into Beans
103     */
104    public BeanMapHandler(Class<V> type, RowProcessor convert) {
105        this(type, convert, 1, null);
106    }
107
108    /**
109     * Creates a new instance of BeanMapHandler.
110     *
111     * @param type
112     *            The Class that objects returned from <code>createRow()</code>
113     *            are created from.
114     * @param columnIndex
115     *            The values to use as keys in the Map are retrieved from the
116     *            column at this index.
117     */
118    public BeanMapHandler(Class<V> type, int columnIndex) {
119        this(type, ArrayHandler.ROW_PROCESSOR, columnIndex, null);
120    }
121
122    /**
123     * Creates a new instance of BeanMapHandler.
124     *
125     * @param type
126     *            The Class that objects returned from <code>createRow()</code>
127     *            are created from.
128     * @param columnName
129     *            The values to use as keys in the Map are retrieved from the
130     *            column with this name.
131     */
132    public BeanMapHandler(Class<V> type, String columnName) {
133        this(type, ArrayHandler.ROW_PROCESSOR, 1, columnName);
134    }
135
136    /**
137     * Private Helper
138     *
139     * @param convert
140     *            The <code>RowProcessor</code> implementation to use when
141     *            converting rows into Beans
142     * @param columnIndex
143     *            The values to use as keys in the Map are retrieved from the
144     *            column at this index.
145     * @param columnName
146     *            The values to use as keys in the Map are retrieved from the
147     *            column with this name.
148     */
149    private BeanMapHandler(Class<V> type, RowProcessor convert,
150            int columnIndex, String columnName) {
151        super();
152        this.type = type;
153        this.convert = convert;
154        this.columnIndex = columnIndex;
155        this.columnName = columnName;
156    }
157
158    /**
159     * This factory method is called by <code>handle()</code> to retrieve the
160     * key value from the current <code>ResultSet</code> row.
161     * @param rs ResultSet to create a key from
162     *
163     * @return K from the configured key column name/index
164     *
165     * @throws SQLException if a database access error occurs
166     * @throws ClassCastException if the class datatype does not match the column type
167     *
168     * @see org.apache.commons.dbutils.handlers.AbstractKeyedHandler#createKey(ResultSet)
169     */
170    // We assume that the user has picked the correct type to match the column
171    // so getObject will return the appropriate type and the cast will succeed.
172    @SuppressWarnings("unchecked")
173    @Override
174    protected K createKey(ResultSet rs) throws SQLException {
175        return (columnName == null) ?
176               (K) rs.getObject(columnIndex) :
177               (K) rs.getObject(columnName);
178    }
179
180    @Override
181    protected V createRow(ResultSet rs) throws SQLException {
182        return this.convert.toBean(rs, type);
183    }
184
185}