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 package org.apache.commons.beanutils2.sql; 19 20 import java.sql.ResultSet; 21 import java.sql.SQLException; 22 import java.util.Iterator; 23 import java.util.Objects; 24 25 import org.apache.commons.beanutils2.DynaBean; 26 import org.apache.commons.beanutils2.DynaClass; 27 28 /** 29 * <p> 30 * Implements {@link DynaClass} for DynaBeans that wrap the {@code java.sql.Row</code> objects of a <code>java.sql.ResultSet}. The normal usage pattern is 31 * something like: 32 * </p> 33 * 34 * <pre> 35 * ResultSet rs = ...; 36 * ResultSetDynaClass rsdc = new ResultSetDynaClass(rs); 37 * Iterator rows = rsdc.iterator(); 38 * while (rows.hasNext()) { 39 * DynaBean row = (DynaBean) rows.next(); 40 * ... process this row ... 41 * } 42 * rs.close(); 43 * </pre> 44 * 45 * <p> 46 * Each column in the result set will be represented as a DynaBean property of the corresponding name (optionally forced to lower case for portability). 47 * </p> 48 * 49 * <p> 50 * <strong>WARNING</strong> - Any {@link DynaBean} instance returned by this class, or from the {@code Iterator} returned by the {@code iterator()} method, is 51 * directly linked to the row that the underlying result set is currently positioned at. This has the following implications: 52 * </p> 53 * <ul> 54 * <li>Once you retrieve a different {@link DynaBean} instance, you should no longer use any previous instance.</li> 55 * <li>Changing the position of the underlying result set will change the data that the {@link DynaBean} references.</li> 56 * <li>Once the underlying result set is closed, the {@link DynaBean} instance may no longer be used.</li> 57 * </ul> 58 * 59 * <p> 60 * Any database data that you wish to utilize outside the context of the current row of an open result set must be copied. For example, you could use the 61 * following code to create standalone copies of the information in a result set: 62 * </p> 63 * 64 * <pre> 65 * List 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 public class ResultSetDynaClass extends AbstractJdbcDynaClass { 81 82 private static final long serialVersionUID = 1L; 83 84 /** 85 * <p> 86 * The {@code ResultSet} we are wrapping. 87 * </p> 88 */ 89 protected ResultSet resultSet; 90 91 /** 92 * <p> 93 * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to column names in the result set will be lower 94 * cased. 95 * </p> 96 * 97 * @param resultSet The result set to be wrapped 98 * @throws NullPointerException if {@code resultSet} is {@code null} 99 * @throws SQLException if the metadata for this result set cannot be introspected 100 */ 101 public ResultSetDynaClass(final ResultSet resultSet) throws SQLException { 102 this(resultSet, true); 103 } 104 105 /** 106 * <p> 107 * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to the column names in the result set will be 108 * lower cased or not, depending on the specified {@code lowerCase} value. 109 * </p> 110 * 111 * <p> 112 * <strong>WARNING</strong> - If you specify {@code false} for {@code lowerCase}, the returned property names will exactly match the column names returned 113 * by your JDBC driver. Because different drivers might return column names in different cases, the property names seen by your application will vary 114 * depending on which JDBC driver you are using. 115 * </p> 116 * 117 * @param resultSet The result set to be wrapped 118 * @param lowerCase Should property names be lower cased? 119 * @throws NullPointerException if {@code resultSet} is {@code null} 120 * @throws SQLException if the metadata for this result set cannot be introspected 121 */ 122 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase) throws SQLException { 123 this(resultSet, lowerCase, false); 124 } 125 126 /** 127 * <p> 128 * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to the column names in the result set will be 129 * lower cased or not, depending on the specified {@code lowerCase} value. 130 * </p> 131 * 132 * <p> 133 * <strong>WARNING</strong> - If you specify {@code false} for {@code lowerCase}, the returned property names will exactly match the column names returned 134 * by your JDBC driver. Because different drivers might return column names in different cases, the property names seen by your application will vary 135 * depending on which JDBC driver you are using. 136 * </p> 137 * 138 * @param resultSet The result set to be wrapped 139 * @param lowerCase Should property names be lower cased? 140 * @param useColumnLabel true if the column label should be used, otherwise false 141 * @throws NullPointerException if {@code resultSet} is {@code null} 142 * @throws SQLException if the metadata for this result set cannot be introspected 143 * @since 1.8.3 144 */ 145 public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) throws SQLException { 146 this.resultSet = Objects.requireNonNull(resultSet, "resultSet"); 147 this.lowerCase = lowerCase; 148 setUseColumnLabel(useColumnLabel); 149 introspect(resultSet); 150 } 151 152 /** 153 * Gets a value from the {@link ResultSet} for the specified property name. 154 * 155 * @param name The property name 156 * @return The value 157 * @throws SQLException if an error occurs 158 * @since 1.8.0 159 */ 160 @SuppressWarnings("resource") // getResultSet() does not allocate. 161 public Object getObjectFromResultSet(final String name) throws SQLException { 162 return getObject(getResultSet(), name); 163 } 164 165 /** 166 * <p> 167 * Gets the result set we are wrapping. 168 * </p> 169 */ 170 ResultSet getResultSet() { 171 return this.resultSet; 172 } 173 174 /** 175 * <p> 176 * Return an {@code Iterator} of {@link DynaBean} instances for each row of the wrapped {@code ResultSet}, in "forward" order. Unless the underlying result 177 * set supports scrolling, this method should be called only once. 178 * </p> 179 * 180 * @return An {@code Iterator} of {@link DynaBean} instances 181 */ 182 public Iterator<DynaBean> iterator() { 183 return new ResultSetIterator(this); 184 } 185 186 /** 187 * <p> 188 * Loads the class of the given name which by default uses the class loader used to load this library. Derivations of this class could implement alternative 189 * class loading policies such as using custom ClassLoader or using the Threads's context class loader etc. 190 * </p> 191 * 192 * @param className The name of the class to load 193 * @return The loaded class 194 * @throws SQLException if the class cannot be loaded 195 */ 196 @Override 197 protected Class<?> loadClass(final String className) throws SQLException { 198 try { 199 return getClass().getClassLoader().loadClass(className); 200 } catch (final Exception e) { 201 throw new SQLException("Cannot load column class '" + className + "': " + e); 202 } 203 } 204 }