You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by dg...@apache.org on 2003/11/09 05:30:51 UTC

cvs commit: jakarta-commons/dbutils/src/test/org/apache/commons/dbutils BaseTestCase.java BasicRowProcessorTest.java TestBean.java

dgraham     2003/11/08 20:30:51

  Modified:    dbutils/src/java/org/apache/commons/dbutils
                        BasicRowProcessor.java
               dbutils/src/test/org/apache/commons/dbutils
                        BaseTestCase.java BasicRowProcessorTest.java
                        TestBean.java
  Log:
  Refactored BasicRowProcessor toBean() and toBeanList()
  to use a new common createBean() method.  This fixed
  a bug where toBean() and toBeanList() handled SQL NULL
  differently.  Now the handling is consistent with
  ResultSet.get* methods.
  
  Revision  Changes    Path
  1.2       +99 -52    jakarta-commons/dbutils/src/java/org/apache/commons/dbutils/BasicRowProcessor.java
  
  Index: BasicRowProcessor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbutils/src/java/org/apache/commons/dbutils/BasicRowProcessor.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- BasicRowProcessor.java	2 Nov 2003 19:15:23 -0000	1.1
  +++ BasicRowProcessor.java	9 Nov 2003 04:30:50 -0000	1.2
  @@ -89,6 +89,24 @@
   public class BasicRowProcessor implements RowProcessor {
   
       /**
  +     * 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 primitveDefaults = new HashMap();
  +
  +    static {
  +        primitveDefaults.put(Integer.TYPE, new Integer(0));
  +        primitveDefaults.put(Short.TYPE, new Short((short) 0));
  +        primitveDefaults.put(Byte.TYPE, new Byte((byte) 0));
  +        primitveDefaults.put(Float.TYPE, new Float(0));
  +        primitveDefaults.put(Double.TYPE, new Double(0));
  +        primitveDefaults.put(Long.TYPE, new Long(0));
  +        primitveDefaults.put(Boolean.TYPE, Boolean.FALSE);
  +        primitveDefaults.put(Character.TYPE, new Character('\u0000'));
  +    }
  +
  +    /**
        * Special array index that indicates there is no bean property that
        * matches a column from a ResultSet.
        */
  @@ -123,15 +141,13 @@
       public Object[] toArray(ResultSet rs) throws SQLException {
           ResultSetMetaData meta = rs.getMetaData();
           int cols = meta.getColumnCount();
  -        Object[] objs = new Object[cols];
  +        Object[] result = new Object[cols];
   
           for (int i = 0; i < cols; i++) {
  -            Object obj = rs.getObject(i + 1);
  -
  -            objs[i] = rs.wasNull() ? null : obj;
  +            result[i] = rs.getObject(i + 1);
           }
   
  -        return objs;
  +        return result;
       }
   
       /**
  @@ -148,38 +164,31 @@
        * 
        *     <li>
        *     The property's set method parameter type matches the column 
  -     *     type. If the datatypes do not match, the setter will not be called.
  +     *     type. If the data types do not match, the setter will not be called.
        *     </li>
        * </ol>
  +     * 
  +     * <p>
  +     * Primitive bean properties are set to their defaults when SQL NULL is
  +     * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
  +     * and booleans are set to false.  Object bean properties are set to 
  +     * <code>null</code> when SQL NULL is returned.  This is the same behavior
  +     * as the <code>ResultSet</code> get* methods.
  +     * </p>
  +     * 
        * @see org.apache.commons.dbutils.RowProcessor#toBean(java.sql.ResultSet, java.lang.Class)
        */
       public Object toBean(ResultSet rs, Class type) throws SQLException {
  -        Object obj = newInstance(type);
   
  -        PropertyDescriptor[] pd = propertyDescriptors(type);
  +        PropertyDescriptor[] props = this.propertyDescriptors(type);
   
           ResultSetMetaData rsmd = rs.getMetaData();
  -        int cols = rsmd.getColumnCount();
  -        for (int i = 1; i <= cols; i++) {
  -            String columnName = rsmd.getColumnName(i);
   
  -            for (int j = 0; j < pd.length; j++) {
  -
  -                if (columnName.equalsIgnoreCase(pd[j].getName())) {
  -                    Object value = rs.getObject(i);
  +        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
   
  -                    if (rs.wasNull()
  -                        && pd[j].getPropertyType().isPrimitive()) {
  -                        continue;
  -                    }
  -
  -                    callSetter(pd[j], obj, value);
  -                    break;
  -                }
  -            }
  -        }
  +        int cols = rsmd.getColumnCount();
   
  -        return obj;
  +        return this.createBean(rs, type, props, columnToProperty, cols);
       }
   
       /**
  @@ -196,9 +205,18 @@
        * 
        *     <li>
        *     The property's set method parameter type matches the column 
  -     *     type. If the datatypes do not match, the setter will not be called.
  +     *     type. If the data types do not match, the setter will not be called.
        *     </li>
        * </ol>
  +     * 
  +     * <p>
  +     * Primitive bean properties are set to their defaults when SQL NULL is
  +     * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
  +     * and booleans are set to false.  Object bean properties are set to 
  +     * <code>null</code> when SQL NULL is returned.  This is the same behavior
  +     * as the <code>ResultSet</code> get* methods.
  +     * </p>
  +     * 
        * @see org.apache.commons.dbutils.RowProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
        */
       public List toBeanList(ResultSet rs, Class type) throws SQLException {
  @@ -208,32 +226,58 @@
               return results;
           }
   
  -        PropertyDescriptor[] pd = propertyDescriptors(type);
  +        PropertyDescriptor[] pd = this.propertyDescriptors(type);
           ResultSetMetaData rsmd = rs.getMetaData();
  -
  -        int[] columnNameToIndex = this.mapColumnsToProperties(rsmd, pd);
  -
  +        int[] columnToProperty = this.mapColumnsToProperties(rsmd, pd);
           int cols = rsmd.getColumnCount();
   
           do {
  -            Object obj = newInstance(type);
  +            results.add(this.createBean(rs, type, pd, columnToProperty, cols));
   
  -            for (int i = 1; i <= cols; i++) {
  -                Object value = rs.getObject(i);
  -                if (rs.wasNull()) {
  -                    continue;
  -                }
  +        } while (rs.next());
   
  -                if (columnNameToIndex[i] != PROPERTY_NOT_FOUND) {
  -                    callSetter(pd[columnNameToIndex[i]], obj, value);
  -                }
  +        return results;
  +    }
  +
  +    /**
  +     * Creates a new object and initializes its fields from the ResultSet.
  +     * @param rs
  +     * @param type
  +     * @param props
  +     * @param columnToProperty
  +     * @param cols
  +     * @return An initialized object.
  +     * @throws SQLException
  +     */
  +    private Object createBean(
  +        ResultSet rs,
  +        Class type,
  +        PropertyDescriptor[] props,
  +        int[] columnToProperty,
  +        int cols)
  +        throws SQLException {
  +
  +        Object bean = this.newInstance(type);
  +
  +        for (int i = 1; i <= cols; i++) {
  +
  +            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
  +                continue;
               }
  +            
  +            Object value = rs.getObject(i);
   
  -            results.add(obj);
  +            PropertyDescriptor prop = props[columnToProperty[i]];
  +            Class propType = prop.getPropertyType();
   
  -        } while (rs.next());
  +            if (propType != null && value == null && propType.isPrimitive()) {
  +                value = primitveDefaults.get(propType);
  +            }
   
  -        return results;
  +            this.callSetter(bean, prop, value);
  +        }
  +
  +        return bean;
       }
   
       /**
  @@ -293,15 +337,18 @@
       /**
        * Calls the setter method on the target object for the given property.
        * If no setter method exists for the property, this method does nothing.
  -     * @param pd The property to set.
        * @param target The object to set the property on.
  +     * @param prop The property to set.
        * @param value The value to pass into the setter.
        * @throws SQLException if an error occurs setting the property.
        */
  -    private void callSetter(PropertyDescriptor pd, Object target, Object value)
  +    private void callSetter(
  +        Object target,
  +        PropertyDescriptor prop,
  +        Object value)
           throws SQLException {
   
  -        Method setter = pd.getWriteMethod();
  +        Method setter = prop.getWriteMethod();
   
           if (setter == null) {
               return;
  @@ -316,15 +363,15 @@
   
           } catch (IllegalArgumentException e) {
               throw new SQLException(
  -                "Cannot set " + pd.getName() + ": " + e.getMessage());
  +                "Cannot set " + prop.getName() + ": " + e.getMessage());
   
           } catch (IllegalAccessException e) {
               throw new SQLException(
  -                "Cannot set " + pd.getName() + ": " + e.getMessage());
  +                "Cannot set " + prop.getName() + ": " + e.getMessage());
   
           } catch (InvocationTargetException e) {
               throw new SQLException(
  -                "Cannot set " + pd.getName() + ": " + e.getMessage());
  +                "Cannot set " + prop.getName() + ": " + e.getMessage());
           }
       }
   
  
  
  
  1.2       +18 -8     jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/BaseTestCase.java
  
  Index: BaseTestCase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/BaseTestCase.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- BaseTestCase.java	2 Nov 2003 19:15:23 -0000	1.1
  +++ BaseTestCase.java	9 Nov 2003 04:30:50 -0000	1.2
  @@ -63,6 +63,7 @@
   
   import java.sql.ResultSet;
   import java.sql.ResultSetMetaData;
  +import java.util.Date;
   
   import junit.framework.Test;
   import junit.framework.TestCase;
  @@ -93,12 +94,15 @@
               "three",
               "notInBean",
               "intTest",
  -            "integerTest" };
  +            "integerTest",
  +            "nullObjectTest",
  +            "nullPrimitiveTest",
  +            "wrongType" };
   
       /**
        * The number of columns in the MockResultSet.
        */
  -    protected static final int COLS = 6;
  +    protected static final int COLS = columnNames.length;
   
       protected static final ResultSetMetaData metaData =
           MockResultSetMetaData.create(columnNames);
  @@ -110,7 +114,10 @@
               "3",
               "  notInBean  ",
               new Integer(1),
  -            new Integer(2)};
  +            new Integer(2),
  +            null,
  +            null,
  +            new Date()};
   
       private static final Object[] row2 =
           new Object[] {
  @@ -119,14 +126,17 @@
               "6",
               "  notInBean  ",
               new Integer(3),
  -            new Integer(4)};
  +            new Integer(4),
  +            null,
  +            null,
  +            new Date()};
   
       private static final Object[][] rows = new Object[][] { row1, row2 };
   
       /**
        * The number of rows in the MockResultSet.
        */
  -    protected static final int ROWS = 2;
  +    protected static final int ROWS = rows.length;
   
       /**
        * The ResultSet all test methods will use.
  
  
  
  1.2       +81 -75    jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/BasicRowProcessorTest.java
  
  Index: BasicRowProcessorTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/BasicRowProcessorTest.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- BasicRowProcessorTest.java	2 Nov 2003 19:15:23 -0000	1.1
  +++ BasicRowProcessorTest.java	9 Nov 2003 04:30:50 -0000	1.2
  @@ -72,82 +72,88 @@
    */
   public class BasicRowProcessorTest extends BaseTestCase {
   
  -	private static final RowProcessor processor = BasicRowProcessor.instance();
  +    private static final RowProcessor processor = BasicRowProcessor.instance();
   
  -	/**
  -	 * Constructor for BasicRowProcessorTest.
  -	 * @param name
  -	 */
  -	public BasicRowProcessorTest(String name) {
  -		super(name);
  -	}
  -
  -	public void testToArray() throws SQLException {
  -
  -		int rowCount = 0;
  -		Object[] a = null;
  -		while (this.rs.next()) {
  -			a = processor.toArray(this.rs);
  -			assertEquals(COLS, a.length);
  -			rowCount++;
  -		}
  -
  -		assertEquals(ROWS, rowCount);
  -		assertEquals("4", a[0]);
  -		assertEquals("5", a[1]);
  -		assertEquals("6", a[2]);
  -	}
  -
  -	public void testToBean() throws SQLException {
  -
  -		int rowCount = 0;
  -		TestBean b = null;
  -		while (this.rs.next()) {
  -			b = (TestBean) processor.toBean(this.rs, TestBean.class);
  -			assertNotNull(b);
  -			rowCount++;
  -		}
  -
  -		assertEquals(ROWS, rowCount);
  -		assertEquals("4", b.getOne());
  -		assertEquals("5", b.getTwo());
  -		assertEquals("6", b.getThree());
  -		assertEquals("not set", b.getDoNotSet());
  +    /**
  +     * Constructor for BasicRowProcessorTest.
  +     * @param name
  +     */
  +    public BasicRowProcessorTest(String name) {
  +        super(name);
  +    }
  +
  +    public void testToArray() throws SQLException {
  +
  +        int rowCount = 0;
  +        Object[] a = null;
  +        while (this.rs.next()) {
  +            a = processor.toArray(this.rs);
  +            assertEquals(COLS, a.length);
  +            rowCount++;
  +        }
  +
  +        assertEquals(ROWS, rowCount);
  +        assertEquals("4", a[0]);
  +        assertEquals("5", a[1]);
  +        assertEquals("6", a[2]);
  +    }
  +
  +    public void testToBean() throws SQLException {
  +
  +        int rowCount = 0;
  +        TestBean b = null;
  +        while (this.rs.next()) {
  +            b = (TestBean) processor.toBean(this.rs, TestBean.class);
  +            assertNotNull(b);
  +            rowCount++;
  +        }
  +
  +        assertEquals(ROWS, rowCount);
  +        assertEquals("4", b.getOne());
  +        assertEquals("5", b.getTwo());
  +        assertEquals("6", b.getThree());
  +        assertEquals("not set", b.getDoNotSet());
           assertEquals(3, b.getIntTest());
           assertEquals(new Integer(4), b.getIntegerTest());
  -	}
  -
  -	public void testToBeanList() throws SQLException {
  -
  -		List list = processor.toBeanList(this.rs, TestBean.class);
  -		assertNotNull(list);
  -		assertEquals(ROWS, list.size());
  -
  -		TestBean b = (TestBean) list.get(1);
  -
  -		assertEquals("4", b.getOne());
  -		assertEquals("5", b.getTwo());
  -		assertEquals("6", b.getThree());
  -		assertEquals("not set", b.getDoNotSet());
  +        assertEquals(null, b.getNullObjectTest());
  +        assertEquals(0, b.getNullPrimitiveTest());
  +        assertEquals("not a date", b.getNotADate());
  +    }
  +
  +    public void testToBeanList() throws SQLException {
  +
  +        List list = processor.toBeanList(this.rs, TestBean.class);
  +        assertNotNull(list);
  +        assertEquals(ROWS, list.size());
  +
  +        TestBean b = (TestBean) list.get(1);
  +
  +        assertEquals("4", b.getOne());
  +        assertEquals("5", b.getTwo());
  +        assertEquals("6", b.getThree());
  +        assertEquals("not set", b.getDoNotSet());
           assertEquals(3, b.getIntTest());
           assertEquals(new Integer(4), b.getIntegerTest());
  -	}
  -
  -	public void testToMap() throws SQLException {
  -
  -		int rowCount = 0;
  -		Map m = null;
  -		while (this.rs.next()) {
  -			m = processor.toMap(this.rs);
  -			assertNotNull(m);
  -			assertEquals(COLS, m.keySet().size());
  -			rowCount++;
  -		}
  -
  -		assertEquals(ROWS, rowCount);
  -		assertEquals("4", m.get("One")); // case shouldn't matter
  -		assertEquals("5", m.get("two"));
  -		assertEquals("6", m.get("THREE"));
  -	}
  +        assertEquals(null, b.getNullObjectTest());
  +        assertEquals(0, b.getNullPrimitiveTest());
  +        assertEquals("not a date", b.getNotADate());
  +    }
  +
  +    public void testToMap() throws SQLException {
  +
  +        int rowCount = 0;
  +        Map m = null;
  +        while (this.rs.next()) {
  +            m = processor.toMap(this.rs);
  +            assertNotNull(m);
  +            assertEquals(COLS, m.keySet().size());
  +            rowCount++;
  +        }
  +
  +        assertEquals(ROWS, rowCount);
  +        assertEquals("4", m.get("One")); // case shouldn't matter
  +        assertEquals("5", m.get("two"));
  +        assertEquals("6", m.get("THREE"));
  +    }
   
   }
  
  
  
  1.2       +46 -3     jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/TestBean.java
  
  Index: TestBean.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbutils/src/test/org/apache/commons/dbutils/TestBean.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestBean.java	2 Nov 2003 19:15:23 -0000	1.1
  +++ TestBean.java	9 Nov 2003 04:30:50 -0000	1.2
  @@ -81,6 +81,25 @@
       private String doNotSet = "not set";
   
       /**
  +     * toBean() should set primitive fields to their defaults (ie. 0) when 
  +     * null is returned from the ResultSet.
  +     */
  +    private int nullPrimitiveTest = 7;
  +
  +    /**
  +     * toBean() should set Object fields to null when null is returned from the
  +     * ResultSet
  +     */
  +    private Object nullObjectTest = "overwrite";
  +
  +    /**
  +     * The property should not be set when the object returned from the 
  +     * ResultSet does not match the type of the bean property.  In this case, 
  +     * a Date will be returned but the property is a String.
  +     */
  +    private String notADate = "not a date";
  +
  +    /**
        * Constructor for TestBean.
        */
       public TestBean() {
  @@ -133,6 +152,30 @@
   
       public void setIntTest(int i) {
           intTest = i;
  +    }
  +
  +    public Object getNullObjectTest() {
  +        return nullObjectTest;
  +    }
  +
  +    public int getNullPrimitiveTest() {
  +        return nullPrimitiveTest;
  +    }
  +
  +    public void setNullObjectTest(Object object) {
  +        nullObjectTest = object;
  +    }
  +
  +    public void setNullPrimitiveTest(int i) {
  +        nullPrimitiveTest = i;
  +    }
  +
  +    public String getNotADate() {
  +        return notADate;
  +    }
  +
  +    public void setNotADate(String string) {
  +        notADate = string;
       }
   
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org