* 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);
}
}
}