`

jee6 学习笔记 8: Handle big data set - without real pagination

    博客分类:
  • JEE
阅读更多
Problem:

There is a problem when dealing with big data set. For instance, if we have millions of records in database to search and display, we cannot just use a @SessionScoped backing bean to put the search result list in memory. This would quickly exhaust server memory if many users are online.

The alternative to @SessionScoped is to use @ViewScoped. It is said that the same @ViewScoped bean would be available as long as user stays on the same view(page). The @ViewScoped backing bean would be garbage collected after user leaves the current page. That's what we want to handle big data set.

However, when testing with JBoss 6.1 and JSF2.1, with Primefaces 3.3.1, annotation @ViewScoped seemed not work as expected: JSF created different instances. This would make Primefaces sorting and paging not working at all. Or put it another way, it did not use the same instance at all for different request. It might be caused by Primefaces, or caused by the JSF2.1 implementation.


Solution:


If you do want to use @SessionScoped backing bean, a workaround is to provide searching filters, ie, enforce user to input searching parameters and thus limit the returned data set in an acceptable size.

If you want to use @ViewScoped backing bean, my experiment shows that you can use the "Flash" scope to store the searching result and pre-load this search result list when initializing the @ViewScoped bean. This way, it solves the big data set problem as well as enables Primefaces sorting and paging to work fine.

The following is the example source code of a @ViewScoped backing bean StudentSearch2:
package com.jxee.action.student;

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

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.inject.Inject;

import org.apache.log4j.Logger;

import com.jxee.ejb.student.StudentDAO;
import com.jxee.model.student.Student;

@ManagedBean(name="ss")
@ViewScoped
public class StudentSearch2 implements Serializable {

  private static final Logger log = Logger.getLogger(StudentSearch2.class);
  private static final String SEARCH_PAGE = "/student/studentSearch.xhtml";
  
  private List<Student> searchResultList;
  private @EJB StudentDAO dao;

  private String nameFilter;
  private int maxRows = 50;
  
  /**
   * property to flag is cached result list needs reload, after add/update/delete etc.
   * set from views like this: <f:setPropertyActionListener value="true" target="#{ss.refresh}" />
   */
  private boolean refresh;
  
  private @Inject Flash flash;  // @Inject seems not working at all :twisted: 
    
  
  @PostConstruct
  public void init() {
    log.debug(this + ": restore from flash...");
    
    // if flash is not injected properly, get it manually
    ExternalContext cntxt = FacesContext.getCurrentInstance().getExternalContext();  
    
    // lets set logged in user into to &quot;flash&quot; scope.   
    // 1. it can be accessed like this: "#{flash.USER-key.username}"; in the JSF2 pages.  
    // 2. or in the backing bean code: User user = (User) flash.get("USER-key");  
    this.flash = cntxt.getFlash();
    
    this.getFromFlash();
  }

  public boolean isRefresh() {
    return refresh;
  }

  public void setRefresh(boolean refresh) {
    log.debug("-- setting refresh: " + refresh);
    this.refresh = refresh;
  }

  public String findByName() {
    log.debug(this.toString() + "-- 21. search student by nameFilter: " + nameFilter + "; refresh=" + this.isRefresh());
    searchResultList = dao.find(this.nameFilter, maxRows);
    log.debug(this.toString() + "-- 22. search found: " + this.searchResultList.size());
    
    this.save2Flash();

    return SEARCH_PAGE;
  }
  
  //put search result in Flash scope for later reference
  private void save2Flash() {
    flash.put("ssList2", this.searchResultList);
    flash.put("sKey", this.nameFilter);
  }
  
  // get search result list from Flash scope
  private void getFromFlash() {
    this.searchResultList = (List<Student>) flash.get("ssList2");
    this.nameFilter = (String) flash.get("sKey");
  }
  
  public String getNameFilter() {
    return nameFilter;
  }

  public void setNameFilter(String afilter) {
    this.nameFilter = afilter;
  }

  public int getMaxRows() {
    return maxRows;
  }

  public void setMaxRows(int maxRows) {
    this.maxRows = maxRows;
  }

  public List<Student> getSearchResultList() {
    if(this.searchResultList == null) {
      this.getFromFlash();
    }
    
    if(this.isRefresh() || this.searchResultList == null) {
      this.findByName();
      this.refresh = false;
    }
    
    return this.searchResultList;
  }

  public void setSearchResultList(List<Student> searchResultList) {
    this.searchResultList = searchResultList;
  }
  
  public int getSize() {
    int size = getSearchResultList().size();
    log.debug("searched result list size: " + size);
    return size;
  }
  
  // use default TimeZone to display time properly
  public TimeZone getTimeZone() {
    TimeZone tz = TimeZone.getDefault();
    return tz;
  }
  
}


Screenshot of the student search with @ViewScoped backing bean and use of "Flash" scope:



Server log shows the @ViewScoped backing bean is not View Scoped at all. For each clicking on sorting/paging of the Primefaces data table, it generated a new instance, gosh!

To enable Primefaces data table working with a @ViewScoped backing bean, the "Flash" scope was used to store the search result list, which was pre-loaded when initializing the @ViewScoped bean.
  • 大小: 70.8 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics