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 018package org.apache.commons.beanutils; 019 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.util.Iterator; 023import java.util.Objects; 024 025/** 026 * <p>Implements <code>DynaClass</code> for DynaBeans that wrap the 027 * <code>java.sql.Row</code> objects of a <code>java.sql.ResultSet</code>. 028 * The normal usage pattern is something like:</p> 029 * <pre> 030 * ResultSet rs = ...; 031 * ResultSetDynaClass rsdc = new ResultSetDynaClass(rs); 032 * Iterator rows = rsdc.iterator(); 033 * while (rows.hasNext()) { 034 * DynaBean row = (DynaBean) rows.next(); 035 * ... process this row ... 036 * } 037 * rs.close(); 038 * </pre> 039 * 040 * <p>Each column in the result set will be represented as a DynaBean 041 * property of the corresponding name (optionally forced to lower case 042 * for portability).</p> 043 * 044 * <p><strong>WARNING</strong> - Any {@link DynaBean} instance returned by 045 * this class, or from the <code>Iterator</code> returned by the 046 * <code>iterator()</code> method, is directly linked to the row that the 047 * underlying result set is currently positioned at. This has the following 048 * implications:</p> 049 * <ul> 050 * <li>Once you retrieve a different {@link DynaBean} instance, you should 051 * no longer use any previous instance.</li> 052 * <li>Changing the position of the underlying result set will change the 053 * data that the {@link DynaBean} references.</li> 054 * <li>Once the underlying result set is closed, the {@link DynaBean} 055 * instance may no longer be used.</li> 056 * </ul> 057 * 058 * <p>Any database data that you wish to utilize outside the context of the 059 * current row of an open result set must be copied. For example, you could 060 * use the following code to create standalone copies of the information in 061 * a result set:</p> 062 * <pre> 063 * ArrayList results = new ArrayList(); // To hold copied list 064 * ResultSetDynaClass rsdc = ...; 065 * DynaProperty[] properties = rsdc.getDynaProperties(); 066 * BasicDynaClass bdc = 067 * new BasicDynaClass("foo", BasicDynaBean.class, 068 * rsdc.getDynaProperties()); 069 * Iterator rows = rsdc.iterator(); 070 * while (rows.hasNext()) { 071 * DynaBean oldRow = (DynaBean) rows.next(); 072 * DynaBean newRow = bdc.newInstance(); 073 * PropertyUtils.copyProperties(newRow, oldRow); 074 * results.add(newRow); 075 * } 076 * </pre> 077 * 078 */ 079 080public class ResultSetDynaClass extends JDBCDynaClass { 081 082 private static final long serialVersionUID = 1L; 083 084 /** 085 * <p>The <code>ResultSet</code> we are wrapping.</p> 086 */ 087 protected ResultSet resultSet; 088 089 /** 090 * <p>Construct a new ResultSetDynaClass for the specified 091 * <code>ResultSet</code>. The property names corresponding 092 * to column names in the result set will be lower cased.</p> 093 * 094 * @param resultSet The result set to be wrapped 095 * @throws NullPointerException if <code>resultSet</code> 096 * is <code>null</code> 097 * @throws SQLException if the metadata for this result set 098 * cannot be introspected 099 */ 100 public ResultSetDynaClass(final ResultSet resultSet) throws SQLException { 101 102 this(resultSet, true); 103 104 } 105 106 /** 107 * <p>Construct a new ResultSetDynaClass for the specified 108 * <code>ResultSet</code>. The property names corresponding 109 * to the column names in the result set will be lower cased or not, 110 * depending on the specified <code>lowerCase</code> value.</p> 111 * 112 * <p><strong>WARNING</strong> - If you specify <code>false</code> 113 * for <code>lowerCase</code>, the returned property names will 114 * exactly match the column names returned by your JDBC driver. 115 * Because different drivers might return column names in different 116 * cases, the property names seen by your application will vary 117 * depending on which JDBC driver you are using.</p> 118 * 119 * @param resultSet The result set to be wrapped 120 * @param lowerCase Should property names be lower cased? 121 * @throws NullPointerException if <code>resultSet</code> 122 * is <code>null</code> 123 * @throws SQLException if the metadata for this result set 124 * cannot be introspected 125 */ 126 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase) 127 throws SQLException { 128 129 this(resultSet, lowerCase, false); 130 131 } 132 133 /** 134 * <p>Construct a new ResultSetDynaClass for the specified 135 * <code>ResultSet</code>. The property names corresponding 136 * to the column names in the result set will be lower cased or not, 137 * depending on the specified <code>lowerCase</code> value.</p> 138 * 139 * <p><strong>WARNING</strong> - If you specify <code>false</code> 140 * for <code>lowerCase</code>, the returned property names will 141 * exactly match the column names returned by your JDBC driver. 142 * Because different drivers might return column names in different 143 * cases, the property names seen by your application will vary 144 * depending on which JDBC driver you are using.</p> 145 * 146 * @param resultSet The result set to be wrapped 147 * @param lowerCase Should property names be lower cased? 148 * @param useColumnLabel true if the column label should be used, otherwise false 149 * @throws NullPointerException if <code>resultSet</code> 150 * is <code>null</code> 151 * @throws SQLException if the metadata for this result set 152 * cannot be introspected 153 * @since 1.8.3 154 */ 155 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) 156 throws SQLException { 157 this.resultSet = Objects.requireNonNull(resultSet, "resultSet"); 158 this.lowerCase = lowerCase; 159 setUseColumnLabel(useColumnLabel); 160 introspect(resultSet); 161 162 } 163 164 /** 165 * Get a value from the {@link ResultSet} for the specified 166 * property name. 167 * 168 * @param name The property name 169 * @return The value 170 * @throws SQLException if an error occurs 171 * @since 1.8.0 172 */ 173 public Object getObjectFromResultSet(final String name) throws SQLException { 174 return getObject(getResultSet(), name); 175 } 176 177 /** 178 * <p>Return the result set we are wrapping.</p> 179 */ 180 ResultSet getResultSet() { 181 182 return this.resultSet; 183 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 * <p>Loads the class of the given name which by default uses the class loader used 201 * to load this library. 202 * Dervations of this class could implement alternative class loading policies such as 203 * using custom ClassLoader or using the Threads's context class loader etc. 204 * </p> 205 * @param className The name of the class to load 206 * @return The loaded class 207 * @throws SQLException if the class cannot be loaded 208 */ 209 @Override 210 protected Class<?> loadClass(final String className) throws SQLException { 211 212 try { 213 return getClass().getClassLoader().loadClass(className); 214 } 215 catch (final Exception e) { 216 throw new SQLException("Cannot load column class '" + 217 className + "': " + e); 218 } 219 } 220}