You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by df...@apache.org on 2009/02/11 11:59:15 UTC

svn commit: r743297 - in /commons/sandbox/dbutils/java5: ./ src/java/org/apache/commons/dbutils/ src/java/org/apache/commons/dbutils/handlers/

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