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