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.HashMap; 22 import java.util.Map; 23 24 import org.apache.commons.dbutils.ResultSetHandler; 25 import org.apache.commons.dbutils.RowProcessor; 26 27 /** 28 * <p> 29 * <code>ResultSetHandler</code> implementation that returns a Map of Maps. 30 * <code>ResultSet</code> rows are converted into Maps which are then stored 31 * in a Map under the given key. Although this implementation uses Maps to 32 * store row contents, subclasses are encouraged to override the 33 * <code>createRow()</code> method to convert the rows into any kind of object. 34 * </p> 35 * <p> 36 * If you had a Person table with a primary key column called ID, you could 37 * retrieve rows from the table like this: 38 * <pre> 39 * ResultSetHandler h = new KeyedHandler("id"); 40 * Map found = (Map) queryRunner.query("select id, name, age from person", h); 41 * Map jane = (Map) found.get(new Long(1)); // jane's id is 1 42 * String janesName = (String) jane.get("name"); 43 * Integer janesAge = (Integer) jane.get("age"); 44 * </pre> 45 * Note that the "id" passed to KeyedHandler and "name" and "age" passed to the 46 * returned Map's get() method can be in any case. The data types returned for 47 * name and age are dependent upon how your JDBC driver converts SQL column 48 * types from the Person table into Java types. 49 * </p> 50 * <p> 51 * To avoid these type issues you could subclass KeyedHandler and override 52 * <code>createRow()</code> to store rows in Java bean instances (ie. a 53 * Person class). 54 * </p> 55 * <p>This class is thread safe.</p> 56 * 57 * @see org.apache.commons.dbutils.ResultSetHandler 58 * @since DbUtils 1.1 59 */ 60 public class KeyedHandler implements ResultSetHandler { 61 62 /** 63 * The RowProcessor implementation to use when converting rows 64 * into Objects. 65 */ 66 protected final RowProcessor convert; 67 68 /** 69 * The column index to retrieve key values from. Defaults to 1. 70 */ 71 protected final int columnIndex; 72 73 /** 74 * The column name to retrieve key values from. Either columnName or 75 * columnIndex will be used but never both. 76 */ 77 protected final String columnName; 78 79 /** 80 * Creates a new instance of KeyedHandler. The value of the first column 81 * of each row will be a key in the Map. 82 */ 83 public KeyedHandler() { 84 this(ArrayHandler.ROW_PROCESSOR, 1, null); 85 } 86 87 /** 88 * Creates a new instance of KeyedHandler. The value of the first column 89 * of each row will be a key in the Map. 90 * 91 * @param convert The <code>RowProcessor</code> implementation 92 * to use when converting rows into Maps 93 */ 94 public KeyedHandler(RowProcessor convert) { 95 this(convert, 1, null); 96 } 97 98 /** 99 * 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 }