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  
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 }