View Javadoc

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;
18  
19  import java.sql.ResultSet;
20  import java.sql.ResultSetMetaData;
21  import java.sql.SQLException;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  /**
27   * Basic implementation of the <code>RowProcessor</code> interface.
28   * 
29   * <p>
30   * This class is thread-safe.
31   * </p>
32   * 
33   * @see RowProcessor
34   */
35  public class BasicRowProcessor implements RowProcessor {
36  
37      /**
38       * The default BeanProcessor instance to use if not supplied in the
39       * constructor.
40       */
41      private static final BeanProcessor defaultConvert = new BeanProcessor();
42  
43      /**
44       * The Singleton instance of this class.
45       */
46      private static final BasicRowProcessor instance = new BasicRowProcessor();
47  
48      /**
49       * Returns the Singleton instance of this class.
50       *
51       * @return The single instance of this class.
52       * @deprecated Create instances with the constructors instead.  This will 
53       * be removed after DbUtils 1.1.
54       */
55      public static BasicRowProcessor instance() {
56          return instance;
57      }
58  
59      /**
60       * Use this to process beans.
61       */
62      private final BeanProcessor convert;
63  
64      /**
65       * BasicRowProcessor constructor.  Bean processing defaults to a 
66       * BeanProcessor instance.
67       */
68      public BasicRowProcessor() {
69          this(defaultConvert);
70      }
71      
72      /**
73       * BasicRowProcessor constructor.
74       * @param convert The BeanProcessor to use when converting columns to 
75       * bean properties.
76       * @since DbUtils 1.1
77       */
78      public BasicRowProcessor(BeanProcessor convert) {
79          super();
80          this.convert = convert;
81      }
82  
83      /**
84       * Convert a <code>ResultSet</code> row into an <code>Object[]</code>.
85       * This implementation copies column values into the array in the same 
86       * order they're returned from the <code>ResultSet</code>.  Array elements
87       * will be set to <code>null</code> if the column was SQL NULL.
88       *
89       * @see org.apache.commons.dbutils.RowProcessor#toArray(java.sql.ResultSet)
90       * @param rs ResultSet that supplies the array data
91       * @throws SQLException if a database access error occurs
92       * @return the newly created array
93       */
94      public Object[] toArray(ResultSet rs) throws SQLException {
95          ResultSetMetaData meta = rs.getMetaData();
96          int cols = meta.getColumnCount();
97          Object[] result = new Object[cols];
98  
99          for (int i = 0; i < cols; i++) {
100             result[i] = rs.getObject(i + 1);
101         }
102 
103         return result;
104     }
105 
106     /**
107      * Convert a <code>ResultSet</code> row into a JavaBean.  This 
108      * implementation delegates to a BeanProcessor instance.
109      * @see org.apache.commons.dbutils.RowProcessor#toBean(java.sql.ResultSet, java.lang.Class)
110      * @see org.apache.commons.dbutils.BeanProcessor#toBean(java.sql.ResultSet, java.lang.Class)
111      * @param <T> The type of bean to create
112      * @param rs ResultSet that supplies the bean data
113      * @param type Class from which to create the bean instance
114      * @throws SQLException if a database access error occurs
115      * @return the newly created bean 
116      */
117     public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {
118         return this.convert.toBean(rs, type);
119     }
120 
121     /**
122      * Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.  
123      * This implementation delegates to a BeanProcessor instance. 
124      * @see org.apache.commons.dbutils.RowProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
125      * @see org.apache.commons.dbutils.BeanProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
126      * @param <T> The type of bean to create
127      * @param rs ResultSet that supplies the bean data
128      * @param type Class from which to create the bean instance
129      * @throws SQLException if a database access error occurs
130      * @return A <code>List</code> of beans with the given type in the order 
131      * they were returned by the <code>ResultSet</code>.
132      */
133     public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
134         return this.convert.toBeanList(rs, type);
135     }
136 
137     /**
138      * Convert a <code>ResultSet</code> row into a <code>Map</code>.  This 
139      * implementation returns a <code>Map</code> with case insensitive column
140      * names as keys.  Calls to <code>map.get("COL")</code> and 
141      * <code>map.get("col")</code> return the same value.
142      * @see org.apache.commons.dbutils.RowProcessor#toMap(java.sql.ResultSet)
143      * @param rs ResultSet that supplies the map data
144      * @throws SQLException if a database access error occurs
145      * @return the newly created Map
146      */
147     public Map<String, Object> toMap(ResultSet rs) throws SQLException {
148         Map<String, Object> result = new CaseInsensitiveHashMap();
149         ResultSetMetaData rsmd = rs.getMetaData();
150         int cols = rsmd.getColumnCount();
151 
152         for (int i = 1; i <= cols; i++) {
153             result.put(rsmd.getColumnName(i), rs.getObject(i));
154         }
155 
156         return result;
157     }
158     
159     /**
160      * A Map that converts all keys to lowercase Strings for case insensitive
161      * lookups.  This is needed for the toMap() implementation because 
162      * databases don't consistenly handle the casing of column names. 
163      * 
164      * <p>The keys are stored as they are given [BUG #DBUTILS-34], so we maintain
165      * an internal mapping from lowercase keys to the real keys in order to 
166      * achieve the case insensitive lookup.
167      * 
168      * <p>Note: This implementation does not allow <tt>null</tt>
169      * for key, whereas {@link HashMap} does, because of the code:
170      * <pre>
171      * key.toString().toLowerCase()
172      * </pre>
173      */
174     private static class CaseInsensitiveHashMap extends HashMap<String, Object> {
175         /**
176          * The internal mapping from lowercase keys to the real keys.
177          * 
178          * <p>
179          * Any query operation using the key 
180          * ({@link #get(Object)}, {@link #containsKey(Object)})
181          * is done in three steps:
182          * <ul>
183          * <li>convert the parameter key to lower case</li>
184          * <li>get the actual key that corresponds to the lower case key</li>
185          * <li>query the map with the actual key</li>
186          * </ul>
187          * </p>
188          */
189         private final Map<String,String> lowerCaseMap = new HashMap<String,String>();
190 
191         /**
192          * Required for serialization support.
193          * 
194          * @see java.io.Serializable
195          */ 
196         private static final long serialVersionUID = -2848100435296897392L;
197 
198         /** {@inheritDoc} */
199         @Override
200         public boolean containsKey(Object key) {
201             Object realKey = lowerCaseMap.get(key.toString().toLowerCase());
202             return super.containsKey(realKey);
203             // Possible optimisation here:
204             // Since the lowerCaseMap contains a mapping for all the keys,
205             // we could just do this:
206             // return lowerCaseMap.containsKey(key.toString().toLowerCase());
207         }
208 
209         /** {@inheritDoc} */
210         @Override
211         public Object get(Object key) {
212             Object realKey = lowerCaseMap.get(key.toString().toLowerCase());
213             return super.get(realKey);
214         }
215 
216         /** {@inheritDoc} */
217         @Override
218         public Object put(String key, Object value) {
219             /*
220              * In order to keep the map and lowerCaseMap synchronized,
221              * we have to remove the old mapping before putting the 
222              * new one. Indeed, oldKey and key are not necessaliry equals.
223              * (That's why we call super.remove(oldKey) and not just
224              * super.put(key, value))
225              */
226             Object oldKey = lowerCaseMap.put(key.toLowerCase(), key);
227             Object oldValue = super.remove(oldKey);
228             super.put(key, value);
229             return oldValue;
230         }
231 
232         /** {@inheritDoc} */
233         @Override
234         public void putAll(Map<? extends String,?> m) {
235             for (Map.Entry<? extends String, ?> entry : m.entrySet()) {
236                 String key = entry.getKey();
237                 Object value = entry.getValue();
238                 this.put(key, value);
239             }
240         }
241 
242         /** {@inheritDoc} */
243         @Override
244         public Object remove(Object key) {
245             Object realKey = lowerCaseMap.remove(key.toString().toLowerCase());
246             return super.remove(realKey);
247         }
248     }
249     
250 }