The Performance Evil that is JSF dataTable

In JSF 1.x, dataTable was bizarrely inadequate and unnecessarily complicated. Things have become much easier in JSF 2.0 and it's a pleasure to work with. But, don't let that lull you into a false sense of security. There are major performance issues larking behind.

Let's say that you have a managed bean that returns a list of products from the database by using the ProductDAO class.


@ManagedBean
public class Controller {
    private Product item = new Product();

    public List<Product> getProducts() {
        return ProductDAO.getProducts();
    }

    public String edit(Product toEdit) {
        this.item = toEdit;

        return "edit_product";
    }

    //getItem/setItem...
}

Now, we can build a view that shows the product list.


<h:dataTable id="products" value="#{controller.products}" var="current">
    <h:column>
        <f:facet name="header">Product Name</f:facet>
         #{current.name}
        <h:commandButton value="Edit" action="#{controller.edit(current)}" />
    </h:column>
</h:dataTable>


When the view is rendered, it will look something like this.

image

Now, let's reveal the performance horror that the dataTable is. During the rendering phase, getProducts() is called repeatedly, once for each item in the list. That means, you are loading the entire list repeatedly from the database. This seems prodigiously wasteful. I don't see why JSF couldn't get the list once and simply iterate through it to render the table.

Next, when you click the Edit button, things get even worse. The getProducts() method is called 2xN number of times where N is the number of items in the list. To render the edit form, all you really need from the database is the item that is being edited. Under this circumstances, dataTable is doing a terrible job.

OK, so, what can we do about this. First, you need to cache the list in a member variable and access the database only if we haven't already done so.


@ManagedBean
public class Controller {
    private Product item = new Product();
    private List<Product> productList = null;

    public List<Product> getProducts() {
        if (productList == null) {
            productList = ProductDAO.getProducts();
        }
        return productList;
    }

    public String edit(Product toEdit) {
        this.item = toEdit;

        return "edit_product";
    }

    //getItem/setItem...
}

This will cure most of the ills. But, there is still the issue of unnecessarily loading the entire list just to render the edit form. The solution for that is to use a GET request. Get rid of the edit() method and instead add the loadProduct() method that will load the product being edited.


@ManagedBean
public class Controller {
    private Product item = new Product();
    private List<Product> productList = null;

    public List<Product> getProducts() {
        if (productList == null) {
            productList = ProductDAO.getProducts();
        }
        return productList;
    }

    public void loadProduct() {
        int productId = item.getId();
        item = ProductDAO.getProductById(productId);
    }

    //getItem/setItem...
}

In the edit form page, say, edit_product.xhtml, setup GET request by adding this metadata.


<f:metadata>
    <f:viewParam name="productId" value="#{controller.item.id}"/>
    <f:event listener="#{controller.loadProduct}" type="preRenderView"/>
</f:metadata>

Finally, change the dataTable to use <h:link> instead of a <h:commandButton>.


<h:dataTable id="products" value="#{controller.products}" var="current">
	<h:column>
		<f:facet name="header">Name</f:facet>
		#{current.name}
		<h:link outcome="edit_product" value="Edit">
			<f:param name="productId" value="#{current.id}"/>
		</h:link>
	</h:column>
</h:dataTable>

That should do it. If you are still hatin' dataTable, then go with <ui:repeat>.

Bibhas Bhattacharya August 15, 2012

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Web age solutions blog Zones