Friday, October 10, 2008

Effective datatable paging and sorting

Effective datatable paging and sorting at DAO level

In the 2 year old article Using datatables I wrote how to sort and page a JSF h:dataTable. Those are useful and nice if the dataset is small. But it is in fact less efficient as it first loads the whole data list from the database and uses Java to do the sorting and paging. It is much more efficient if you let the database do all the task. A self-respected database can sort the results much faster than Java can do. Querying a sublist from the database consumes much less memory in Java than when you query the complete list from the database. This all will make it much faster when you have a database with at least hundreds of rows.

Sorting using SQL can easily be done with the standardized ORDER BY clause. The way to obtain a subset of results differs per database. This article is targeted on MySQL. In MySQL you can obtain a subset of results with the LIMIT x, y clause. PostgreSQL uses LIMIT x OFFSET y. In Oracle you need to execute a ORDER BY subquery first and then use the ROWNUM clause on its results (SELECT * FROM (SELECT * FROM table ORDER BY column) WHERE ROWNUM BETWEEN x AND y). For MSSQL and DB2 you'll need to write a (w)hacky SQL query or to create a stored procedure. Consult Google or database specific documentation for details.

Back to top

Preparations

Next to a standard JSF implementation, we need the Tomahawk component library as it offers us the t:dataList and t:saveState components. The t:dataList is needed to display a collection of links with page numbers. It is preferred above JSTL's c:forEach, because it does its work more efficient. The t:saveState is needed to cache the displayed list and some important paging and sorting variables for the next request. It is preferred above h:inputHidden, because it does its work more efficient and it doesn't require a converter for non-standard object types. You can even cache a complete bean for the subsequent request, with which you can simulate a "conversation scope".

Integrating Tomahawk isn't that hard, you can even do that on a Sun Mojarra environment. You just need to add at least the following JAR's to the classpath, e.g. /WEB-INF/lib. The version numbers doesn't matter that much, as long as you get the newest.

The Tomahawk JAR is the Tomahawk component library itself which under each contains the t:dataList and t:saveState components. The commons JAR's are required by other components and/or the core of the Tomahawk component library.

Back to top

Backing Bean

Here is how the basic backing bean code look like. It is request scoped. If you're interested, an example of the DAOFactory can be found here: DAO tutorial - the data layer.

package mypackage;

import java.io.Serializable;
import java.util.List;

import javax.faces.component.UICommand;
import javax.faces.event.ActionEvent;

import mydao.DAOException;
import mydao.DAOFactory;
import mydao.MyDataDAO;
import mymodel.MyData;

/**
 * The example backing bean for effective datatable paging and sorting.
 *
 * @author BalusC
 * @link http://balusc.blogspot.com/2008/10/effective-datatable-paging-and-sorting.html
 */
public class MyBean implements Serializable {

    // Properties ---------------------------------------------------------------------------------

    // DAO.
    private static MyDataDAO dao = DAOFactory.getInstance("javabase").getMyDataDAO();

    // Data.
    private List<MyData> dataList;
    private int totalRows;

    // Paging.
    private int firstRow;
    private int rowsPerPage;
    private int totalPages;
    private int pageRange;
    private Integer[] pages;
    private int currentPage;

    // Sorting.
    private String sortField;
    private boolean sortAscending;

    // Constructors -------------------------------------------------------------------------------

    public MyBean() {
        // Set default values somehow (properties files?).
        rowsPerPage = 10; // Default rows per page (max amount of rows to be displayed at once).
        pageRange = 10; // Default page range (max amount of page links to be displayed at once).
        sortField = "id"; // Default sort field.
        sortAscending = true; // Default sort direction.
    }

    // Paging actions -----------------------------------------------------------------------------

    public void pageFirst() {
        page(0);
    }

    public void pageNext() {
        page(firstRow + rowsPerPage);
    }

    public void pagePrevious() {
        page(firstRow - rowsPerPage);
    }

    public void pageLast() {
        page(totalRows - ((totalRows % rowsPerPage != 0) ? totalRows % rowsPerPage : rowsPerPage));
    }

    public void page(ActionEvent event) {
        page(((Integer) ((UICommand) event.getComponent()).getValue() - 1) * rowsPerPage);
    }

