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 18 19 package org.apache.commons.beanutils; 20 21 22 import java.sql.ResultSet; 23 import java.sql.SQLException; 24 import java.util.Iterator; 25 26 27 /** 28 * <p>Implementation of <code>DynaClass</code> for DynaBeans that wrap the 29 * <code>java.sql.Row</code> objects of a <code>java.sql.ResultSet</code>. 30 * The normal usage pattern is something like:</p> 31 * <pre> 32 * ResultSet rs = ...; 33 * ResultSetDynaClass rsdc = new ResultSetDynaClass(rs); 34 * Iterator rows = rsdc.iterator(); 35 * while (rows.hasNext()) { 36 * DynaBean row = (DynaBean) rows.next(); 37 * ... process this row ... 38 * } 39 * rs.close(); 40 * </pre> 41 * 42 * <p>Each column in the result set will be represented as a DynaBean 43 * property of the corresponding name (optionally forced to lower case 44 * for portability).</p> 45 * 46 * <p><strong>WARNING</strong> - Any {@link DynaBean} instance returned by 47 * this class, or from the <code>Iterator</code> returned by the 48 * <code>iterator()</code> method, is directly linked to the row that the 49 * underlying result set is currently positioned at. This has the following 50 * implications:</p> 51 * <ul> 52 * <li>Once you retrieve a different {@link DynaBean} instance, you should 53 * no longer use any previous instance.</li> 54 * <li>Changing the position of the underlying result set will change the 55 * data that the {@link DynaBean} references.</li> 56 * <li>Once the underlying result set is closed, the {@link DynaBean} 57 * instance may no longer be used.</li> 58 * </ul> 59 * 60 * <p>Any database data that you wish to utilize outside the context of the 61 * current row of an open result set must be copied. For example, you could 62 * use the following code to create standalone copies of the information in 63 * a result set:</p> 64 * <pre> 65 * ArrayList results = new ArrayList(); // To hold copied list 66 * ResultSetDynaClass rsdc = ...; 67 * DynaProperty[] properties = rsdc.getDynaProperties(); 68 * BasicDynaClass bdc = 69 * new BasicDynaClass("foo", BasicDynaBean.class, 70 * rsdc.getDynaProperties()); 71 * Iterator rows = rsdc.iterator(); 72 * while (rows.hasNext()) { 73 * DynaBean oldRow = (DynaBean) rows.next(); 74 * DynaBean newRow = bdc.newInstance(); 75 * PropertyUtils.copyProperties(newRow, oldRow); 76 * results.add(newRow); 77 * } 78 * </pre> 79 * 80 * @version $Id$ 81 */ 82 83 public class ResultSetDynaClass extends JDBCDynaClass implements DynaClass { 84 85 86 // ----------------------------------------------------------- Constructors 87 88 89 /** 90 * <p>Construct a new ResultSetDynaClass for the specified 91 * <code>ResultSet</code>. The property names corresponding 92 * to column names in the result set will be lower cased.</p> 93 * 94 * @param resultSet The result set to be wrapped 95 * 96 * @throws NullPointerException if <code>resultSet</code> 97 * is <code>null</code> 98 * @throws SQLException if the metadata for this result set 99 * cannot be introspected 100 */ 101 public ResultSetDynaClass(final ResultSet resultSet) throws SQLException { 102 103 this(resultSet, true); 104 105 } 106 107 108 /** 109 * <p>Construct a new ResultSetDynaClass for the specified 110 * <code>ResultSet</code>. The property names corresponding 111 * to the column names in the result set will be lower cased or not, 112 * depending on the specified <code>lowerCase</code> value.</p> 113 * 114 * <p><strong>WARNING</strong> - If you specify <code>false</code> 115 * for <code>lowerCase</code>, the returned property names will 116 * exactly match the column names returned by your JDBC driver. 117 * Because different drivers might return column names in different 118 * cases, the property names seen by your application will vary 119 * depending on which JDBC driver you are using.</p> 120 * 121 * @param resultSet The result set to be wrapped 122 * @param lowerCase Should property names be lower cased? 123 * 124 * @throws NullPointerException if <code>resultSet</code> 125 * is <code>null</code> 126 * @throws SQLException if the metadata for this result set 127 * cannot be introspected 128 */ 129 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase) 130 throws SQLException { 131 132 this(resultSet, lowerCase, false); 133 134 } 135 136 137 /** 138 * <p>Construct a new ResultSetDynaClass for the specified 139 * <code>ResultSet</code>. The property names corresponding 140 * to the column names in the result set will be lower cased or not, 141 * depending on the specified <code>lowerCase</code> value.</p> 142 * 143 * <p><strong>WARNING</strong> - If you specify <code>false</code> 144 * for <code>lowerCase</code>, the returned property names will 145 * exactly match the column names returned by your JDBC driver. 146 * Because different drivers might return column names in different 147 * cases, the property names seen by your application will vary 148 * depending on which JDBC driver you are using.</p> 149 * 150 * @param resultSet The result set to be wrapped 151 * @param lowerCase Should property names be lower cased? 152 * @param useColumnLabel true if the column label should be used, otherwise false 153 * 154 * @throws NullPointerException if <code>resultSet</code> 155 * is <code>null</code> 156 * @throws SQLException if the metadata for this result set 157 * cannot be introspected 158 * @since 1.8.3 159 */ 160 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) 161 throws SQLException { 162 163 if (resultSet == null) { 164 throw new NullPointerException(); 165 } 166 this.resultSet = resultSet; 167 this.lowerCase = lowerCase; 168 setUseColumnLabel(useColumnLabel); 169 introspect(resultSet); 170 171 } 172 173 174 // ----------------------------------------------------- Instance Variables 175 176 177 /** 178 * <p>The <code>ResultSet</code> we are wrapping.</p> 179 */ 180 protected ResultSet resultSet = null; 181 182 183 // --------------------------------------------------------- Public Methods 184 185 186 /** 187 * <p>Return an <code>Iterator</code> of {@link DynaBean} instances for 188 * each row of the wrapped <code>ResultSet</code>, in "forward" order. 189 * Unless the underlying result set supports scrolling, this method 190 * should be called only once.</p> 191 * @return An <code>Iterator</code> of {@link DynaBean} instances 192 */ 193 public Iterator<DynaBean> iterator() { 194 195 return (new ResultSetIterator(this)); 196 197 } 198 199 200 /** 201 * Get a value from the {@link ResultSet} for the specified 202 * property name. 203 * 204 * @param name The property name 205 * @return The value 206 * @throws SQLException if an error occurs 207 * @since 1.8.0 208 */ 209 public Object getObjectFromResultSet(final String name) throws SQLException { 210 return getObject(getResultSet(), name); 211 } 212 213 // -------------------------------------------------------- Package Methods 214 215 216 /** 217 * <p>Return the result set we are wrapping.</p> 218 */ 219 ResultSet getResultSet() { 220 221 return (this.resultSet); 222 223 } 224 225 226 // ------------------------------------------------------ Protected Methods 227 228 /** 229 * <p>Loads the class of the given name which by default uses the class loader used 230 * to load this library. 231 * Dervations of this class could implement alternative class loading policies such as 232 * using custom ClassLoader or using the Threads's context class loader etc. 233 * </p> 234 * @param className The name of the class to load 235 * @return The loaded class 236 * @throws SQLException if the class cannot be loaded 237 */ 238 @Override 239 protected Class<?> loadClass(final String className) throws SQLException { 240 241 try { 242 return getClass().getClassLoader().loadClass(className); 243 } 244 catch (final Exception e) { 245 throw new SQLException("Cannot load column class '" + 246 className + "': " + e); 247 } 248 } 249 }