31 May 2012

Multiple iterator bindings for one View Object

The common practice (and default option for the declarative approach) is to create one iterator binding per View Object in the Page Def file.  But there's nothing to prevent us from defining several iterators in the PageDef's executable section for the same View Object. By default iterator binding binds to default row set iterator of the View Object. Iterator binding has RSIName attribute which allows us to bind to the named row set iterator of the View Object. When it could be an option? Let's assume we have a View Object with a bind variable in its query and  we need to show on the same page two separate results of the query with different values of the bind variable:

select * from Employees
where job_id = :job_id 

As we know View Object can have many secondary row sets besides its main default row set which is used by default. Every row set can store its own set of bind variable values. The view links functionality is based on this feature. We are going to use it as well. So, we have our ViewObjectImpl class with two overridden methods:

  protected void create() { 
    super.create(); 
    
    //Create Row Set for clerks and define value for the job_id bind variable
    ViewRowSetImpl rsClerk = (ViewRowSetImpl) createRowSet("ClerkRSI");
    rsClerk.setExecuteParameters(null, 
                    new Object[] { new Object[] {"job_id", "ST_CLERK"}}, false);       

    //Create Row Set for managers and define value for the job_id bind variable
    ViewRowSetImpl rsMan = (ViewRowSetImpl) createRowSet("ManRSI");
    rsMan.setExecuteParameters(null,
                    new Object[] { new Object[] {"job_id", "ST_MAN"}}, false);       

    }
  
   public oracle.jbo.RowSetIterator findRowSetIterator(java.lang.String p1) {
       RowSetIterator rsi = super.findRowSetIterator(p1);        
       if (rsi == null) 
          //Probably we are looking for  ClerkRSI or ManRSI
           return findRowSet(p1);
        return rsi; 
      }


And in our Page Def file we have two iterator bindings:

    <iterator Binds="VEmp" RangeSize="25" DataControl="AppModuleDataControl"
              id="VEmpClerkIterator" RSIName="ClerkRSI"/>
    <iterator Binds="VEmp" RangeSize="25" DataControl="AppModuleDataControl"
              id="VEmpManagerIterator" RSIName="ManRSI"/>


 
These bindings can be used to put two separate tables on our page with clerks and managers.

That's it!

20 May 2012

Working with VO's built-in aggregation functions

When we work with View Link accessors or Association accessors we can use Groovy for aggregation calculations in a very convenient way. Groovy API provides five predefined functions:
  • accessor.sum("expr")
  • accessor.count("expr")
  • accessor.avg("expr")
  • accessor.min("expr")
  • accessor.max("expr")
This API is commonly used to calculate values of transient attributes in the master VO's.  But what if we don't have any master-detail relationship and don't have any accessors? We just have a ViewObject and we need to do some aggregation calculations on it - this is very common use case.  The common practice for this issue is to write custom Java method in the ViewObjectImpl class. But we have already five (that is enough for most cases) built-in methods used by the Groovy API. These methods are implemented by the ViewRowSetImpl (extends RowSetHelper) class and as any useful methods these ones are private. Groovy API uses InvokerHelper class to invoke them as it could be easily seen from the call stack. Let's do the same. Off-course these built-in methods can be deprecated in the future in the latest versions of ADF and we should be aware of that, but until it has not happened we do the following:
 
     1. Create an inner helper class in our ViewObjectImpl
   private class AgrFuncHelper extends HashMap
  {
    private String funcName;

    public AgrFuncHelper(String funcName) 
    {
      super();
      this.funcName = funcName;  
    }


    public Object get(Object key) 
    {
      //Invoke private method
      //of our DefaultRowSet (sum,count,avg,min,max)
      //key is argument expression for the aggr funcion being called
      //sum("Salary")

      return InvokerHelper.invokeMethod(getDefaultRowSet(), funcName, key);
    }

  }

 
    2. Publish aggregation methods
  public Map getSum() 
  {
    return new AgrFuncHelper("sum");
  }
  
  public Map getCount() 
  {
    return new AgrFuncHelper("count");
  }

 
   3. Use the methods in jspx
<af:outputText value="#{bindings.EmployeesView1Iterator.viewObject.sum['Salary']}"                        
   id="ot12"/>
<af:outputText value="#{bindings.EmployeesView1Iterator.viewObject.count['*']}" 
  id="ot13"/>
 


That's it!