The ResultHandler<RES> interface

You can create an implementation of the com.ibm.pdq.runtime.handlers.ResultHandler<RES> interface that pureQuery can use to create an object that represents a query result.
The ResultHandler<RES> interface declares only one method:
public T handle (ResultSet resultSet);

pureQuery calls the handle() method to create an object that represents a query result. pureQuery passes the query results to the method in an instance of java.sql.ResultSet. If you are creating an implementation of ResultHandler<RES>, implement this method to create and return an object of type <RES> that represents a query result in the ResultSet instance.

When you specify a handler as a parameter, the parameter must be the last parameter in the method signature. If you specify a ResultHandler together with a ParameterHandler as parameters, the handlers must be the last two parameters in the method signature.

Imagine that an application that queries a data source sends the results of the queries in full as String objects to another application. That other application expects the String to contain a series of objects in the JSON format. You can use the handle() method in the ResultHandler<RES> interface to convert the query results to a single JSON string that represents an array of objects.

package customHandlers;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import com.ibm.pdq.runtime.handlers.ResultHandler;

public class JSONResultHandler implements ResultHandler<String>
{
  // The ResultHandler<RES> interface defines this method
  public String handle (ResultSet rs)
  {
    StringBuffer stringBuffer = new StringBuffer ();
    try {
      ResultSetMetaData resultSetMetaData = rs.getMetaData ();
      int columnCount = resultSetMetaData.getColumnCount ();
      boolean first = true;
      stringBuffer.append ("[");
      while (rs.next ()) {
        // All row objects, except the last, are followed by a ","
        if (!first) stringBuffer.append (",");
        first = false;
        stringBuffer.append ("{");
        for (int x = 1; x <= columnCount; x++) {
          stringBuffer.append ("\"");
          stringBuffer.append (toJSONString (resultSetMetaData.getColumnLabel (x)));
          stringBuffer.append ("\":\"");
          stringBuffer.append (toJSONString (rs.getString (x)));
          stringBuffer.append ("\"");
          if (x < columnCount) stringBuffer.append (",");
        }
        stringBuffer.append ("}");
      }
      stringBuffer.append ("]");
    }
    catch (SQLException e) {
      throw new RuntimeException ("The JSONResultHandler failed to create a JSON string from the query results.", e);
    }
    return stringBuffer.toString ();
  }

  private String toJSONString (String value)
  {
    if (value != null) {
      int valueLength = value.length ();
      StringBuffer sb = new StringBuffer (valueLength);
      for (int i = 0; i < valueLength; i++) {
        char c = value.charAt (i);
        switch (c) {
          case '\\':
            sb.append ("\\\\");
          break;
          case '"':
            sb.append ("\\\"");
          break;
          case '/':
            sb.append ("\\/");
          break;
          case 0x08:
            sb.append ("\\b");
          break;
          case 0xC:
            sb.append ("\\f");
          break;
          case 0xA:
            sb.append ("\\n");
          break;
          case 0xD:
            sb.append ("\\r");
          break;
          case 0x9:
            sb.append ("\\t");
          break;
          default:
            sb.append (c);
          break;
        }
      }
      return sb.toString ();
    }
    else
      return null;
  }

}

Here is an example of a JSON string that the JSONResultHandler could produce:

[{"DEPTNO":"A00","DEPTNAME":"SPIFFY COMPUTER SERVICE DIV.","MGRNO":"000010","ADMRDEPT":"A00","LOCATION":"null"},
{"DEPTNO":"B01","DEPTNAME":"PLANNING","MGRNO":"000020",
"ADMRDEPT":"A00","LOCATION":"null"},...]

Using ResultHandler objects with annotated methods

There are two ways in which you can specify ResultHandler objects for annotated methods.

Use the @Handler annotation
Provide the @Handler annotation on the definition of the method, in addition to using the @Select or @Update annotation. In the @Handler annotation, you specify the class of your ResultHandler implementation. The handler that you specify must have a public, no-argument constructor. pureQuery uses the no-argument constructor to create a single instance of the handler, and that instance is used by all invocations of the annotated method.
For example, you could declare an annotated method that uses JSONResultHandler. pureQuery instantiates the handler with the no-argument constructor.
@Select(sql = “select * from employee where workdept = ?1.departmentNumber") 
@Handler(resultHandler = customHandlers.JSONResultHandler.class) 
String toJSON(DepartmentBean department);
Use method parameters
Alternatively, you can specify the ResultHandler implementation as a parameter of the annotated method definition. When your application calls the method, it can provide the actual instance for pureQuery to use.

When you specify a handler as a parameter, the parameter must be the last parameter in the method signature.

For example, suppose you wanted to get the results of your query in an Iterator<EmployeeBean>, and that you only wanted the Iterator<EmployeeBean> to contain rows 11 through 20 of the results. You could use the com.ibm.pdq.runtime.data.handlers.IteratorPagingResultHandler<RES>. In an interface, you could define an annotated method that takes an instance of IteratorPagingResultHandler<RES> as a parameter.

@Select(sql = "select * from employee where workdept = ?1.departmentNumber")
  Iterator<EmployeeBean> selectEmployeesInDepartment(DepartmentBean department, IteratorPagingResultHandler resultHandler);

If you wanted to get rows 11 through 20 of the results, you could invoke the method like this:

// Execute the query and create an iterator for rows 11-20
Iterator<EmployeeBean> employees = face.selectEmployeesInDepartment(theDepartment, new IteratorPagingResultHandler(EmployeeBean.class, 11, 20));

If you wanted to use a single annotated method declaration with multiple ResultHandler<RES> implementations, you could define an annotated method that uses the generic ResultHandler<RES> in the signature:

@Select(sql = "select * from employee where workdept = ?1.departmentNumber")
  <RES> RES selectEmployeesInDepartment(DepartmentBean department, ResultHandler<RES> resultHandler);

You could then invoke the method by passing a ResultHandler object of a specific type:

Iterator<EmployeeBean> employee = face.selectEmployeesInDepartment(theDepartment, new IteratorPagingResultHandler(EmployeeBean.class, 11, 20));

Using ResultHandler objects with inline methods

Suppose you wanted to get the results of your query in an Iterator<EmployeeBean>, and that you only wanted the Iterator<EmployeeBean> to contain rows 11 through 20 of the results. You could use the com.ibm.pdq.runtime.data.handlers.IteratorPagingResultHandler<RES>. you could define an inline method that takes an instance of IteratorPagingResultHandler<RES> as a parameter.
Connection con = DriverManager.getConnection(...);
  Data d = DataFactory.getData(con);

// Execute the query and create an iterator for rows 11-20
  Iterator<EmployeeBean> employees = d.query("select * from employee where workdept = ?1.departmentNumber",
         theDepartment, new IteratorPagingResultHandler(EmployeeBean.class,11, 20));

Feedback