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 * </p>
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 }