    private void page(int firstRow) {
        this.firstRow = firstRow;
        loadDataList(); // Load requested page.
    }

    // Sorting actions ----------------------------------------------------------------------------

    public void sort(ActionEvent event) {
        String sortFieldAttribute = (String) event.getComponent().getAttributes().get("sortField");

        // If the same field is sorted, then reverse order, else sort the new field ascending.
        if (sortField.equals(sortFieldAttribute)) {
            sortAscending = !sortAscending;
        } else {
            sortField = sortFieldAttribute;
            sortAscending = true;
        }

        pageFirst(); // Go to first page and load requested page.
    }

    // Loaders ------------------------------------------------------------------------------------

    private void loadDataList() {

        // Load list and totalCount.
        try {
            dataList = dao.list(firstRow, rowsPerPage, sortField, sortAscending);
            totalRows = dao.count();
        } catch (DAOException e) {
            throw new RuntimeException(e); // Handle it yourself.
        }

        // Set currentPage, totalPages and pages.
        currentPage = (totalRows / rowsPerPage) - ((totalRows - firstRow) / rowsPerPage) + 1;
        totalPages = (totalRows / rowsPerPage) + ((totalRows % rowsPerPage != 0) ? 1 : 0);
        int pagesLength = Math.min(pageRange, totalPages);
        pages = new Integer[pagesLength];

        // firstPage must be greater than 0 and lesser than totalPages-pageLength.
        int firstPage = Math.min(Math.max(0, currentPage - (pageRange / 2)), totalPages - pagesLength);

        // Create pages (page numbers for page links).
        for (int i = 0; i < pagesLength; i++) {
            pages[i] = ++firstPage;
        }
    }

    // Getters ------------------------------------------------------------------------------------

    public List<MyData> getDataList() {
        if (dataList == null) {
            loadDataList(); // Preload page for the 1st view.
        }
        return dataList;
    }

    public int getTotalRows() {
        return totalRows;
    }

    public int getFirstRow() {
        return firstRow;
    }

    public int getRowsPerPage() {
        return rowsPerPage;
    }

    public Integer[] getPages() {
        return pages;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public int getTotalPages() {
        return totalPages;
    }

    // Setters ------------------------------------------------------------------------------------

    public void setRowsPerPage(int rowsPerPage) {
        this.rowsPerPage = rowsPerPage;
    }

}

Define it as usual in the faces-config.xml:


    <managed-bean>
        <managed-bean-name>myBean</managed-bean-name>
        <managed-bean-class>mypackage.MyBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>

Back to top

Example DTO

Here is the basic DTO example. It's nothing special. It's just a dummy DTO with three fields: ID, Name and Value.

package mymodel;

import java.io.Serializable;

/**
 * MyData. The example DTO (Data Transfer Object).
 *
 * @author BalusC
 * @link http://balusc.blogspot.com/2008/10/effective-datatable-paging-and-sorting.html
 */
public class MyData implements Serializable {

    // Properties ---------------------------------------------------------------------------------

    private Long id;
    private String name;
    private Integer value;

    // Constructors -------------------------------------------------------------------------------

    public MyData() {
        // Keep default constructor alive.
    }

    public MyData(Long id, String name, Integer value) {
        this.id = id;
        this.name = name;
        this.value = value;
    }

    // Getters ------------------------------------------------------------------------------------

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Integer getValue() {
        return value;
    }

    // Setters ------------------------------------------------------------------------------------

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

}
Back to top

Example DAO

The basic DAO example. Note that you cannot set the ORDER BY field and direction as PreparedStatement value. That's why it uses the String#format() for it. Keep SQL injection risks in mind. As long as the client can't control the values, you don't need to be afraid.

For more information and examples of the DAO layer and the DAOUtil class, you may find this article useful: DAO tutorial - the data layer.

package mydao;

import static mydao.DAOUtil.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import mymodel.MyData;

/**
 * This class represents a SQL Database Access Object for the {@link MyData} DTO.
 *
 * @author BalusC
 * @link http://balusc.blogspot.com/2008/10/effective-datatable-paging-and-sorting.html
 */
public final class MyDataDAO {

