View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.dbutils.handlers;
18  
19  import java.sql.ResultSet;
20  import java.sql.SQLException;
21  import java.util.Map;
22  
23  import org.apache.commons.dbutils.RowProcessor;
24  
25  /**
26   * <p>
27   * {@code ResultSetHandler} implementation that returns a Map of Maps.
28   * {@code ResultSet} rows are converted into Maps which are then stored
29   * in a Map under the given key.
30   * </p>
31   * <p>
32   * If you had a Person table with a primary key column called ID, you could
33   * retrieve rows from the table like this:
34   * <pre>
35   * ResultSetHandler h = new KeyedHandler("id");
36   * Map found = (Map) queryRunner.query("select id, name, age from person", h);
37   * Map jane = (Map) found.get(new Long(1)); // jane's id is 1
38   * String janesName = (String) jane.get("name");
39   * Integer janesAge = (Integer) jane.get("age");
40   * </pre>
41   * Note that the "id" passed to KeyedHandler and "name" and "age" passed to the
42   * returned Map's get() method can be in any case.  The data types returned for
43   * name and age are dependent upon how your JDBC driver converts SQL column
44   * types from the Person table into Java types.
45   * &lt;/p&gt;
46   * <p>This class is thread safe.</p>
47   *
48   * @param <K> The type of the key
49   * @see org.apache.commons.dbutils.ResultSetHandler
50   * @since 1.1
51   */
52  public class KeyedHandler<K> extends AbstractKeyedHandler<K, Map<String, Object>> {
53  
54      /**
55       * The RowProcessor implementation to use when converting rows
56       * into Objects.
57       */
58      protected final RowProcessor convert;
59  
60      /**
61       * The column index to retrieve key values from.  Defaults to 1.
62       */
63      protected final int columnIndex;
64  
65      /**
66       * The column name to retrieve key values from.  Either columnName or
67       * columnIndex will be used but never both.
68       */
69      protected final String columnName;
70  
71      /**
72       * Creates a new instance of KeyedHandler.  The value of the first column
73       * of each row will be a key in the Map.
74       */
75      public KeyedHandler() {
76          this(ArrayHandler.ROW_PROCESSOR, 1, null);
77      }
78  
79      /**
80       * Creates a new instance of KeyedHandler.
81       *
82       * @param columnIndex The values to use as keys in the Map are
83       * retrieved from the column at this index.
84       */
85      public KeyedHandler(final int columnIndex) {
86          this(ArrayHandler.ROW_PROCESSOR, columnIndex, null);
87      }
88  
89      /**
90       * Creates a new instance of KeyedHandler.  The value of the first column
91       * of each row will be a key in the Map.
92       *
93       * @param convert The {@code RowProcessor} implementation
94       * to use when converting rows into Maps
95       */
96      public KeyedHandler(final RowProcessor convert) {
97          this(convert, 1, null);
98      }
99  
100     /** Private Helper
101      * @param convert The {@code RowProcessor} implementation
102      * to use when converting rows into Maps
103      * @param columnIndex The values to use as keys in the Map are
104      * retrieved from the column at this index.
105      * @param columnName The values to use as keys in the Map are
106      * retrieved from the column with this name.
107      */
108     private KeyedHandler(final RowProcessor convert, final int columnIndex,
109             final String columnName) {
110         this.convert = convert;
111         this.columnIndex = columnIndex;
112         this.columnName = columnName;
113     }
114 
115     /**
116      * Creates a new instance of KeyedHandler.
117      *
118      * @param columnName The values to use as keys in the Map are
119      * retrieved from the column with this name.
120      */
121     public KeyedHandler(final String columnName) {
122         this(ArrayHandler.ROW_PROCESSOR, 1, columnName);
123     }
124     /**
125      * This factory method is called by {@code handle()} to retrieve the
126      * key value from the current {@code ResultSet} row.  This
127      * implementation returns {@code ResultSet.getObject()} for the
128      * configured key column name or index.
129      * @param rs ResultSet to create a key from
130      * @return Object from the configured key column name/index
131      *
132      * @throws SQLException if a database access error occurs
133      * @throws ClassCastException if the class datatype does not match the column type
134      */
135     // We assume that the user has picked the correct type to match the column
136     // so getObject will return the appropriate type and the cast will succeed.
137     @SuppressWarnings("unchecked")
138     @Override
139     protected K createKey(final ResultSet rs) throws SQLException {
140         return columnName == null ?
141                (K) rs.getObject(columnIndex) :
142                (K) rs.getObject(columnName);
143     }
144 
145     /**
146      * This factory method is called by {@code handle()} to store the
147      * current {@code ResultSet} row in some object. This
148      * implementation returns a {@code Map} with case insensitive column
149      * names as keys.  Calls to {@code map.get("COL")} and
150      * {@code map.get("col")} return the same value.
151      * @param resultSet ResultSet to create a row from
152      * @return Object typed Map containing column names to values
153      * @throws SQLException if a database access error occurs
154      */
155     @Override
156     protected Map<String, Object> createRow(final ResultSet resultSet) throws SQLException {
157         return this.convert.toMap(resultSet);
158     }
159 
160 }