Friday, March 16, 2012

Displaying the row count for an af:table

Problem

Say you have an af:table based on a view object.  You want to display the number of rows in the table.  Below I describe a couple ways to do it.  The solutions apply to ADF 11.1.1.3.

Solution 1

Create an af:outputText on the page.  Set the value to an EL expression that returns the estimated row count.  Use partialTriggers to refresh the row count when needed.

    <af:outputText value="  Row count: #{bindings.MyVOIterator.estimatedRowCount}"
                              id="ot4" partialTriggers="cb1 cb2"/>

Thanks to the unknown commenter for suggesting this solution.

Solution 2

1. Create a method in a managed bean to return the number of rows.  E.g.

    public int getLineCount() {
        BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();
        DCBindingContainer bindings = (DCBindingContainer)bc;
        DCIteratorBinding iter = bindings.findIteratorBinding("MyVOIterator");
        return iter.getRowSetIterator().getRowCount();
    }


2.  Make sure there is a binding for the VO iterator ("MyVOIterator") in the page definition.

3. Create an af:outputText on the page.  Set the value to an EL expression that points to the managed bean method.  Use partialTriggers to refresh the row count when needed.

    <af:outputText value="Row count: #{backingBeanScope.MyBean.lineCount}"
                   id="ot2" partialTriggers="cb1 cb2"/>


Note on getRowCount() vs getEstimatedRowCount()

The ViewObjectImpl class has two methods that return the row count: getRowCount() and getEstimatedRowCount().  See this post for a good explanation of the differences.  Briefly:
  • getRowCount() is more accurate, but less performant.
  • getEstimatedRowCount() is potentially less accurate, but more performant. 

Thanks to Richard Ver Steeg for his insights on this topic.

    Monday, March 5, 2012

    Using the ADF BC userData object

    Introduction

    ADF BC provides a userData object that is stored in the session.  It is useful for storing data related to the user and/or the session.  However, there are some nuances you need to address.

    The following applies to ADF 11.1.1.3.

    The userData object

    The userData object is a Hashtable.  Key/value pairs are stored as type Object.  Here's an example of storing data.  Note that the "userId" in this example is originally of type oracle.jbo.domain.Number.

        protected void storeUserDetails(Number userId, String userName) {
            this.getSession().getUserData().put("userId", userId);
            this.getSession().getUserData().put("userName", userName);
        }


    You can then retrieve the values like this:

        Number userId =
            (Number)this.getSession().getUserData().get("userId");
        String userName = this.getSession().getUserData().get("userName");


    Problem

    However, when the Application Module passivates, the userData is lost.  To keep the userData, you must include custom code in your Application Module class.  This post has good example code for this.  Note however that the example code stores and retrieves the values as String in an XML "TextNode".  This causes a problem when you try to retrieve a Number after a passivation/activation cycle.  Executing this ...

        Number userId = this.getSession().getUserData().get("userId");

      ... will now cause "java.lang.ClassCastException: java.lang.String cannot be cast to oracle.jbo.domain.Number"

    Solution

    Use a helper method to retrieve Numbers from the userData object.

    Here' s the helper method:

        public Number getNumber(Object obj) {
            Number num = null;
            try {
                num = new Number(obj);    // Works when value is a String
            } catch (SQLException sqle) {
                num = (Number)obj;    // Works when value is an Object
            }
            return num;
        }


    Here's it's usage within a VO implementation class:

        MyAppModuleImpl am = (MyAppModuleImpl)this.getApplicationModule();
        Number userId =

            am.getNumber(am.getSession().getUserData().get("userId"));

    This helper method works whether the userData object has been passivated/activated or not.