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 DbUtils 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. The value of the first column 081 * of each row will be a key in the Map. 082 * 083 * @param convert The {@code RowProcessor} implementation 084 * to use when converting rows into Maps 085 */ 086 public KeyedHandler(final RowProcessor convert) { 087 this(convert, 1, null); 088 } 089 090 /** 091 * Creates a new instance of KeyedHandler. 092 * 093 * @param columnIndex The values to use as keys in the Map are 094 * retrieved from the column at this index. 095 */ 096 public KeyedHandler(final int columnIndex) { 097 this(ArrayHandler.ROW_PROCESSOR, columnIndex, null); 098 } 099 100 /** 101 * Creates a new instance of KeyedHandler. 102 * 103 * @param columnName The values to use as keys in the Map are 104 * retrieved from the column with this name. 105 */ 106 public KeyedHandler(final String columnName) { 107 this(ArrayHandler.ROW_PROCESSOR, 1, columnName); 108 } 109 110 /** Private Helper 111 * @param convert The {@code RowProcessor} implementation 112 * to use when converting rows into Maps 113 * @param columnIndex The values to use as keys in the Map are 114 * retrieved from the column at this index. 115 * @param columnName The values to use as keys in the Map are 116 * retrieved from the column with this name. 117 */ 118 private KeyedHandler(final RowProcessor convert, final int columnIndex, 119 final String columnName) { 120 super(); 121 this.convert = convert; 122 this.columnIndex = columnIndex; 123 this.columnName = columnName; 124 } 125 /** 126 * This factory method is called by {@code handle()} to retrieve the 127 * key value from the current {@code ResultSet} row. This 128 * implementation returns {@code ResultSet.getObject()} for the 129 * configured key column name or index. 130 * @param rs ResultSet to create a key from 131 * @return Object from the configured key column name/index 132 * 133 * @throws SQLException if a database access error occurs 134 * @throws ClassCastException if the class datatype does not match the column type 135 */ 136 // We assume that the user has picked the correct type to match the column 137 // so getObject will return the appropriate type and the cast will succeed. 138 @SuppressWarnings("unchecked") 139 @Override 140 protected K createKey(final ResultSet rs) throws SQLException { 141 return (columnName == null) ? 142 (K) rs.getObject(columnIndex) : 143 (K) rs.getObject(columnName); 144 } 145 146 /** 147 * This factory method is called by {@code handle()} to store the 148 * current {@code ResultSet} row in some object. This 149 * implementation returns a {@code Map} with case insensitive column 150 * names as keys. Calls to {@code map.get("COL")} and 151 * {@code map.get("col")} return the same value. 152 * @param rs ResultSet to create a row from 153 * @return Object typed Map containing column names to values 154 * @throws SQLException if a database access error occurs 155 */ 156 @Override 157 protected Map<String, Object> createRow(final ResultSet rs) throws SQLException { 158 return this.convert.toMap(rs); 159 } 160 161}