    // Constants ----------------------------------------------------------------------------------

    private static final String SQL_LIST_BY_ORDER_AND_LIMIT =
        "SELECT id, name, value FROM mydata ORDER BY %s %s LIMIT ?, ?";
    private static final String SQL_COUNT =
        "SELECT count(*) FROM mydata";

    // Properties ---------------------------------------------------------------------------------

    private DAOFactory daoFactory;

    // Constructors -------------------------------------------------------------------------------

    /**
     * Construct MyData DAO for the given DAOFactory. Package private so that it can be constructed
     * inside the DAO package only.
     * @param daoFactory The DAOFactory to construct this MyData DAO for.
     */
    MyDataDAO(DAOFactory daoFactory) {
        this.daoFactory = daoFactory;
    }

    // Actions ------------------------------------------------------------------------------------

    /**
     * Returns list of MyData items starting at the given first index with the given row count,
     * sorted by the given sort field and sort order.
     * @param firstRow First index of rows to be returned.
     * @param rowCount Amount of rows to be returned.
     * @param sortField Field to sort the data on.
     * @param sortAscending Whether to sort data ascending or not.
     * @return list of MyData items starting at the given first index with the given row count,
     * sorted by the given sort field and sort order.
     * @throws DAOException If something fails at DAO level.
     */
    public List<MyData> list(int firstRow, int rowCount, String sortField, boolean sortAscending)
        throws DAOException
    {
        Object[] values = { firstRow, rowCount };

        String sortDirection = sortAscending ? "ASC" : "DESC";
        String sql = String.format(SQL_LIST_BY_ORDER_AND_LIMIT, sortField, sortDirection);
        List<MyData> dataList = new ArrayList<>();

        try (
            Connection connection = daoFactory.getConnection();
            PreparedStatement statement = prepareStatement(connection, sql, false, values);
            ResultSet resultSet = statement.executeQuery();
        ) {
            while (resultSet.next()) {
                dataList.add(mapMyData(resultSet));
            }
        } catch (SQLException e) {
            throw new DAOException(e);
        }

        return dataList;
    }

    /**
     * Returns total amount of rows in table.
     * @return Total amount of rows in table.
     * @throws DAOException If something fails at DAO level.
     */
    public int count() throws DAOException {
        int count = 0;

        try (
            Connection connection = daoFactory.getConnection();
            PreparedStatement statement = connection.prepareStatement(SQL_COUNT);
            ResultSet resultSet = statement.executeQuery();
        ) {
            if (resultSet.next()) {
                count = resultSet.getInt(1);
            }
        } catch (SQLException e) {
            throw new DAOException(e);
        }

        return count;
    }

    /**
     * Map the current row of the given ResultSet to MyData.
     * @param resultSet The ResultSet of which the current row is to be mapped to MyData.
     * @return The mapped MyData from the current row of the given ResultSet.
     * @throws SQLException If something fails at database level.
     */
    private static MyData mapMyData(ResultSet resultSet) throws SQLException {
        return new MyData(
            resultSet.getLong("id"),
            resultSet.getString("name"),
            resultSet.getObject("value") != null ? resultSet.getInt("value") : null
        );
    }

}
Back to top

JSF file

And now the JSF file, it has a sortable datatable, a bunch of paging buttons (first, previous, next and last), the status of current page and total pages, a bunch of links pointing to a specific page and finally a input field where you can specify the amount of rows to be displayed at once.

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<f:view>
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <title>Effective datatable paging and sorting at DAO level</title>
        </head>
        <body>
            <h:form id="form">

                <%-- The sortable datatable --%>
                <h:dataTable value="#{myBean.dataList}" var="item">
                    <h:column>
                        <f:facet name="header">
                            <h:commandLink value="ID" actionListener="#{myBean.sort}">
                                <f:attribute name="sortField" value="id" />
                            </h:commandLink>
                        </f:facet>
                        <h:outputText value="#{item.id}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:commandLink value="Name" actionListener="#{myBean.sort}">
                                <f:attribute name="sortField" value="name" />
                            </h:commandLink>
                        </f:facet>
                        <h:outputText value="#{item.name}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:commandLink value="Value" actionListener="#{myBean.sort}">
                                <f:attribute name="sortField" value="value" />
                            </h:commandLink>
                        </f:facet>
                        <h:outputText value="#{item.value}" />
                    </h:column>
                </h:dataTable>

