You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by cs...@apache.org on 2006/03/14 21:21:48 UTC
svn commit: r385874 - in /beehive/trunk/system-controls:
src/jdbc/org/apache/beehive/controls/system/jdbc/
src/jdbc/org/apache/beehive/controls/system/jdbc/parser/
test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/
test/jdbc/junit...
Author: cschoett
Date: Tue Mar 14 12:21:45 2006
New Revision: 385874
URL: http://svn.apache.org/viewcvs?rev=385874&view=rev
Log:
Added the ablity for the JdbcControl to perform additional evaluation on ComplexSqlFragments when resolving sql statements.
This modification allows a jdbc control method parameter to return an instance of a ComplexSqlFragment which contains SQL text
AND prepared statement parameters and have the jdbc control resolve it correctly. For example, the SQL text
could be something like: 'WHERE Name = ?' and the parameter value would be the value subsitiuted into the resulting
prepared statement.
Also added new DRTs to test this feature.
Modified:
beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControl.java
beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ReflectionFragment.java
beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlFragment.java
beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java
beehive/trunk/system-controls/test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ResultsTestCtrl.jcx
beehive/trunk/system-controls/test/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/DBSingleRowResultsTest.java
Modified: beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControl.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControl.java?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControl.java (original)
+++ beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/JdbcControl.java Tue Mar 14 12:21:45 2006
@@ -36,6 +36,8 @@
import java.sql.Types;
import java.sql.SQLData;
import java.util.Calendar;
+import java.util.List;
+import java.util.Arrays;
/**
* Simplifies access to a relational database from your Java code using SQL commands.
@@ -561,6 +563,66 @@
*/
public Object clone() {
return new SQLParameter(value, type, dir);
+ }
+ }
+
+ /**
+ * A ComplexSqlFragment can be used as a return value from a parameter reflection operation for
+ * return values which contain BOTH SQL text and parameters. For Example, the text portion
+ * could be something like 'where NAME = ?' and the parameter value is 'Fred'.
+ */
+ public static class ComplexSqlFragment {
+
+ protected CharSequence sql;
+ protected List<SQLParameter> parameters;
+
+ /**
+ * Create a new SQLFragment.
+ */
+ public ComplexSqlFragment() {
+ sql = null;
+ parameters = null;
+ }
+
+ /**
+ * Create a new SQLFragment with the specified SQL and parameter list.
+ *
+ * @param sql SQL contents of the fragment.
+ * @param parameters Substitution parameters.
+ */
+ public ComplexSqlFragment(String sql, SQLParameter[] parameters) {
+ this.sql = sql;
+ if (null != parameters)
+ this.parameters = Arrays.asList(parameters);
+ }
+
+ /**
+ * Get the SQL of this fragment.
+ *
+ * @return String.
+ */
+ public String getSQL() {
+ return sql.toString();
+ }
+
+ /**
+ * Get the parameters contained within this fragment.
+ * Returns a zero-based array.
+ *
+ * @return SQLParameter array.
+ */
+ public SQLParameter[] getParameters() {
+ if (null == parameters)
+ return new SQLParameter[0];
+ return parameters.toArray(new SQLParameter[parameters.size()]);
+ }
+
+ /**
+ * Get the SQL string contained within this fragment.
+ * @return String.
+ */
+ public String toString() {
+ return sql.toString();
}
}
}
Modified: beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ReflectionFragment.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ReflectionFragment.java?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ReflectionFragment.java (original)
+++ beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ReflectionFragment.java Tue Mar 14 12:21:45 2006
@@ -21,6 +21,7 @@
import org.apache.beehive.controls.api.ControlException;
import org.apache.beehive.controls.api.context.ControlBeanContext;
import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
+import org.apache.beehive.controls.system.jdbc.JdbcControl;
import java.lang.reflect.Method;
import java.util.Map;
@@ -49,7 +50,7 @@
*
* @param parameterName The name of the parameter whose value should be substituted at this location.
*/
- ReflectionFragment(String parameterName) {
+ protected ReflectionFragment(String parameterName) {
_parameterName = parameterName;
_sqlDataType = TypeMappingsFactory.TYPE_UNKNOWN;
_nameQualifiers = s_parameterNamePattern.split(_parameterName);
@@ -61,7 +62,7 @@
* @param parameterName The name of the parameter whose value should be substituted at this location.
* @param sqlDataType A String specifing the SQL data type for this parameter.
*/
- ReflectionFragment(String parameterName, String sqlDataType) {
+ protected ReflectionFragment(String parameterName, String sqlDataType) {
this(parameterName);
if (sqlDataType != null) {
_sqlDataType = TypeMappingsFactory.getInstance().convertStringToSQLType(sqlDataType);
@@ -76,8 +77,29 @@
* @param args The method's parameters
* @return Always returns a PREPARED_STATEMENT_SUB_MARK
*/
- String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
- return PREPARED_STATEMENT_SUB_MARK;
+ protected String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
+ // this reflection fragment may resolve to a JdbcControl.ComplexSqlFragment
+ // if so it changes the behavior a bit.
+ Object val = getParameterValue(context, m, args);
+ if (val instanceof JdbcControl.ComplexSqlFragment) {
+ return ((JdbcControl.ComplexSqlFragment)val).getSQL();
+ } else {
+ return PREPARED_STATEMENT_SUB_MARK;
+ }
+ }
+
+ /**
+ * A reflection fragment may evaluate to an JdbcControl.ComplexSqlFragment type,
+ * which requires additional steps to evaluate after reflection.
+ *
+ * @param context Control bean context.
+ * @param m Method.
+ * @param args Method args.
+ * @return true or false.
+ */
+ protected boolean hasComplexValue(ControlBeanContext context, Method m, Object[] args) {
+ Object val = getParameterValue(context, m, args);
+ return val instanceof JdbcControl.ComplexSqlFragment;
}
/**
@@ -85,21 +107,21 @@
*
* @return true
*/
- boolean hasParamValue() { return true; }
+ protected boolean hasParamValue() { return true; }
/**
* Get the parameter name (as specified in the SQL statement).
*
* @return The parameter name.
*/
- String getParameterName() { return _parameterName; }
+ protected String getParameterName() { return _parameterName; }
/**
* Get a copy of the array of parameter name qualifiers.
*
* @return An array of parameter name qualifiers.
*/
- String[] getParameterNameQualifiers() {
+ protected String[] getParameterNameQualifiers() {
String[] nameQualifiersCopy = new String[_nameQualifiers.length];
System.arraycopy(_nameQualifiers, 0, nameQualifiersCopy, 0, _nameQualifiers.length);
return nameQualifiersCopy;
@@ -110,7 +132,7 @@
*
* @return The SQL data type for this param.
*/
- int getParamSqlDataType() { return _sqlDataType; }
+ protected int getParamSqlDataType() { return _sqlDataType; }
/**
* For JUnit testing.
@@ -127,9 +149,32 @@
* @param args Method argument values
* @return All parameter object values contained within this fragment
*/
- Object[] getParameterValues(ControlBeanContext context, Method method, Object[] args) {
+ protected Object[] getParameterValues(ControlBeanContext context, Method method, Object[] args) {
- Object value = null;
+ Object value = getParameterValue(context, method, args);
+ if (value instanceof JdbcControl.ComplexSqlFragment) {
+ JdbcControl.SQLParameter[] params = ((JdbcControl.ComplexSqlFragment)value).getParameters();
+ Object[] values = new Object[params.length];
+ for (int i = 0; i < params.length; i++) {
+ values[i] = params[i].value;
+ }
+ return values;
+ }
+ return new Object[]{value};
+ }
+
+ //
+ // /////////////////////////////////////////////// PRIVATE METHODS /////////////////////////////////////////////
+ //
+
+ /**
+ * Get the value from the method param.
+ * @param method
+ * @param args
+ * @return Value of reflected method param.
+ */
+ private Object getParameterValue(ControlBeanContext context, Method method, Object[] args) {
+ Object value;
try {
value = context.getParameterValue(method, _nameQualifiers[0], args);
} catch (IllegalArgumentException iae) {
@@ -140,32 +185,28 @@
// handle maps, properties, and fields...
value = extractValue(value, _nameQualifiers[i - 1], _nameQualifiers[i]);
}
- return new Object[]{value};
+ return value;
}
- //
- // /////////////////////////////////////////////// PRIVATE METHODS /////////////////////////////////////////////
- //
-
/**
* Get the value from the referenced method parameter using java reflection
*
* @param aValue
* @param aName
* @param bName
- * @return
+ * @return The value
*/
private Object extractValue(Object aValue, String aName, String bName) {
Class aClass = aValue.getClass();
- Object value = null;
+ Object value;
//
// a.isB() or a.getB()
//
String bNameCapped = Character.toUpperCase(bName.charAt(0)) + bName.substring(1);
Method getMethod = null;
- Class retType = null;
+ Class retType;
//
// try a.isB() first, if found verify that a.isB() returns a boolean value,
@@ -198,6 +239,7 @@
}
}
} catch (NoSuchMethodException e) {
+ // ignore
}
//
@@ -206,8 +248,8 @@
if (getMethod == null) {
try {
getMethod = aClass.getMethod("get" + bNameCapped, (Class[])null);
- retType = getMethod.getReturnType();
} catch (NoSuchMethodException e) {
+ // ignore
}
}
@@ -231,7 +273,9 @@
value = aClass.getField(bName).get(aValue);
return value;
} catch (NoSuchFieldException e) {
+ // ignore
} catch (IllegalAccessException e) {
+ // ignore
}
//
@@ -248,11 +292,8 @@
}
// no other options...
- if (true) {
- throw new ControlException("Illegal argument in SQL statement: " + _parameterName.toString()
- + "; unable to find suitable method of retrieving property " + bName.toString()
- + " out of object " + aName.toString() + ".");
- }
- return null;
+ throw new ControlException("Illegal argument in SQL statement: " + _parameterName
+ + "; unable to find suitable method of retrieving property " + bName
+ + " out of object " + aName + ".");
}
}
Modified: beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlFragment.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlFragment.java?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlFragment.java (original)
+++ beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlFragment.java Tue Mar 14 12:21:45 2006
@@ -45,6 +45,19 @@
}
/**
+ * True if this fragment evaluates to a JdbcControl.ComplexSqlFragment, i.e.
+ * a value type which needs additional evaluation after reflection.
+ *
+ * @param context Control bean context.
+ * @param method Method.
+ * @param args Method args.
+ * @return true if evaluates to a complex sql fragment.
+ */
+ protected boolean hasComplexValue(ControlBeanContext context, Method method, Object[] args) {
+ return false;
+ }
+
+ /**
* Get the SQL data type for the parameter value contained within this fragment.
* @return The SQL data type for this fragment.
*/
Modified: beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java (original)
+++ beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java Tue Mar 14 12:21:45 2006
@@ -22,13 +22,15 @@
import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
/**
* Represents a fragement from the SQL annotation's statement member which begins with '{sql:'.
* Substitution fragements are unique in that they are fully evaluated BEFORE a PreparedStatement
* is generated.
* <p/>
- * Supported 'sql:' escapes are subst and fn. subst is the default mode, and will be used if 'sql: '
+ * Supported 'sql:' escapes are subst and fn. subst is the default mode, and will be used if 'sql: '
* is specified.
*
* The <tt>fn</tt> variant of this construct has a very ridgid syntax at this point. It must conform to:
@@ -39,14 +41,16 @@
*
* where the '{y}' could also be some literal term.
*/
-public class SqlSubstitutionFragment extends SqlFragmentContainer {
+public final class SqlSubstitutionFragment extends SqlFragmentContainer {
+
+ private boolean _hasParamValue = false;
/**
* Constructor for subst or function with no param substitution
*
* @param child An child which is contained in this fragment.
*/
- SqlSubstitutionFragment(SqlFragment child) {
+ protected SqlSubstitutionFragment(SqlFragment child) {
super();
addChild(child);
}
@@ -58,7 +62,7 @@
* @param rf The ReflectionFragment containing the parameter substitution
* @param lff A LiteralFragment which contains any text which occures after the parameter substitution.
*/
- SqlSubstitutionFragment(LiteralFragment lf, ReflectionFragment rf, LiteralFragment lff) {
+ protected SqlSubstitutionFragment(LiteralFragment lf, ReflectionFragment rf, LiteralFragment lff) {
super();
addChild(lf);
addChild(rf);
@@ -69,33 +73,61 @@
* Always true for this fragment type
* @return true
*/
- boolean isDynamicFragment() { return true; }
+ protected boolean isDynamicFragment() { return true; }
/**
- * Always false for this fragment type, since all param values are resolved
- * before the prepared statement is created.
- * @return false
+ * Will be true for this fragment type only if one of its children contains
+ * a complex sql fragment.
+ * @return true if there are param values which need to be retrieved.
+ */
+ protected boolean hasParamValue() {
+ return _hasParamValue;
+ }
+
+ /**
+ * Get the parameter values from this fragment and its children. An SqlSubstitutionFragment
+ * only contains parameters if one of its children has a complex value type.
+ *
+ * @param context A ControlBeanContext instance
+ * @param m The annotated method
+ * @param args The method parameters
+ * @return Array of objects.
*/
- boolean hasParamValue() { return false; }
+ protected Object[] getParameterValues(ControlBeanContext context, Method m, Object[] args) {
+ ArrayList<Object> paramValues = new ArrayList<Object>();
+ for (SqlFragment frag : _children) {
+ if (frag.hasComplexValue(context, m, args)) {
+ paramValues.addAll(Arrays.asList(frag.getParameterValues(context, m, args)));
+ }
+ }
+ return paramValues.toArray();
+ }
/**
- * Return the text for a PreparedStatement from this fragment.
+ * Return the text for a PreparedStatement from this fragment. This type of fragment
+ * typically evaluates any reflection parameters at this point. The exception
+ * is if the reflected result is a ComplexSqlFragment, it that case the sql text
+ * is retrieved from the fragment in this method. The parameter values are
+ * retrieved in the getParameterValues method of this class.
*
* @param context A ControlBeanContext instance
* @param m The annotated method
* @param args The method parameters
* @return A String containing the value of this fragment and its children
*/
- String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
+ protected String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
StringBuilder sb = new StringBuilder();
for (SqlFragment frag : _children) {
- if (frag.hasParamValue()) {
+
+ boolean complexFragment = frag.hasComplexValue(context, m, args);
+ if (frag.hasParamValue() && !complexFragment) {
Object[] pValues = frag.getParameterValues(context, m, args);
for (Object o : pValues) {
sb.append(processSqlParams(o));
}
} else {
+ _hasParamValue |= complexFragment;
sb.append(frag.getPreparedStatementText(context, m, args));
}
}
@@ -111,7 +143,7 @@
* seperated by commas.
*
* @param value
- * @return
+ * @return String containing value.
*/
private String processSqlParams(Object value) {
Modified: beehive/trunk/system-controls/test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ResultsTestCtrl.jcx
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ResultsTestCtrl.jcx?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ResultsTestCtrl.jcx (original)
+++ beehive/trunk/system-controls/test/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ResultsTestCtrl.jcx Tue Mar 14 12:21:45 2006
@@ -39,7 +39,7 @@
* This control is used for unit tests for results returned from the db control.
*/
@org.apache.beehive.controls.api.bean.ControlExtension
-@JdbcControl.ConnectionDriver(databaseDriverClass="org.apache.derby.jdbc.EmbeddedDriver", databaseURL="jdbc:derby:MyDB")
+@JdbcControl.ConnectionDriver(databaseDriverClass="org.apache.derby.jdbc.EmbeddedDriver", databaseURL="jdbc:derby:MyDB;create=true")
public interface ResultsTestCtrl extends JdbcControl {
public static class Customer {
@@ -73,6 +73,16 @@
private int foo;
}
+ public static class ComplexWhereClause
+ {
+ public JdbcControl.ComplexSqlFragment getWhereClause()
+ {
+ JdbcControl.SQLParameter[] params = new JdbcControl.SQLParameter[1];
+ params[0] = new JdbcControl.SQLParameter("tester4");
+ return new JdbcControl.ComplexSqlFragment("where fname = ?", params);
+ }
+ }
+
public static class BlobInfo {
private Blob blb;
@@ -155,6 +165,14 @@
//
@SQL(statement="SELECT * FROM USERS WHERE {sql: where}")
public ResultSet getJustOneUser(String where) throws SQLException;
+
+ // query using ComplexSqlFragments in a sql: substitution
+ @SQL(statement="SELECT * FROM USERS {sql: cwc.whereClause}")
+ public ResultSet whereClauseSql(ComplexWhereClause cwc);
+
+ // query using ComplexSqlFragments in a parameter substitution
+ @SQL(statement="SELECT * FROM USERS {cwc.whereClause}")
+ public ResultSet whereClauseSub(ComplexWhereClause cwc);
//
// query returning an array of Object
Modified: beehive/trunk/system-controls/test/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/DBSingleRowResultsTest.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/system-controls/test/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/DBSingleRowResultsTest.java?rev=385874&r1=385873&r2=385874&view=diff
==============================================================================
--- beehive/trunk/system-controls/test/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/DBSingleRowResultsTest.java (original)
+++ beehive/trunk/system-controls/test/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/DBSingleRowResultsTest.java Tue Mar 14 12:21:45 2006
@@ -118,6 +118,32 @@
}
/**
+ * test query which does reflection on a property which contains an ComplexSqlFragment type.
+ * @throws Exception
+ */
+ public void testSqlEscapeComplexWhere() throws Exception {
+ ResultSet rs = testCtrl.whereClauseSql(new ResultsTestCtrl.ComplexWhereClause());
+ assertNotNull(rs);
+ rs.next();
+ String name = rs.getString("FNAME");
+ assertEquals(name, "tester4");
+ rs.close();
+ }
+
+ /**
+ * test query which does reflection on a property which contains an ComplexSqlFragment type.
+ * @throws Exception
+ */
+ public void testComplexWhere() throws Exception {
+ ResultSet rs = testCtrl.whereClauseSub(new ResultsTestCtrl.ComplexWhereClause());
+ assertNotNull(rs);
+ rs.next();
+ String name = rs.getString("FNAME");
+ assertEquals(name, "tester4");
+ rs.close();
+ }
+
+ /**
* param sub from object getter method
* @throws Exception
*/