Author: dfabulich Date: Wed Feb 11 10:59:15 2009 New Revision: 743297 URL: http://svn.apache.org/viewvc?rev=743297&view=rev Log: merging changes from bugfixing branch
Added: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java - copied, changed from r743293, commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java Removed: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/GenericListHandler.java Modified: commons/sandbox/dbutils/java5/ (props changed) commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BasicRowProcessor.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BeanProcessor.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/QueryRunner.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ArrayListHandler.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/BeanListHandler.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/MapListHandler.java Propchange: commons/sandbox/dbutils/java5/ ------------------------------------------------------------------------------ svn:mergeinfo = /commons/sandbox/dbutils/bugfixing:741988-743293 Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BasicRowProcessor.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BasicRowProcessor.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BasicRowProcessor.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BasicRowProcessor.java Wed Feb 11 10:59:15 2009 @@ -143,22 +143,52 @@ * 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. + * + * <p>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. + * + * <p>Note: This implementation does not allow <tt>null</tt> + * for key, whereas {...@link HashMap} does, because of the code: + * <pre> + * key.toString().toLowerCase() + * </pre> */ private static class CaseInsensitiveHashMap extends HashMap<String, Object> { + /** + * The internal mapping from lowercase keys to the real keys. + * + * <p> + * Any query operation using the key + * ({...@link #get(Object)}, {...@link #containsKey(Object)}) + * is done in three steps: + * <ul> + * <li>convert the parameter key to lower case</li> + * <li>get the actual key that corresponds to the lower case key</li> + * <li>query the map with the actual key</li> + * </ul> + * </p> + */ + private final Map<String,String> lowerCaseMap = new HashMap<String,String>(); /** * Required for serialization support. * * @see java.io.Serializable */ - private static final long serialVersionUID = 1841673097701957808L; + private static final long serialVersionUID = -2848100435296897392L; /** * @see java.util.Map#containsKey(java.lang.Object) */ @Override public boolean containsKey(Object key) { - return super.containsKey(key.toString().toLowerCase()); + Object realKey = lowerCaseMap.get(key.toString().toLowerCase()); + return super.containsKey(realKey); + // Possible optimisation here: + // Since the lowerCaseMap contains a mapping for all the keys, + // we could just do this: + // return lowerCaseMap.containsKey(key.toString().toLowerCase()); } /** @@ -166,7 +196,8 @@ */ @Override public Object get(Object key) { - return super.get(key.toString().toLowerCase()); + Object realKey = lowerCaseMap.get(key.toString().toLowerCase()); + return super.get(realKey); } /** @@ -174,16 +205,27 @@ */ @Override public Object put(String key, Object value) { - return super.put(key.toLowerCase(), value); + /* + * In order to keep the map and lowerCaseMap synchronized, + * we have to remove the old mapping before putting the + * new one. Indeed, oldKey and key are not necessaliry equals. + * (That's why we call super.remove(oldKey) and not just + * super.put(key, value)) + */ + Object oldKey = lowerCaseMap.put(key.toString().toLowerCase(), key); + Object oldValue = super.remove(oldKey); + super.put(key, value); + return oldValue; } /** * @see java.util.Map#putAll(java.util.Map) */ @Override - public void putAll(Map<? extends String, ?> m) { - for (String key : m.keySet()) { - Object value = m.get(key); + public void putAll(Map<? extends String,?> m) { + for (Map.Entry<? extends String, ?> entry : m.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); this.put(key, value); } } @@ -193,7 +235,8 @@ */ @Override public Object remove(Object key) { - return super.remove(key.toString().toLowerCase()); + Object realKey = lowerCaseMap.remove(key.toString().toLowerCase()); + return super.remove(realKey); } } Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BeanProcessor.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BeanProcessor.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BeanProcessor.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/BeanProcessor.java Wed Feb 11 10:59:15 2009 @@ -432,7 +432,11 @@ */ 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); Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/QueryRunner.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/QueryRunner.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/QueryRunner.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/QueryRunner.java Wed Feb 11 10:59:15 2009 @@ -16,7 +16,11 @@ */ package org.apache.commons.dbutils; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.sql.Connection; +import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -35,6 +39,11 @@ public class QueryRunner { /** + * Is {...@link ParameterMetaData#getParameterType(int)} broken (have we tried it yet)? + */ + private volatile boolean pmdKnownBroken = false; + + /** * The DataSource to retrieve connections from. */ protected DataSource ds = null; @@ -47,7 +56,18 @@ } /** - * Constructor for QueryRunner. Methods that do not take a + * Constructor for QueryRunner, allows workaround for Oracle drivers + * @param pmdKnownBroken Oracle drivers don't support {...@link ParameterMetaData#getParameterType(int) }; + * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it, + * and if it breaks, we'll remember not to use it again. + */ + public QueryRunner(boolean pmdKnownBroken) { + super(); + this.pmdKnownBroken = pmdKnownBroken; + } + + /** + * Constructor for QueryRunner, allows workaround for Oracle drivers. Methods that do not take a * <code>Connection</code> parameter will retrieve connections from this * <code>DataSource</code>. * @@ -59,6 +79,22 @@ } /** + * Constructor for QueryRunner, allows workaround for Oracle drivers. Methods that do not take a + * <code>Connection</code> parameter will retrieve connections from this + * <code>DataSource</code>. + * + * @param ds The <code>DataSource</code> to retrieve connections from. + * @param pmdKnownBroken Oracle drivers don't support {...@link ParameterMetaData#getParameterType(int) }; + * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it, + * and if it breaks, we'll remember not to use it again. + */ + public QueryRunner(DataSource ds, boolean pmdKnownBroken) { + super(); + this.pmdKnownBroken = pmdKnownBroken; + setDataSource(ds); + } + + /** * Execute a batch of SQL INSERT, UPDATE, or DELETE queries. * * @param conn The Connection to use to run the query. The caller is @@ -124,13 +160,18 @@ * value to pass in. * @throws SQLException if a database access error occurs */ - protected void fillStatement(PreparedStatement stmt, Object... params) + public void fillStatement(PreparedStatement stmt, Object... params) throws SQLException { if (params == null) { return; } - + + ParameterMetaData pmd = stmt.getParameterMetaData(); + if (pmd.getParameterCount() < params.length) { + throw new SQLException("Too many parameters: expected " + + pmd.getParameterCount() + ", was given " + params.length); + } for (int i = 0; i < params.length; i++) { if (params[i] != null) { stmt.setObject(i + 1, params[i]); @@ -138,9 +179,98 @@ // VARCHAR works with many drivers regardless // of the actual column type. Oddly, NULL and // OTHER don't work with Oracle's drivers. - stmt.setNull(i + 1, Types.VARCHAR); + int sqlType = Types.VARCHAR; + if (!pmdKnownBroken) { + try { + sqlType = pmd.getParameterType(i + 1); + } catch (SQLException e) { + pmdKnownBroken = true; + } + } + stmt.setNull(i + 1, sqlType); + } + } + } + + /** + * Fill the <code>PreparedStatement</code> replacement parameters with the + * given object's bean property values. + * + * @param stmt + * PreparedStatement to fill + * @param bean + * a JavaBean object + * @param properties + * an ordered array of properties; this gives the order to insert + * values in the statement + * @throws SQLException + * if a database access error occurs + */ + public void fillStatementWithBean(PreparedStatement stmt, Object bean, + PropertyDescriptor[] properties) throws SQLException { + Object[] params = new Object[properties.length]; + for (int i = 0; i < properties.length; i++) { + PropertyDescriptor property = properties[i]; + Object value = null; + try { + if (property.getReadMethod().getParameterTypes().length > 0) { + throw new SQLException( + "Can't use an indexed bean property as a SQL parameter: " + + property.getName()); + } + value = property.getReadMethod().invoke(bean, new Object[0]); + } catch (Exception e) { + throw new RuntimeException(e); + } + params[i] = value; + } + fillStatement(stmt, params); + } + + /** + * Fill the <code>PreparedStatement</code> replacement parameters with the + * given object's bean property values. + * + * @param stmt + * PreparedStatement to fill + * @param bean + * a JavaBean object + * @param propertyNames + * an ordered array of property names (these should match the + * getters/setters); this gives the order to insert values in the + * statement + * @throws SQLException + * if a database access error occurs + */ + public void fillStatementWithBean(PreparedStatement stmt, Object bean, + String... propertyNames) throws SQLException { + PropertyDescriptor[] descriptors; + try { + descriptors = Introspector.getBeanInfo(bean.getClass()) + .getPropertyDescriptors(); + } catch (IntrospectionException e) { + throw new RuntimeException(e); + } + PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length]; + for (int i = 0; i < propertyNames.length; i++) { + String propertyName = propertyNames[i]; + if (propertyName == null) + throw new NullPointerException("propertyName can't be null: " + + i); + boolean found = false; + for (int j = 0; j < descriptors.length; j++) { + PropertyDescriptor descriptor = descriptors[j]; + if (propertyName.equals(descriptor.getName())) { + sorted[i] = descriptor; + found = true; + break; + } } + if (!found) + throw new RuntimeException("Couldn't find bean property: " + + bean.getClass() + " " + propertyName); } + fillStatementWithBean(stmt, bean, sorted); } /** @@ -382,7 +512,9 @@ protected void rethrow(SQLException cause, String sql, Object... params) throws SQLException { - StringBuffer msg = new StringBuffer(cause.getMessage()); + String causeMessage = cause.getMessage(); + if (causeMessage == null) causeMessage = ""; + StringBuffer msg = new StringBuffer(causeMessage); msg.append(" Query: "); msg.append(sql); Copied: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java (from r743293, commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java) URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java?p2=commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java&p1=commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java&r1=743293&r2=743297&rev=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/AbstractListHandler.java Wed Feb 11 10:59:15 2009 @@ -29,7 +29,7 @@ * * @see org.apache.commons.dbutils.ResultSetHandler */ -public abstract class AbstractListHandler implements ResultSetHandler { +public abstract class AbstractListHandler<T> implements ResultSetHandler<List<T>> { /** * Whole <code>ResultSet</code> handler. It produce <code>List</code> as * result. To convert individual rows into Java objects it uses @@ -37,8 +37,8 @@ * * @see #handleRow(ResultSet) */ - public Object handle(ResultSet rs) throws SQLException { - List rows = new ArrayList(); + public List<T> handle(ResultSet rs) throws SQLException { + List<T> rows = new ArrayList<T>(); while (rs.next()) { rows.add(this.handleRow(rs)); } @@ -52,5 +52,5 @@ * @return row processing result * @throws SQLException error occurs */ - protected abstract Object handleRow(ResultSet rs) throws SQLException; + protected abstract T handleRow(ResultSet rs) throws SQLException; } Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ArrayListHandler.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ArrayListHandler.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ArrayListHandler.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ArrayListHandler.java Wed Feb 11 10:59:15 2009 @@ -28,7 +28,7 @@ * * @see org.apache.commons.dbutils.ResultSetHandler */ -public class ArrayListHandler extends GenericListHandler<Object[]> { +public class ArrayListHandler extends AbstractListHandler<Object[]> { /** * The RowProcessor implementation to use when converting rows @@ -62,7 +62,7 @@ * @return <code>Object[]</code>, never <code>null</code>. * * @throws SQLException if a database access error occurs - * @see org.apache.commons.dbutils.handlers.GenericListHandler#handle(ResultSet) + * @see org.apache.commons.dbutils.handlers.AbstractListHandler#handle(ResultSet) */ protected Object[] handleRow(ResultSet rs) throws SQLException { return this.convert.toArray(rs); Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/BeanListHandler.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/BeanListHandler.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/BeanListHandler.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/BeanListHandler.java Wed Feb 11 10:59:15 2009 @@ -18,7 +18,9 @@ import java.sql.ResultSet; import java.sql.SQLException; +import java.util.List; +import org.apache.commons.dbutils.ResultSetHandler; import org.apache.commons.dbutils.RowProcessor; /** @@ -28,7 +30,7 @@ * * @see org.apache.commons.dbutils.ResultSetHandler */ -public class BeanListHandler<T> extends GenericListHandler<T> { +public class BeanListHandler<T> implements ResultSetHandler<List<T>> { /** * The Class of beans produced by this handler. @@ -65,16 +67,17 @@ } /** - * Convert the <code>ResultSet</code> row into a bean with + * Convert the whole <code>ResultSet</code> into a List of beans with * the <code>Class</code> given in the constructor. * - * @return A bean, never <code>null</code>. + * @param rs The <code>ResultSet</code> to handle. + * + * @return A List of beans, never <code>null</code>. * * @throws SQLException if a database access error occurs - * @see org.apache.commons.dbutils.handlers.GenericListHandler#handle(ResultSet) + * @see org.apache.commons.dbutils.RowProcessor#toBeanList(ResultSet, Class) */ - protected T handleRow(ResultSet rs) throws SQLException { - return this.convert.toBean(rs, type); + public List<T> handle(ResultSet rs) throws SQLException { + return this.convert.toBeanList(rs, type); } - } Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java Wed Feb 11 10:59:15 2009 @@ -27,7 +27,7 @@ * @see org.apache.commons.dbutils.ResultSetHandler * @since DbUtils 1.1 */ -public class ColumnListHandler extends GenericListHandler<Object> { +public class ColumnListHandler extends AbstractListHandler<Object> { /** * The column number to retrieve. @@ -75,7 +75,7 @@ * * @throws SQLException if a database access error occurs * - * @see org.apache.commons.dbutils.handlers.GenericListHandler#handle(ResultSet) + * @see org.apache.commons.dbutils.handlers.AbstractListHandler#handle(ResultSet) */ protected Object handleRow(ResultSet rs) throws SQLException { if (this.columnName == null) { Modified: commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/MapListHandler.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/MapListHandler.java?rev=743297&r1=743296&r2=743297&view=diff ============================================================================== --- commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/MapListHandler.java (original) +++ commons/sandbox/dbutils/java5/src/java/org/apache/commons/dbutils/handlers/MapListHandler.java Wed Feb 11 10:59:15 2009 @@ -29,7 +29,7 @@ * * @see org.apache.commons.dbutils.ResultSetHandler */ -public class MapListHandler extends GenericListHandler<Map<String,Object>> { +public class MapListHandler extends AbstractListHandler<Map<String,Object>> { /** * The RowProcessor implementation to use when converting rows @@ -63,7 +63,7 @@ * * @throws SQLException if a database access error occurs * - * @see org.apache.commons.dbutils.handlers.GenericListHandler#handle(ResultSet) + * @see org.apache.commons.dbutils.handlers.AbstractListHandler#handle(ResultSet) */ protected Map<String,Object> handleRow(ResultSet rs) throws SQLException { return this.convert.toMap(rs);