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