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    package org.apache.commons.dbutils.handlers;
018    
019    import java.sql.ResultSet;
020    import java.sql.SQLException;
021    
022    import 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     */
058    public 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    }