                <%-- The paging buttons --%>
                <h:commandButton value="first" action="#{myBean.pageFirst}"
                    disabled="#{myBean.firstRow == 0}" />
                <h:commandButton value="prev" action="#{myBean.pagePrevious}"
                    disabled="#{myBean.firstRow == 0}" />
                <h:commandButton value="next" action="#{myBean.pageNext}"
                    disabled="#{myBean.firstRow + myBean.rowsPerPage >= myBean.totalRows}" />
                <h:commandButton value="last" action="#{myBean.pageLast}"
                    disabled="#{myBean.firstRow + myBean.rowsPerPage >= myBean.totalRows}" />
                <h:outputText value="Page #{myBean.currentPage} / #{myBean.totalPages}" />
                <br />

                <%-- The paging links --%>
                <t:dataList value="#{myBean.pages}" var="page">
                    <h:commandLink value="#{page}" actionListener="#{myBean.page}"
                        rendered="#{page != myBean.currentPage}" />
                    <h:outputText value="<b>#{page}</b>" escape="false"
                        rendered="#{page == myBean.currentPage}" />
                </t:dataList>
                <br />

                <%-- Set rows per page --%>
                <h:outputLabel for="rowsPerPage" value="Rows per page" />
                <h:inputText id="rowsPerPage" value="#{myBean.rowsPerPage}" size="3" maxlength="3" />
                <h:commandButton value="Set" action="#{myBean.pageFirst}" />
                <h:message for="rowsPerPage" errorStyle="color: red;" />

                <%-- Cache bean with data list, paging and sorting variables for next request --%>
                <t:saveState value="#{myBean}" />
            </h:form>
        </body>
    </html>
</f:view>

Save it as paging.jsp or so and invoke it by http://localhost:8080/playground/paging.jsf, assuming that your development server runs at port 8080 and the playground environment's context root is called 'playground'.

That's all!

Back to top

Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair.

(C) October 2008, BalusC

63 comments:

NicoG said...

Hello BalusC,

Fan of your blog, it gave me the idea to create mine.
http://nicogblog.blogsopt.com

Without your permission??, I created I link to yours. If this is an issue, please do not hesitate to let me know
nicolasgaudin@yahoo.fr


May I ask you a question: How do you format your source code snippets this way?
Would you mind give me the tricks (installation in blogspot, maybe using a message model????) to do the same thing and use it in my blog?

Looking forward to hearing from you
Nicog

BalusC said...

I use a slight modified java2html tool.

NicoG said...

Thank you for the tip
Regards

cham said...

Hi BalusC,

Your blog have been really helpful.

I am new to JSF. I am trying to use tomahawk t:treeCheckbox tag. Can you give or point to a detailed example / steps how to use t:treeCheckbox component and retrieve the selected nodes

your help will be really appreciated.

Thanks,
Cham

Kim said...

Hello Balusc,
nice blog and great articles about JSF.
I've been reading your articles about DAO and this 'effective data...' articlt.

But there is one thing i don't understand:
in your backing bean you've the following line:
private static MyDataDAO dao = DAOFactory.getInstance("javabase").getMyDataDAO();

getInstance returns a DAOFactory on which you call the method getMyDataDao.

But this method doesn't exist in your implementation. Have i missd something?

best regards and thanks for the articles.

Kim Gabrielsen

BalusC said...

Just create one yourself :)

Kim said...

Hi Balusc,
i'm back.

I've implemend your DAO and MyData and MyBean AS IS. I haven't change anything.

When I run it i receive an exception like this:

java.io.NotSerializableException: mymodel.MyData
java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
java.util.ArrayList.writeObject(ArrayList.java:570)
sun.reflect.GeneratedMethodAccessor59.invoke(Unknown Source)

i happens no matter what app server i'm using. I've tested with tomcat and glassfish. i'm using netbeans 6.5 rc2.

When i change MyData to:

public class MyData implements Serializable

it works. and i can event sort and page my way through the data.

But why do the bean have to implement Serializeable?

