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