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 */ 017package org.apache.commons.dbutils.handlers; 018 019import java.sql.ResultSet; 020import java.sql.SQLException; 021import java.util.Map; 022 023import org.apache.commons.dbutils.RowProcessor; 024 025/** 026 * <p> 027 * {@code ResultSetHandler} implementation that returns a Map of Maps. 028 * {@code ResultSet} rows are converted into Maps which are then stored 029 * in a Map under the given key. 030 * </p> 031 * <p> 032 * If you had a Person table with a primary key column called ID, you could 033 * retrieve rows from the table like this: 034 * <pre> 035 * ResultSetHandler h = new KeyedHandler("id"); 036 * Map found = (Map) queryRunner.query("select id, name, age from person", h); 037 * Map jane = (Map) found.get(new Long(1)); // jane's id is 1 038 * String janesName = (String) jane.get("name"); 039 * Integer janesAge = (Integer) jane.get("age"); 040 * </pre> 041 * Note that the "id" passed to KeyedHandler and "name" and "age" passed to the 042 * returned Map's get() method can be in any case. The data types returned for 043 * name and age are dependent upon how your JDBC driver converts SQL column 044 * types from the Person table into Java types. 045 * </p> 046 * <p>This class is thread safe.</p> 047 * 048 * @param <K> The type of the key 049 * @see org.apache.commons.dbutils.ResultSetHandler 050 * @since 1.1 051 */ 052public class KeyedHandler<K> extends AbstractKeyedHandler<K, Map<String, Object>> { 053 054 /** 055 * The RowProcessor implementation to use when converting rows 056 * into Objects. 057 */ 058 protected final RowProcessor convert; 059 060 /** 061 * The column index to retrieve key values from. Defaults to 1. 062 */ 063 protected final int columnIndex; 064 065 /** 066 * The column name to retrieve key values from. Either columnName or 067 * columnIndex will be used but never both. 068 */ 069 protected final String columnName; 070 071 /** 072 * Creates a new instance of KeyedHandler. The value of the first column 073 * of each row will be a key in the Map. 074 */ 075 public KeyedHandler() { 076 this(ArrayHandler.ROW_PROCESSOR, 1, null); 077 } 078 079 /** 080 * Creates a new instance of KeyedHandler. 081 * 082 * @param columnIndex The values to use as keys in the Map are 083 * retrieved from the column at this index. 084 */ 085 public KeyedHandler(final int columnIndex) { 086 this(ArrayHandler.ROW_PROCESSOR, columnIndex, null); 087 } 088 089 /** 090 * Creates a new instance of KeyedHandler. The value of the first column 091 * of each row will be a key in the Map. 092 * 093 * @param convert The {@code RowProcessor} implementation 094 * to use when converting rows into Maps 095 */ 096 public KeyedHandler(final RowProcessor convert) { 097 this(convert, 1, null); 098 } 099 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}