When you wrote and tested this code how come you didn't experience this behaviour?

I wonder if it might interfere with Hibernate when i start using this to persist my data.

best regards

kim

BalusC said...

@kim: didn't occurred to me. Maybe a side effect of Tomahawk t:saveState in combination with certain appserver and EL versions.

Implementing serializable is okay. I will change the MyData example in this article accordingly. Thank you for feedback.

Kim said...

Hi BalusC,
i've had time to play with your sample and it sems very nice.

I have a question though. When i adapt this source to a table with more than 1000 rows, the page links looks awful - that is, the number of them seems overwhelming.

i've tried to fiddle with the datatable from ice faces and the paginator. it works really nice too, but it requires - as far as i'm concerned - that all rows be fetched at once.

do you know if it possible to emulate the paginator somehow? I'd really hate to fetch all rows at once but it's not a solution either to have 150 page links at the bottom of the page.

do i have to write my own component to get this behavior?

kim

BalusC said...

Change the loadDataList() so that the pages only contains a limited number of pages. For example the first five and the last five pages?

chamika deshan said...

Hi BALUSC
I'm use this space to thank you. Basically we all know that your great contribution at JSF world and Sun JSF forum. You have just passed ur 20,000 post (Congratulation!). What a contribution! We gained and will be gain so many things from you.
Finally...
THANK YOU VERY MUCH!

Roshan said...

Hi Balus'C!!!
your blog is really fantastic.It gave me total idea regarding the jsf behaviour.Thanks alot for sharing the knowledge.

alenoor said...

Hi Balusc ,

This blog & the source code helped me a lot , Thank you so much.

Can you give me an idea how to implement the same thing in Icefaces ? In Icefaces , how to cache the paging variables .

Waiting for your reply !!!

Alenoor

Abhishek said...

Hi Balusc ,
I am new to your blog.

Could you please help me how to maintain a focus on any component on page, after having a server trip.

For example, on a dropdown I applied a value change listner and submitted the form to reload again on the same page.
Now I want the focus to retain.

Please help.

Regards

Senthi said...

Excellent ... your example has made my work easier ... but, i have to do it like google search ... can u help me on this ... thanks

ioana said...

What about rich:datascroller ?

BalusC said...

rich:datascroller needs the complete datamodel being loaded into memory and only displays a part of it. Not very efficient if the rowcount exceeds 1000 or so.

BalusC said...

For the case anyone is interested: a "Google-like" paging is added; showing a fixed number of page links at once so that you don't get 1000 links when you've 10000 rows with 10 rows per page ;)

Check the new 'pageRange' property of the bean.

Kiran0927 said...

I am using Richfaces in my JSF and
I have a datatable in my jsf page and also have a submit button on the same page whcih is out side of the datatable.When i submit the button,i need to get a atleast one value from the data table to managed bean.How should i do that.Please help me out

THanks
JK

BalusC said...

JSF should already have set the values in the datamodel. Check the 'Using datatables' article. Please keep comments on-topic, this has nothing to do with paging/sorting.

derbysta said...

Hi BalusC,

I am using a richfaces datatable, however since I need to display 100 columns even though the result set doesn't not containg many rows (<1000) whenever I sort a column or I move from one page to another using the datascroller component the performance are really slow.

I am new to JSF and I am still confuded what the best approach would be to improve the performance in displaying the data. I would need the possibility to sort for any of the column and also to perform the filterning on some of them.

What do you advice?

Thanks in advance

Kymair said...

Hi, BalusC

I've implemented the paging as the article demonstrates, however it doesn't work, the paging action doesn't get called at all! (Per debug result).
I found out that the cause is form's property
enctype="multipart/form-data". It is used to support a t:inputFileUpload within the same form. Moreover sometimes it works under Google Chrome, but fails always under IE8.

Do I have to separate the upload tag to another form? But as mentioned above sometimes it does work under Chrome..

Please kindly advise, thanks very much.

BalusC said...

Did you configure the ExtensionsFilter properly? The t:inputFileUpload requires the ExtensionsFilter being configured so that it can process the multipart/form-data request.

Massimo said...

