Thursday, December 23, 2010

[SQL Server] Paging with Total Record Count in SQL Server 2005


In professional business, developers are usually required to query database and fetch results. With huge datasets, it downgrades the application performance. One of the solutions to this problem is paging which allows the user to fetch small chunks of data and traverse the entire dataset through forward/backward navigations.

While paging, developers do a big mistake. They fetch the entire dataset and filter out required data chunk at application level. This works pretty well with small datasets, but doesn’t suit well for huge datasets. The good approach is to request only the required chunk of data and improve the application response time. This is how we’ll do it:

Java Code:

private static final int DEFAULT_PAGE_SIZE = 10;
int totalRecords = -1, recordShowing = 0, pageIndex = 0, pageSize = DEFAULT_PAGE_SIZE;

String action = request.getParameter("action");
pageIndex = StringUtils.isBlank( request.getParameter("pageIndex") ) ? 0 : Integer.valueOf(request.getParameter("pageIndex")) ;   
pageIndex = (pageIndex <= 0) ? 0 : pageIndex;

if ( action.equalsIgnoreCase("showNone") ) {
    // Some code to display default page, when no action is set (if required)...
}
else {
    // Set page Index and page Size, based on user action
    if ( action.equalsIgnoreCase("showAll") ) {    // for Show All records
        pageSize = -1;
        pageIndex = 0;
    }
    else if ( action.equalsIgnoreCase("showPrev") ) { // for Backward navigation
        pageIndex = (pageIndex <= 0) ? 0 : (pageIndex - 1);   
    }
    else if ( action.equals("showNext") ) { // for Forward Navigation
        pageIndex = pageIndex < 0 ? 0 : (pageIndex + 1);
    }
   
    int startIndex = (pageSize < 0) ? -1 : (pageIndex * pageSize) + 1;
    int endIndex = (pageSize < 0) ? -1 : (pageIndex * pageSize) + pageSize;

    /**
    *    Call your DAO method to fetch the required result set...
    *     Here I'm assuming that result set is returned in following fashion:
    *
    *    Map<Object, Object> serachResult = new HashMap<Object, Object>();
    *    serachResult.put("totalRecords", totalRecords);
    *    serachResult.put("resultList", resultList);
    */
   
    // Set variables based on the result set 
    // to display proper pagination info to the user
    totalRecords = Integer.valueOf( resultSet.get("totalRecords").toString() );
    pageSize = (pageSize == -1) ? totalRecords : pageSize;
   
    if ( action.equalsIgnoreCase("showAll") ) {
        recordShowing = resultList.size();
    }
    else {
        recordShowing = resultList.size() < DEFAULT_PAGE_SIZE ? totalRecords : (pageIndex + 1) * DEFAULT_PAGE_SIZE;
    }   
}

 
SQL Query:

In SQL, we can achieve this through temp. tables and CTE (Common Table Expressions) as well. But what if these are not the options for us. Below is how we can do it through select query. 

SELECT *  FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY <column-name>) AS ROWNUM
    , COUNT(1) OVER () AS TOTALRECORDS
    FROM <table-name>
    WHERE <conditions>
) recSet
WHERE ((@startIndex = -1 AND @endIndex = -1) OR (ROWNUM BETWEEN @startIndex AND @endIndex ))
ORDER BY <column-name>

Friday, December 17, 2010

[Java] Error: Method getBindStatus threw exception for reference $springMacroRequestContext

Recently, I came across a problem while working on Spring framework and Velocity; the exception appears to be "ERROR [velocity.app.VelocityEngine] Method getBindStatus threw exception for reference $springMacroRequestContext in template <xyz.vm>". Apparently, it seems like velocity was unable to call the macro or unable to bind some variable in the template.

Actually, I used macro to display tree structure, defined on two different velocity templates, both macro have the same name but different variables were bind with respect to each template requirement. This caused the first visited page to be loaded properly, and on visiting next page, the same macro (from the first page) is called at back end, hence was throwing above mentioned exception. The exception occured because JVM was unable to find the variables used in first template macro, while loading the macro in second template with same macro name.

The solution, is quite simple to this problem. To use different macro name in each template, IF it has any variable or form controls specific to that particualar template.

Another useful information: Changes done in velocity macros do not appear until we restart Tomcat. To have macros changes appear on browser without restarting Tomcat, just change the macro name and refresh the browser.

Enjoy working with Velocity templates. :)