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 }