Thanks BalusC,
it's exactly what I was looking for, but I'm still not able to let it work. The myBean seems not saved and everytime I move to the next page, the number of rows in the page is set to the default value. Any hints?
Thanks

/Max

Massimo said...

I have to correct myself,
I've put a trace in the pageNext function, my DB query returns a list of 8 rows while the default rows per page is 10. Then I change the number of rows per page to 3 and press set.
The new page shows again 8 rows, but the input textfield contains 3!
I commented out the check on dataList in getDataList. If I do that, after pressing set the number of rows displayed is 3 this time instead of 8, but when I press next nothing happen. I've put a trace in the function pageNext but it seems that it is never called. I wonder why. I seems to me a too strange behaviour.
Additional info:
- I had to remove the in your original jsf code because I encapsulated the dataTable in a h:pageGrid
Please help

Massimo said...

... and if the I set the bean session scope instead of request it works. But it is not the acceptable behaviour...
/Max

BalusC said...

Did you use t:saveState?

Massimo said...

Yes, I did!

Ramesh "Defeat the Defeats;Defeat defeats you" said...

Hi balusC,

I need radio buttons inside
the datatable(JBoss & RichFaces).
How to create rows inside the dataTable? Post me as soon as possible....i ve to complete by tomo..

thanks in advance...

adi said...

Hello
Do you know how could I have a sortable DataTable(sorting done by "t:commandSortHeader" AND the table shoul have a scrollbar also. The difficult thing( at least for me) is to scroll only the content, but not the header.
Sorry for my lausy english :)

asertoglu said...

Hi BalusC,

I am using rich:dataTable for inserting&updating multiple rows into database. My users want to navigating between dataTable rows with arrow keys(up/down/left/right) and return key.

My dataTable rows has 3 columns in which has h:inputText component. After User writes the value in h:inputText, with arrow keys or return key wants to go to the next row's h:inputText. But i couldn't succeed this action.

How can i do this ?

Thanks a lot.

Ramesh "Defeat the Defeats;Defeat defeats you" said...

Hello BalusC,

In this given example pagination Next,Last,First,Previous are not working.....I ve done as per the code but it's not working....commandButton method bindings are not calling from JSP page....
Please help me....
Thanks in advance
Ramesh

shanthi said...

Hi,

i am using this code for lazy loading in seam famework.but i am getting problem.first and next are not working .It is enabled.I can able to see the total pages and all.but not able to go next page.sometimes i can able to go.Please help me.whether is works in seam

RickJ said...

Hi Balusc,

I have been enjoying your blog for awhile. Currently the company I work for uses IBM Rational Application Developer with IBM version of JSF. I would like to know if I could use your examples using tomahawk extentions with ibmjsf. I would like to sort my datatables the way your example explains. Again thanks always for you insight!

BalusC said...

You can use Tomahawk on any JSF implementation. It's just a component library. The ibmjsf is by the way also just a component library like Tomahawk itself is. RAD/WSAD ships by default with Sun JSF RI (Mojarra).

Max Cavalera said...

Hello BalusC,

I like your blogpost, very clear and helpful. I'm currently also busy with these datatables. However I want to show combined entity data, example.

Entity: Employee
Fields: REF, NAME, SALARY

Entity: Department
Fields: REF, NAME, DESCRIPTION, BUDGET

Now in my datatable I would like to show 'Employee.NAME, Employee.SALARY, Department.NAME, Department.Budget'.

Ofcourse I could create a DTO with these fields, but the fact is I have like 100+ of these views with different number of columns. So actually I'm looking for a more generic datatable which would save me the work of creating 100+ JSF pages with all configuration etc.

Do you have any suggestions on how to solve this?

Hugo said...

Hey there BalusC,

After following your tutorial I encountered a problem I can't really figure out.

When I change my "rows per page" and press the button "set" my page simply refreshes back to default values, meaning, the total number of rows are always 10 like initialized.
I suspect this is due to the saveState tag which is not 'persisting' my bean so when getDataList gets invoked the dataList and all the other paging parameters are null.

By the way, my bean as well as my DAO are both Serializable.

Do you have any thoughts on this?

Senorita said...

Gr8 blog. I was searcing this for adding in my code. thanks alot

krishna pokala said...

