ResultSetDynaClass.java

  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.beanutils2.sql;

  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.util.Iterator;
  21. import java.util.Objects;

  22. import org.apache.commons.beanutils2.DynaBean;
  23. import org.apache.commons.beanutils2.DynaClass;

  24. /**
  25.  * <p>
  26.  * 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
  27.  * something like:
  28.  * </p>
  29.  *
  30.  * <pre>
  31.  *   ResultSet rs = ...;
  32.  *   ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
  33.  *   Iterator rows = rsdc.iterator();
  34.  *   while (rows.hasNext())  {
  35.  *     DynaBean row = (DynaBean) rows.next();
  36.  *     ... process this row ...
  37.  *   }
  38.  *   rs.close();
  39.  * </pre>
  40.  *
  41.  * <p>
  42.  * Each column in the result set will be represented as a DynaBean property of the corresponding name (optionally forced to lower case for portability).
  43.  * </p>
  44.  *
  45.  * <p>
  46.  * <strong>WARNING</strong> - Any {@link DynaBean} instance returned by this class, or from the {@code Iterator} returned by the {@code iterator()} method, is
  47.  * directly linked to the row that the underlying result set is currently positioned at. This has the following implications:
  48.  * </p>
  49.  * <ul>
  50.  * <li>Once you retrieve a different {@link DynaBean} instance, you should no longer use any previous instance.</li>
  51.  * <li>Changing the position of the underlying result set will change the data that the {@link DynaBean} references.</li>
  52.  * <li>Once the underlying result set is closed, the {@link DynaBean} instance may no longer be used.</li>
  53.  * </ul>
  54.  *
  55.  * <p>
  56.  * 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
  57.  * following code to create standalone copies of the information in a result set:
  58.  * </p>
  59.  *
  60.  * <pre>
  61.  *   List results = new ArrayList(); // To hold copied list
  62.  *   ResultSetDynaClass rsdc = ...;
  63.  *   DynaProperty[] properties = rsdc.getDynaProperties();
  64.  *   BasicDynaClass bdc =
  65.  *     new BasicDynaClass("foo", BasicDynaBean.class,
  66.  *                        rsdc.getDynaProperties());
  67.  *   Iterator rows = rsdc.iterator();
  68.  *   while (rows.hasNext()) {
  69.  *     DynaBean oldRow = (DynaBean) rows.next();
  70.  *     DynaBean newRow = bdc.newInstance();
  71.  *     PropertyUtils.copyProperties(newRow, oldRow);
  72.  *     results.add(newRow);
  73.  *   }
  74.  * </pre>
  75.  */
  76. public class ResultSetDynaClass extends AbstractJdbcDynaClass {

  77.     private static final long serialVersionUID = 1L;

  78.     /**
  79.      * <p>
  80.      * The {@code ResultSet} we are wrapping.
  81.      * </p>
  82.      */
  83.     protected ResultSet resultSet;

  84.     /**
  85.      * <p>
  86.      * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to column names in the result set will be lower
  87.      * cased.
  88.      * </p>
  89.      *
  90.      * @param resultSet The result set to be wrapped
  91.      * @throws NullPointerException if {@code resultSet} is {@code null}
  92.      * @throws SQLException         if the metadata for this result set cannot be introspected
  93.      */
  94.     public ResultSetDynaClass(final ResultSet resultSet) throws SQLException {
  95.         this(resultSet, true);
  96.     }

  97.     /**
  98.      * <p>
  99.      * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to the column names in the result set will be
  100.      * lower cased or not, depending on the specified {@code lowerCase} value.
  101.      * </p>
  102.      *
  103.      * <p>
  104.      * <strong>WARNING</strong> - If you specify {@code false} for {@code lowerCase}, the returned property names will exactly match the column names returned
  105.      * by your JDBC driver. Because different drivers might return column names in different cases, the property names seen by your application will vary
  106.      * depending on which JDBC driver you are using.
  107.      * </p>
  108.      *
  109.      * @param resultSet The result set to be wrapped
  110.      * @param lowerCase Should property names be lower cased?
  111.      * @throws NullPointerException if {@code resultSet} is {@code null}
  112.      * @throws SQLException         if the metadata for this result set cannot be introspected
  113.      */
  114.     public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase) throws SQLException {
  115.         this(resultSet, lowerCase, false);
  116.     }

  117.     /**
  118.      * <p>
  119.      * Constructs a new ResultSetDynaClass for the specified {@code ResultSet}. The property names corresponding to the column names in the result set will be
  120.      * lower cased or not, depending on the specified {@code lowerCase} value.
  121.      * </p>
  122.      *
  123.      * <p>
  124.      * <strong>WARNING</strong> - If you specify {@code false} for {@code lowerCase}, the returned property names will exactly match the column names returned
  125.      * by your JDBC driver. Because different drivers might return column names in different cases, the property names seen by your application will vary
  126.      * depending on which JDBC driver you are using.
  127.      * </p>
  128.      *
  129.      * @param resultSet      The result set to be wrapped
  130.      * @param lowerCase      Should property names be lower cased?
  131.      * @param useColumnLabel true if the column label should be used, otherwise false
  132.      * @throws NullPointerException if {@code resultSet} is {@code null}
  133.      * @throws SQLException         if the metadata for this result set cannot be introspected
  134.      * @since 1.8.3
  135.      */
  136.     public ResultSetDynaClass(final ResultSet resultSet, final boolean lowerCase, final boolean useColumnLabel) throws SQLException {
  137.         this.resultSet = Objects.requireNonNull(resultSet, "resultSet");
  138.         this.lowerCase = lowerCase;
  139.         setUseColumnLabel(useColumnLabel);
  140.         introspect(resultSet);
  141.     }

  142.     /**
  143.      * Gets a value from the {@link ResultSet} for the specified property name.
  144.      *
  145.      * @param name The property name
  146.      * @return The value
  147.      * @throws SQLException if an error occurs
  148.      * @since 1.8.0
  149.      */
  150.     @SuppressWarnings("resource") // getResultSet() does not allocate.
  151.     public Object getObjectFromResultSet(final String name) throws SQLException {
  152.         return getObject(getResultSet(), name);
  153.     }

  154.     /**
  155.      * <p>
  156.      * Gets the result set we are wrapping.
  157.      * </p>
  158.      */
  159.     ResultSet getResultSet() {
  160.         return this.resultSet;
  161.     }

  162.     /**
  163.      * <p>
  164.      * Return an {@code Iterator} of {@link DynaBean} instances for each row of the wrapped {@code ResultSet}, in "forward" order. Unless the underlying result
  165.      * set supports scrolling, this method should be called only once.
  166.      * </p>
  167.      *
  168.      * @return An {@code Iterator} of {@link DynaBean} instances
  169.      */
  170.     public Iterator<DynaBean> iterator() {
  171.         return new ResultSetIterator(this);
  172.     }

  173.     /**
  174.      * <p>
  175.      * 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
  176.      * class loading policies such as using custom ClassLoader or using the Threads's context class loader etc.
  177.      * </p>
  178.      *
  179.      * @param className The name of the class to load
  180.      * @return The loaded class
  181.      * @throws SQLException if the class cannot be loaded
  182.      */
  183.     @Override
  184.     protected Class<?> loadClass(final String className) throws SQLException {
  185.         try {
  186.             return getClass().getClassLoader().loadClass(className);
  187.         } catch (final Exception e) {
  188.             throw new SQLException("Cannot load column class '" + className + "': " + e);
  189.         }
  190.     }
  191. }