* Wraps a ResultSet in an Iterator. This is useful
* when you want to present a non-database application layer with domain
* neutral data.
*
* This implementation requires the ResultSet.isLast() method
* to be implemented.
*
ResultSet.
*/
private final ResultSet rs;
/**
* The processor to use when converting a row into an Object[].
*/
private final RowProcessor convert;
/**
* Constructor for ResultSetIterator.
* @param rs Wrap this ResultSet in an Iterator.
*/
public ResultSetIterator(ResultSet rs) {
this(rs , new BasicRowProcessor());
}
/**
* Constructor for ResultSetIterator.
* @param rs Wrap this ResultSet in an Iterator.
* @param convert The processor to use when converting a row into an
* Object[]. Defaults to a
* BasicRowProcessor.
*/
public ResultSetIterator(ResultSet rs, RowProcessor convert) {
this.rs = rs;
this.convert = convert;
}
/**
* Returns true if there are more rows in the ResultSet.
* @return boolean true if there are more rows
* @throws RuntimeException if an SQLException occurs.
*/
public boolean hasNext() {
try {
return !rs.isLast();
} catch (SQLException e) {
rethrow(e);
return false;
}
}
/**
* Returns the next row as an Object[].
* @return An Object[] with the same number of elements as
* columns in the ResultSet.
* @see java.util.Iterator#next()
* @throws RuntimeException if an SQLException occurs.
*/
public Object next() {
try {
rs.next();
return this.convert.toArray(rs);
} catch (SQLException e) {
rethrow(e);
return null;
}
}
/**
* Deletes the current row from the ResultSet.
* @see java.util.Iterator#remove()
* @throws RuntimeException if an SQLException occurs.
*/
public void remove() {
try {
this.rs.deleteRow();
} catch (SQLException e) {
rethrow(e);
}
}
/**
* Rethrow the SQLException as a RuntimeException. This implementation
* creates a new RuntimeException with the SQLException's error message.
* @param e SQLException to rethrow
* @since DbUtils 1.1
*/
protected void rethrow(SQLException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* RowProcessor implementations convert
* ResultSet rows into various other objects. Implementations
* can extend BasicRowProcessor to protect themselves
* from changes to this interface.
*
* @see BasicRowProcessor
*/
interface RowProcessor {
/**
* Create an Object[] from the column values in one
* ResultSet row. The ResultSet should be
* positioned on a valid row before passing it to this method.
* Implementations of this method must not alter the row position of
* the ResultSet.
*
* @param rs ResultSet that supplies the array data
* @throws SQLException if a database access error occurs
* @return the newly created array
*/
public Object[] toArray(ResultSet rs) throws SQLException;
/**
* Create a JavaBean from the column values in one ResultSet
* row. The ResultSet should be positioned on a valid row before
* passing it to this method. Implementations of this method must not
* alter the row position of the ResultSet.
*
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return the newly created bean
*/
public Object toBean(ResultSet rs, Class type) throws SQLException;
/**
* Create a List of JavaBeans from the column values in all
* ResultSet rows. ResultSet.next() should
* not be called before passing it to this method.
*
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return A List of beans with the given type in the order
* they were returned by the ResultSet.
*/
public List toBeanList(ResultSet rs, Class type) throws SQLException;
/**
* Create a Map from the column values in one
* ResultSet row. The ResultSet should be
* positioned on a valid row before
* passing it to this method. Implementations of this method must not
* alter the row position of the ResultSet.
*
* @param rs ResultSet that supplies the map data
* @throws SQLException if a database access error occurs
* @return the newly created Map
*/
public Map toMap(ResultSet rs) throws SQLException;
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Basic implementation of the RowProcessor interface.
*
* * This class is thread-safe. *
* * @see RowProcessor */ class BasicRowProcessor implements RowProcessor { /** * The default BeanProcessor instance to use if not supplied in the * constructor. */ private static final BeanProcessor defaultConvert = new BeanProcessor(); /** * The Singleton instance of this class. */ private static final BasicRowProcessor instance = new BasicRowProcessor(); /** * Returns the Singleton instance of this class. * * @return The single instance of this class. * @deprecated Create instances with the constructors instead. This will * be removed after DbUtils 1.1. */ public static BasicRowProcessor instance() { return instance; } /** * Use this to process beans. */ private final BeanProcessor convert; /** * BasicRowProcessor constructor. Bean processing defaults to a * BeanProcessor instance. */ public BasicRowProcessor() { this(defaultConvert); } /** * BasicRowProcessor constructor. * @param convert The BeanProcessor to use when converting columns to * bean properties. * @since DbUtils 1.1 */ public BasicRowProcessor(BeanProcessor convert) { super(); this.convert = convert; } /** * Convert aResultSet row into an Object[].
* This implementation copies column values into the array in the same
* order they're returned from the ResultSet. Array elements
* will be set to null if the column was SQL NULL.
*
* @see org.apache.commons.dbutils.RowProcessor#toArray(java.sql.ResultSet)
*/
public Object[] toArray(ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
int cols = meta.getColumnCount();
Object[] result = new Object[cols];
for (int i = 0; i < cols; i++) {
result[i] = rs.getObject(i + 1);
}
return result;
}
/**
* Convert a ResultSet row into a JavaBean. This
* implementation delegates to a BeanProcessor instance.
* @see org.apache.commons.dbutils.RowProcessor#toBean(java.sql.ResultSet, java.lang.Class)
* @see org.apache.commons.dbutils.BeanProcessor#toBean(java.sql.ResultSet, java.lang.Class)
*/
public Object toBean(ResultSet rs, Class type) throws SQLException {
return this.convert.toBean(rs, type);
}
/**
* Convert a ResultSet into a List of JavaBeans.
* This implementation delegates to a BeanProcessor instance.
* @see org.apache.commons.dbutils.RowProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
* @see org.apache.commons.dbutils.BeanProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
*/
public List toBeanList(ResultSet rs, Class type) throws SQLException {
return this.convert.toBeanList(rs, type);
}
/**
* Convert a ResultSet row into a Map. This
* implementation returns a Map with case insensitive column
* names as keys. Calls to map.get("COL") and
* map.get("col") return the same value.
* @see org.apache.commons.dbutils.RowProcessor#toMap(java.sql.ResultSet)
*/
public Map toMap(ResultSet rs) throws SQLException {
Map result = new CaseInsensitiveHashMap();
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
for (int i = 1; i <= cols; i++) {
result.put(rsmd.getColumnName(i), rs.getObject(i));
}
return result;
}
/**
* A Map that converts all keys to lowercase Strings for case insensitive
* lookups. This is needed for the toMap() implementation because
* databases don't consistenly handle the casing of column names.
*
* The keys are stored as they are given [BUG #DBUTILS-34], so we maintain * an internal mapping from lowercase keys to the real keys in order to * achieve the case insensitive lookup. * *
Note: This implementation does not allow null * for key, whereas {@link HashMap} does, because of the code: *
* key.toString().toLowerCase() **/ private static class CaseInsensitiveHashMap extends HashMap { /** * The internal mapping from lowercase keys to the real keys. * *
* Any query operation using the key * ({@link #get(Object)}, {@link #containsKey(Object)}) * is done in three steps: *
* BeanProcessor matches column names to bean property names
* and converts ResultSet columns into objects for those bean
* properties. Subclasses should override the methods in the processing chain
* to customize behavior.
*
* This class is thread-safe. *
* * @see BasicRowProcessor * * @since DbUtils 1.1 */ class BeanProcessor { /** * Special array value used bymapColumnsToProperties that
* indicates there is no bean property that matches a column from a
* ResultSet.
*/
protected static final int PROPERTY_NOT_FOUND = -1;
/**
* Set a bean's primitive properties to these defaults when SQL NULL
* is returned. These are the same as the defaults that ResultSet get*
* methods return in the event of a NULL column.
*/
private static final Map primitiveDefaults = new HashMap();
static {
primitiveDefaults.put(Integer.TYPE, new Integer(0));
primitiveDefaults.put(Short.TYPE, new Short((short) 0));
primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
primitiveDefaults.put(Float.TYPE, new Float(0));
primitiveDefaults.put(Double.TYPE, new Double(0));
primitiveDefaults.put(Long.TYPE, new Long(0));
primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
primitiveDefaults.put(Character.TYPE, new Character('\u0000'));
}
/**
* Constructor for BeanProcessor.
*/
public BeanProcessor() {
super();
}
/**
* Convert a ResultSet row into a JavaBean. This
* implementation uses reflection and BeanInfo classes to
* match column names to bean property names. Properties are matched to
* columns based on several factors:
*
* Primitive bean properties are set to their defaults when SQL NULL is
* returned from the ResultSet. Numeric fields are set to 0
* and booleans are set to false. Object bean properties are set to
* null when SQL NULL is returned. This is the same behavior
* as the ResultSet get* methods.
*
ResultSet into a List of JavaBeans.
* This implementation uses reflection and BeanInfo classes to
* match column names to bean property names. Properties are matched to
* columns based on several factors:
*
* Primitive bean properties are set to their defaults when SQL NULL is
* returned from the ResultSet. Numeric fields are set to 0
* and booleans are set to false. Object bean properties are set to
* null when SQL NULL is returned. This is the same behavior
* as the ResultSet get* methods.
*
PropertyDescriptor[] for the bean property that matches
* the column name. If no bean property was found for a column, the
* position is set to PROPERTY_NOT_FOUND.
*
* @param rsmd The ResultSetMetaData containing column
* information.
*
* @param props The bean property descriptors.
*
* @throws SQLException if a database access error occurs
*
* @return An int[] with column index to property index mappings. The 0th
* element is meaningless because JDBC column indexing starts at 1.
*/
protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
PropertyDescriptor[] props) throws SQLException {
int cols = rsmd.getColumnCount();
int columnToProperty[] = new int[cols + 1];
Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
for (int col = 1; col <= cols; col++) {
String columnName = rsmd.getColumnName(col);
for (int i = 0; i < props.length; i++) {
if (columnName.equalsIgnoreCase(props[i].getName())) {
columnToProperty[col] = i;
break;
}
}
}
return columnToProperty;
}
/**
* Convert a ResultSet column into an object. Simple
* implementations could just call rs.getObject(index) while
* more complex implementations could perform type manipulation to match
* the column's type to the bean property type.
*
*
* This implementation calls the appropriate ResultSet getter
* method for the given property type to perform the type conversion. If
* the property type doesn't match one of the supported
* ResultSet types, getObject is called.
*
ResultSet currently being processed. It is
* positioned on a valid row before being passed into this method.
*
* @param index The current column index being processed.
*
* @param propType The bean property type that this column needs to be
* converted into.
*
* @throws SQLException if a database access error occurs
*
* @return The object from the ResultSet at the given column
* index after optional type processing or null if the column
* value was SQL NULL.
*/
protected Object processColumn(ResultSet rs, int index, Class propType)
throws SQLException {
if ( !propType.isPrimitive() && rs.getObject(index) == null ) {
return null;
}
if (propType.equals(String.class)) {
return rs.getString(index);
} else if (
propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {
return new Integer(rs.getInt(index));
} else if (
propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {
return new Boolean(rs.getBoolean(index));
} else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
return new Long(rs.getLong(index));
} else if (
propType.equals(Double.TYPE) || propType.equals(Double.class)) {
return new Double(rs.getDouble(index));
} else if (
propType.equals(Float.TYPE) || propType.equals(Float.class)) {
return new Float(rs.getFloat(index));
} else if (
propType.equals(Short.TYPE) || propType.equals(Short.class)) {
return new Short(rs.getShort(index));
} else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {
return new Byte(rs.getByte(index));
} else if (propType.equals(Timestamp.class)) {
return rs.getTimestamp(index);
} else {
return rs.getObject(index);
}
}
}