Hi Balus,
really like the way you present.can we have a defaultsort column in datatable?I mean a table to load with a certain column sorted at first.
kpokala@gmail.com

BalusC said...

Yes certainly. Check the following line in the bean's code:

sortField = "id"; // Default sort field.

krishna pokala said...

Thanks Balus for the quick response.we are actually using Command sortheader which doesnt allow this.

briefly my code


and the column is




and I defined the sortCoulm in my Bean as Description.

mustak said...

Hi BalusC,

Thank you very much for posting article on DAO and paging. Can you please help me in Struts pagination?

lpoulo said...
This comment has been removed by the author.
lpoulo said...

Hi BalusC,

hey man thanks for posting this! have you or someone else perhaps done the same thing using Amazon SimpleDB?

I have been struggling quite a bit with pagination using Java servlets in SimpleDB.

Any help would be appreciated from anyone!

cheers,
Lebeko

colingaj said...

Hello,

I tried using your code but when clicking on the sort column the constructor for the backing bean gets invoked again and all member variables get re-initialized.

Any ideas? Is this because it is a request-scoped? I hesitate to believe this is the case otherwise lots of items won't work correctly.

Thanks
Colin

BalusC said...

Likely you missed <t:saveState value="#{myBean}" />.

colingaj said...

Thanks for your quick response. Tried that and it still doesn't work. Setup is JSF 1.1 and IBM WAS 6.1 (no choice in this).

Continuing to debug, but appreciate any ideas you may have ...

BalusC said...

@Jan: it's because there's another constructor. This would make the default implicit constructor to disappear which in turn would cause to violating the javabean specs and an uninstantiablte class whenever used as managed bean, ORM entity, etc.

Jan said...
This comment has been removed by the author.
Brad Revolver said...

Hello BalusC,

As far as I see, we need to connect to the database on every pagination click. Since it is a request scoped bean, the dataList will be null in every request.

Is there anyway to do this with only "one" database connection without session scope in jsf?

Kind regards,

BalusC said...

No, it won't be thanks to t:saveState. It will only be connected to obtain the next page.

Sanjay said...

Hi BalusC, I want to use filtering on your given example. Could you please give an exmaple of filtering?
Thanks.

Sanjay said...

Hi BalusC, How to use filtering on datatable on your "Effective datatable paging and sorting" example given above?

Please give example.

Thanks

sarode krishna said...

Thanks lord of JSF.....i just gone trough your blog and have mined lots of information....really your blog is pure mines of gold...Bravo

megan said...

Hi there,

I was wondering what happens with this is a row is deleted or added during the paging process of this? Say if one was deleted the count could mean that an exsisting row is never dispalyed, could it not?

John said...

Thanks for the article. really informative and clear code.

My question is what if the Bean had a Session Scope instead of Request scope? what changes will have to be made to make your code with with a session scope bean? Thanks

sorna said...

Great thoughts you got there, believe I may possibly try just some of it throughout my daily life.


ASC Coding

casanova said...

Hi,

When i use a rich:dataTable with selectOneRadio and when i sort my data table with an row select, the selectOneRadio is checked for a wrong row.

There is any way to correct this ?

Life has a twist said...

Hi Balus,

I am trying to implement the below example:

http://www.primefaces.org/showcase/ui/datatablePagination.jsf

i can display the contents abut i dont see the navigation happening which is part of pagination. Please help .THanks

Poornima

Udun said...

Hi BalusC :)

Is Tomahawk still needed ? Are all those jars you refer to still needed ?

JSF 2.2 on glassfish 4

Asif Arshad said...

Hi BalusC,

I've a situation where I've multiple columnGroups inside my datatable and for each record I render one or more columnGroup depending on some business logic.

Now, since the number of rows in the datatable now are different from size of my records list, how do I get total number of pages in the datatable. I need that number for my custom datascroller.

Yi SHU said...

Hello,

I ran the sample except for using session scoped bean instead of request scope.
However the paging links 2, 3, 4, etc no longer worked. For example whenever I clicked on the 4st page I got finally the 1st page.

Did anything go wrong?

efrain said...

Hi BalusC, very nice your post, but in your code you call a static method with name: prepareStatement that it don't find, please I need to see what you did in there, thanks in advance.