6 Replies Latest reply on Feb 2, 2008 6:08 PM by supernovasoftware.com

    Here is a Richfaces Ajax Datascroler and Seam Example

      I use the following to view large tables where fetching the all the results is not feasible. It has about 1 million rows.

      It does assume that you object implements the interface Idable shown next.

      
      import java.io.IOException;
      import java.io.Serializable;
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      import javax.faces.context.FacesContext;
      
      import org.ajax4jsf.model.DataVisitor;
      import org.ajax4jsf.model.ExtendedDataModel;
      import org.ajax4jsf.model.Range;
      import org.ajax4jsf.model.SequenceRange;
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Logger;
      import org.jboss.seam.annotations.Out;
      import org.jboss.seam.log.Log;
      
      import my.package.Idable;
      
      public abstract class BaseExtendedDataModel<T,ID extends Serializable> extends ExtendedDataModel implements BaseExtendedDataModelDAO<T, ID>{
      
       private @Logger Log log;
      
       @Out(scope=ScopeType.CONVERSATION, required=false)
       public List<T> listRow;
      
       private ID currentId;
       private Map<ID, T> wrappedData = new HashMap<ID, T>();
       private List<ID> wrappedKeys;
       private Long rowCount; // better to buffer row count locally
      
       public abstract Long getCount();
       public abstract List<T> getList(Integer firstRow, Integer maxResults);
       public abstract T findById(ID id);
      
       public void wrap(FacesContext context, DataVisitor visitor, Range range, Object argument, List<T> list) throws IOException
       {
       wrappedKeys = new ArrayList<ID>();
       wrappedData = new HashMap<ID, T>();
       for (T row : list)
       {
       Idable idable = (Idable) row;
       ID id = (ID) idable.getId();
       wrappedKeys.add(id);
       wrappedData.put(id, row);
       visitor.process(context, id, argument);
       }
       }
      
       public boolean hasById(ID id)
       {
       for (T row : listRow)
       {
       Idable idable = (Idable) row;
       Long rowId = (Long) idable.getId();
       if (rowId.equals(id))
       {
       return true;
       }
       }
       return false;
       }
      
       @Override
       public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException
       {
       int firstRow = ((SequenceRange) range).getFirstRow();
       int maxResults = ((SequenceRange) range).getRows();
       wrap(context, visitor, range, argument, getList(firstRow, maxResults));
       }
      
       /*
       * This method normally called by Visitor before request Data Row.
       */
       @Override
       public void setRowKey(Object key)
       {
       this.currentId = (ID) key;
       }
      
       @Override
       public int getRowCount()
       {
       if(rowCount == null)
       return (rowCount = this.getCount()).intValue();
       else
       return rowCount.intValue();
       }
      
       @Override
       public boolean isRowAvailable()
       {
       if (currentId == null) {
       return false;
       } else {
       return hasById(currentId);
       }
       }
      
       /**
       * This is main way to obtain data row. It is intensively used by framework.
       * We strongly recommend use of local cache in that method.
       */
       @Override
       public Object getRowData() {
       if (currentId == null) {
       return null;
       } else {
       T ret = wrappedData.get(currentId);
       if (ret == null) {
       ret = this.findById(currentId);
       wrappedData.put(currentId, ret);
       return ret;
       } else {
       return ret;
       }
       }
       }
      
       /**
       * Unused rudiment from old JSF staff.
       */
       @Override public Object getWrappedData() { throw new UnsupportedOperationException(); }
       @Override public int getRowIndex() { throw new UnsupportedOperationException(); }
       @Override public void setRowIndex(int rowIndex) { throw new UnsupportedOperationException(); }
       @Override public void setWrappedData(Object data) { throw new UnsupportedOperationException(); }
      
       /**
       * Unused update data.
       */
      // @Override public void update() { throw new UnsupportedOperationException(); }
      
       /**
       * TODO if this is never called by the framework why is it necessary.
       */
       @Override public Object getRowKey() { if (true) throw new UnsupportedOperationException(); return currentId; }
      
      }
      
      public interface BaseExtendedDataModelDAO<T, ID> {
      
      }
      
      
      

      Idable interface
      public interface Idable
      {
       public Long getId();
      }
      


      I extend the base class as shown below.



      import java.util.List;
      
      import org.jboss.seam.annotations.In;
      import org.jboss.seam.annotations.Name;
      
      import mypackage.general.access.Access;
      
      @Name("accessExtendedDataModel")
      public class AccessExtendedDataModel extends BaseExtendedDataModel<Access, Long>
      {
       @In(create=true) AccessDAO accessDAO;
      
       @Override
       public Long getCount()
       {
       return accessDAO.getCount();
       }
      
       @Override
       public Access findById(Long id)
       {
       return accessDAO.findById(id);
       }
      
       @Override
       public List<Access> getList(Integer firstRow, Integer maxResults)
       {
       return listRow = accessDAO.getRange(firstRow, maxResults);
       }
      


      Then in the facelets page

       <a4j:form>
       <rich:datascroller for="accessList" />
       <rich:spacer height="10" />
       <rich:dataTable id="accessList"
       value="#{accessExtendedDataModel}"
       var="accessL"
       styleClass="f_table"
       rowClasses="tr0,tr1"
       rows="30">
       <rich:column id="id">
       <f:facet name="header">id</f:facet>
       <h:outputText value="#{accessL.id}" />
       </rich:column>
       <rich:column id="User">
       <f:facet name="header">User</f:facet>
       <h:outputText value="#{accessL.usr.usr}" />
       </rich:column>
       </rich:dataTable>
       </a4j:form>
      


      The scroller is now connected to the datatable. This is a first pass and was adapted for an example in the Richfaces cvs I took a while back.

      The paging is all done with Ajax and only 30 objects are loaded at a time.

      Any suggestions for improvement are most welcome.


        • 1. Re: Here is a Richfaces Ajax Datascroler and Seam Example

          If your object does not implement Idable you can override the methods shown below. These are the only methods that depend on this. I will need to do this in some cases where I am returning a list of maps instead a list of entities.

          public void wrap(FacesContext context, DataVisitor visitor, Range range, Object argument, List<T> list) throws IOException;
          public boolean hasById(ID id);
          


          • 2. Re: Here is a Richfaces Ajax Datascroler and Seam Example
            brachie

            Hi,

            thanks you for the code.. lazy loading with Richfaces datatable is really helpul for me. But I am not using DAO's to access my Entities. I am using the normal EntityManager.createQuery(..) method.

            I would have to make 2 queries one for getCount and one for getList (and a third for getId), correct?

            Regards,

            Alexander

            • 3. Re: Here is a Richfaces Ajax Datascroler and Seam Example

              Yes. You should be able to just override the methods with your custom queries.

              Please let me know how it goes as I want to improve on this, but do not have the time currently to spend.

              If you have problems I will try to help.

              • 4. Re: Here is a Richfaces Ajax Datascroler and Seam Example
                brachie

                @supernovasoftware.com
                Thanks for offering your help :-)

                As far as I understand the code, I would need a separate seam component (representing the DataModel and extending BaseExtendedDataModel) for each query of which result I want to page through the results.

                I will give it a try if I have time and keep you updated.

                Regards,

                Alexander

                • 5. Re: Here is a Richfaces Ajax Datascroler and Seam Example
                  baspet

                  I tried to run your code but i am getting NotSupportedException because setRowIndex is called. Is that expected?. Any idea?

                  Thanks,
                  V.

                  • 6. Re: Here is a Richfaces Ajax Datascroler and Seam Example

                    Here is a more recent version of the code. This time I have the query return a List of maps for an HQL query.

                    I just override get id and it works. Try this more recent version. Are you using Seam 2.0.x?

                    I have not seen too much interest in my method. If people like the technique employed, I can post an example when I get some time.


                    
                    import java.io.IOException;
                    import java.io.Serializable;
                    import java.util.ArrayList;
                    import java.util.HashMap;
                    import java.util.List;
                    import java.util.Map;
                    
                    import javax.faces.context.FacesContext;
                    
                    import org.ajax4jsf.model.DataVisitor;
                    import org.ajax4jsf.model.ExtendedDataModel;
                    import org.ajax4jsf.model.Range;
                    import org.ajax4jsf.model.SequenceRange;
                    import org.jboss.seam.annotations.Logger;
                    import org.jboss.seam.log.Log;
                    
                    import com.xxx.ui.Idable;
                    
                    public abstract class BaseExtendedDataModel<T,ID extends Serializable> extends ExtendedDataModel implements BaseExtendedDataModelDAO<T, ID>{
                    
                     private @Logger Log log;
                    
                     int rowNum=-1;
                    
                     public int getRowNum()
                     {
                     return ++rowNum;
                     }
                    
                     public List<T> listRow;
                    
                     private ID currentId;
                     private Map<ID, T> wrappedData = new HashMap<ID, T>();
                     private List<ID> wrappedKeys;
                     private Long rowCount; // better to buffer row count locally
                    
                     public abstract Long getCount();
                     public abstract List<T> getList(Integer firstRow, Integer maxResults);
                     public abstract T findById(ID id);
                    
                     public ID getId(T row)
                     {
                     Idable idable = (Idable) row;
                     ID id = (ID) idable.getId();
                     return id;
                     }
                    
                     public void wrap(FacesContext context, DataVisitor visitor, Range range, Object argument, List<T> list) throws IOException
                     {
                     wrappedKeys = new ArrayList<ID>();
                     wrappedData = new HashMap<ID, T>();
                     for (T row : list)
                     {
                     ID id = getId(row);
                     wrappedKeys.add(id);
                     wrappedData.put(id, row);
                     visitor.process(context, id, argument);
                     }
                     }
                    
                     public boolean hasById(ID id)
                     {
                     for (T row : listRow)
                     {
                     ID rowId = getId(row);
                     if (rowId.equals(id))
                     {
                     return true;
                     }
                     }
                     return false;
                     }
                    
                     @Override
                     public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException
                     {
                     int firstRow = ((SequenceRange) range).getFirstRow();
                     int maxResults = ((SequenceRange) range).getRows();
                     log.info("("+firstRow +", "+ maxResults+")");
                     wrap(context, visitor, range, argument, getList(firstRow, maxResults));
                     }
                    
                     /*
                     * This method normally called by Visitor before request Data Row.
                     */
                     @Override
                     public void setRowKey(Object key)
                     {
                     this.currentId = (ID) key;
                     }
                    
                     @Override
                     public int getRowCount()
                     {
                     if(rowCount == null)
                     return (rowCount = this.getCount()).intValue();
                     else
                     return rowCount.intValue();
                     }
                    
                     @Override
                     public boolean isRowAvailable()
                     {
                     if (currentId == null) {
                     return false;
                     } else {
                     return hasById(currentId);
                     }
                     }
                    
                     /**
                     * This is main way to obtain data row. It is intensively used by framework.
                     * We strongly recommend use of local cache in that method.
                     */
                     @Override
                     public Object getRowData() {
                     if (currentId == null) {
                     return null;
                     } else {
                     T ret = wrappedData.get(currentId);
                     if (ret == null) {
                     ret = this.findById(currentId);
                     wrappedData.put(currentId, ret);
                     return ret;
                     } else {
                     return ret;
                     }
                     }
                     }
                    
                    
                     // Unused rudiment from old JSF staff.
                     @Override public int getRowIndex() { throw new UnsupportedOperationException(); }
                     @Override public void setRowIndex(int rowIndex) { throw new UnsupportedOperationException(); }
                     @Override public Object getWrappedData() { throw new UnsupportedOperationException(); }
                     @Override public void setWrappedData(Object data) { throw new UnsupportedOperationException(); }
                    
                     // TODO if this is never called by the framework why is it necessary.
                     @Override public Object getRowKey() { throw new UnsupportedOperationException(); }
                    
                    }
                    
                    


                    
                    
                    import java.util.List;
                    import java.util.Map;
                    
                    import org.jboss.seam.ScopeType;
                    import org.jboss.seam.annotations.In;
                    import org.jboss.seam.annotations.Logger;
                    import org.jboss.seam.annotations.Name;
                    import org.jboss.seam.annotations.Out;
                    import org.jboss.seam.annotations.Scope;
                    import org.jboss.seam.log.Log;
                    
                    import com.xxx.dao.richfaces.BaseExtendedDataModel;
                    import com.xxx.search.PipeSearch;
                    
                    @Name("pipeSearchExtendedDataModel")
                    @Scope(ScopeType.CONVERSATION)
                    public class PipeSearchExtendedDataModel extends BaseExtendedDataModel<Map<String, Object>, Long>
                    {
                     private @Logger Log log;
                    
                     @In(create=true) PipeSearchDAO pipeSearchDAO;
                    
                     @In(required=false) @Out(required = false)
                     private PipeSearch pipeSearch;
                     public PipeSearch getPipeSearch() { return pipeSearch; }
                     public void setPipeSearch(PipeSearch pipeSearch) { this.pipeSearch = pipeSearch; }
                    
                     @Override
                     public Long getId(Map<String, Object> row)
                     {
                     return (Long) row.get("minId");
                     }
                    
                     @Override
                     public Long getCount()
                     {
                     return pipeSearchDAO.getCount(pipeSearch);
                     }
                    
                     @Override
                     public Map<String, Object> findById(Long id)
                     {
                     return pipeSearchDAO.findResultById(id);
                     }
                    
                     @Override
                     public List<Map<String, Object>> getList(Integer firstRow, Integer maxResults)
                     {
                     return listRow = pipeSearchDAO.getList(pipeSearch,firstRow, maxResults);
                     }
                    
                    }