Author: dfabulich Date: Tue Feb 24 17:30:59 2009 New Revision: 747449 URL: http://svn.apache.org/viewvc?rev=747449&view=rev Log: fillStatementWithBean: tweaked exception handling, added unit test
Added: commons/sandbox/dbutils/bugfixing/src/test/org/apache/commons/dbutils/QueryRunnerTest.java Modified: commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/QueryRunner.java Modified: commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/QueryRunner.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/QueryRunner.java?rev=747449&r1=747448&r2=747449&view=diff ============================================================================== --- commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/QueryRunner.java (original) +++ commons/sandbox/dbutils/bugfixing/src/java/org/apache/commons/dbutils/QueryRunner.java Tue Feb 24 17:30:59 2009 @@ -19,6 +19,8 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; @@ -212,16 +214,19 @@ for (int i = 0; i < properties.length; i++) { PropertyDescriptor property = properties[i]; Object value = null; + Method method = property.getReadMethod(); + if (method == null) + throw new RuntimeException("No read method for bean property " + + bean.getClass() + " " + property.getName()); 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); - } + value = method.invoke(bean, new Object[0]); + } catch (InvocationTargetException e) { + throw new RuntimeException("Couldn't invoke method: " + method, e); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Couldn't invoke method with 0 arguments: " + method, e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Couldn't invoke method: " + method, e); + } params[i] = value; } fillStatement(stmt, params); @@ -249,14 +254,13 @@ descriptors = Introspector.getBeanInfo(bean.getClass()) .getPropertyDescriptors(); } catch (IntrospectionException e) { - throw new RuntimeException(e); + throw new RuntimeException("Couldn't introspect bean " + bean.getClass().toString(), 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); + throw new NullPointerException("propertyName can't be null: " + i); boolean found = false; for (int j = 0; j < descriptors.length; j++) { PropertyDescriptor descriptor = descriptors[j]; Added: commons/sandbox/dbutils/bugfixing/src/test/org/apache/commons/dbutils/QueryRunnerTest.java URL: http://svn.apache.org/viewvc/commons/sandbox/dbutils/bugfixing/src/test/org/apache/commons/dbutils/QueryRunnerTest.java?rev=747449&view=auto ============================================================================== --- commons/sandbox/dbutils/bugfixing/src/test/org/apache/commons/dbutils/QueryRunnerTest.java (added) +++ commons/sandbox/dbutils/bugfixing/src/test/org/apache/commons/dbutils/QueryRunnerTest.java Tue Feb 24 17:30:59 2009 @@ -0,0 +1,124 @@ +package org.apache.commons.dbutils; + +import java.beans.IndexedPropertyDescriptor; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class QueryRunnerTest extends TestCase { + QueryRunner runner; + PreparedStatement stmt; + + public void setUp() { + runner = new QueryRunner(); + stmt = fakePreparedStatement(); + } + + public void testFillStatementWithBean() throws SQLException { + TestBean tb = new TestBean(); + tb.setOne("uno"); + tb.setTwo("dos"); + tb.setThree("tres"); + NoOpFillStatement fakeQueryRunner = new NoOpFillStatement(); + fakeQueryRunner.fillStatementWithBean(stmt, tb, new String[] {"three", "two", "one"}); + String[] expected = new String[] {"tres", "dos", "uno"}; + assertArrayEquals("Statement filled with incorrect parameters", expected, fakeQueryRunner.params); + } + + private PreparedStatement fakePreparedStatement() { + InvocationHandler noOpHandler = new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + return null; + } + }; + PreparedStatement stmt = ProxyFactory.instance().createPreparedStatement(noOpHandler); + return stmt; + } + + public void testFillStatementWithBeanErrorNoReadMethod() throws Exception { + TestBean tb = new TestBean(); + PropertyDescriptor noReadMethod = new PropertyDescriptor("one", TestBean.class, null, "setOne"); + + PropertyDescriptor properties[] = new PropertyDescriptor[] { noReadMethod }; + try { + runner.fillStatementWithBean(stmt, tb, properties); + fail("Expected RuntimeException: tried to use a property with no read method"); + } catch (RuntimeException expected) {} + } + + public void testFillStatementWithBeanErrorBadReadMethod() throws Exception { + PropertyDescriptor badReadMethod = new IndexedPropertyDescriptor("indexed", getClass(), null, null, "getIndexed", null) { + public synchronized Method getReadMethod() { + return super.getIndexedReadMethod(); + } + }; + PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; + try { + runner.fillStatementWithBean(stmt, this, properties); + fail("Expected RuntimeException: tried to use a property with no no-arg read method"); + } catch (RuntimeException expected) {} + } + + public void testFillStatementWithBeanErrorReadMethodThrows() throws Exception { + PropertyDescriptor badReadMethod = new PropertyDescriptor("throwsException", getClass(), "getThrowsException", null); + PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; + try { + runner.fillStatementWithBean(stmt, this, properties); + fail("Expected RuntimeException: tried to call a method that throws"); + } catch (RuntimeException expected) {} + } + + public void testFillStatementWithBeanErrorReadMethodPrivate() throws Exception { + getPrivate(); + Method getPrivate = getClass().getDeclaredMethod("getPrivate", new Class[0]); + PropertyDescriptor badReadMethod = new PropertyDescriptor("throwsException", getPrivate, null); + PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; + try { + runner.fillStatementWithBean(stmt, this, properties); + fail("Expected RuntimeException: tried to call a private method"); + } catch (RuntimeException expected) {} + } + + public void testRethrowNullMessage() throws SQLException { + // DBUTILS-40 + SQLException sqe = new SQLException((String)null); + QueryRunner qr = new QueryRunner(); + try { + qr.rethrow(sqe, "foo", new Object[] {"bar"}); + fail("rethrow didn't throw"); + } catch (SQLException expected) {}; + } + + // indexed bean property + public String getIndexed(int index) { + return null; + } + + public String getThrowsException() { + throw new RuntimeException("this getter always throws an exception"); + } + + private String getPrivate() { + return null; + } + + private void assertArrayEquals(String message, Object[] expected, Object[] actual) { + assertEquals(message, Arrays.asList(expected).toString(), Arrays.asList(actual).toString()); + assertEquals(message, expected.length, actual.length); + } + + + private class NoOpFillStatement extends QueryRunner { + Object[] params; + public void fillStatement(PreparedStatement stmt, Object[] params) + throws SQLException { + this.params = params; + } + } +}