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
22 import org.apache.commons.dbutils.RowProcessor;
23
24 /**
25 * <p>
26 * {@code ResultSetHandler} implementation that returns a Map of Beans.
27 * {@code ResultSet} rows are converted into Beans which are then stored in
28 * a Map under the given key.
29 * </p>
30 * <p>
31 * If you had a Person table with a primary key column called ID, you could
32 * retrieve rows from the table like this:
33 *
34 * <pre>
35 * ResultSetHandler<Map<Long, Person>> h = new BeanMapHandler<Long, Person>(Person.class, "id");
36 * Map<Long, Person> found = queryRunner.query("select id, name, age from person", h);
37 * Person jane = found.get(1L); // jane's id is 1
38 * String janesName = jane.getName();
39 * Integer janesAge = jane.getAge();
40 * </pre>
41 *
42 * Note that the "id" passed to BeanMapHandler can be in any case. The data type
43 * returned for id is dependent upon how your JDBC driver converts SQL column
44 * types from the Person table into Java types. The "name" and "age" columns are
45 * converted according to their property descriptors by DbUtils.
46 * </p>
47 * <p>
48 * This class is thread safe.
49 * </p>
50 *
51 * @param <K>
52 * the type of keys maintained by the returned map
53 * @param <V>
54 * the type of the bean
55 * @see org.apache.commons.dbutils.ResultSetHandler
56 * @since 1.5
57 */
58 public class BeanMapHandler<K, V> extends AbstractKeyedHandler<K, V> {
59
60 /**
61 * The Class of beans produced by this handler.
62 */
63 private final Class<V> type;
64
65 /**
66 * The RowProcessor implementation to use when converting rows into Objects.
67 */
68 private final RowProcessor convert;
69
70 /**
71 * The column index to retrieve key values from. Defaults to 1.
72 */
73 private final int columnIndex;
74
75 /**
76 * The column name to retrieve key values from. Either columnName or
77 * columnIndex will be used but never both.
78 */
79 private final String columnName;
80
81 /**
82 * Creates a new instance of BeanMapHandler. The value of the first column
83 * of each row will be a key in the Map.
84 *
85 * @param type
86 * The Class that objects returned from {@code createRow()}
87 * are created from.
88 */
89 public BeanMapHandler(final Class<V> type) {
90 this(type, ArrayHandler.ROW_PROCESSOR, 1, null);
91 }
92
93 /**
94 * Creates a new instance of BeanMapHandler.
95 *
96 * @param type
97 * The Class that objects returned from {@code createRow()}
98 * are created from.
99 * @param columnIndex
100 * The values to use as keys in the Map are retrieved from the
101 * column at this index.
102 */
103 public BeanMapHandler(final Class<V> type, final int columnIndex) {
104 this(type, ArrayHandler.ROW_PROCESSOR, columnIndex, null);
105 }
106
107 /**
108 * Creates a new instance of BeanMapHandler. The value of the first column
109 * of each row will be a key in the Map.
110 *
111 * @param type
112 * The Class that objects returned from {@code createRow()}
113 * are created from.
114 * @param convert
115 * The {@code RowProcessor} implementation to use when
116 * converting rows into Beans
117 */
118 public BeanMapHandler(final Class<V> type, final RowProcessor convert) {
119 this(type, convert, 1, null);
120 }
121
122 /**
123 * Private Helper
124 *
125 * @param convert
126 * The {@code RowProcessor} implementation to use when
127 * converting rows into Beans
128 * @param columnIndex
129 * The values to use as keys in the Map are retrieved from the
130 * column at this index.
131 * @param columnName
132 * The values to use as keys in the Map are retrieved from the
133 * column with this name.
134 */
135 private BeanMapHandler(final Class<V> type, final RowProcessor convert,
136 final int columnIndex, final String columnName) {
137 this.type = type;
138 this.convert = convert;
139 this.columnIndex = columnIndex;
140 this.columnName = columnName;
141 }
142
143 /**
144 * Creates a new instance of BeanMapHandler.
145 *
146 * @param type
147 * The Class that objects returned from {@code createRow()}
148 * are created from.
149 * @param columnName
150 * The values to use as keys in the Map are retrieved from the
151 * column with this name.
152 */
153 public BeanMapHandler(final Class<V> type, final String columnName) {
154 this(type, ArrayHandler.ROW_PROCESSOR, 1, columnName);
155 }
156
157 /**
158 * This factory method is called by {@code handle()} to retrieve the
159 * key value from the current {@code ResultSet} row.
160 * @param resultSet ResultSet to create a key from
161 *
162 * @return K from the configured key column name/index
163 *
164 * @throws SQLException if a database access error occurs
165 * @throws ClassCastException if the class datatype does not match the column type
166 *
167 * @see org.apache.commons.dbutils.handlers.AbstractKeyedHandler#createKey(ResultSet)
168 */
169 // We assume that the user has picked the correct type to match the column
170 // so getObject will return the appropriate type and the cast will succeed.
171 @SuppressWarnings("unchecked")
172 @Override
173 protected K createKey(final ResultSet resultSet) throws SQLException {
174 return columnName == null ?
175 (K) resultSet.getObject(columnIndex) :
176 (K) resultSet.getObject(columnName);
177 }
178
179 @Override
180 protected V createRow(final ResultSet resultSet) throws SQLException {
181 return this.convert.toBean(resultSet, type);
182 }
183
184 }