Rian Houston wrote:

Hi Werner,
Sorry I can't help with your problem. But I would be very interested in hearing more about how you implemented your custom data model and how you use it with the dataTable component. I need to do a similar thing and am struggling with it...

Well what I intented to do, was to make a data model which feeds directly hibernate objects into the table, and having a query object passed down the data model which handles the query part.


The model itself should have some kind of caching window to speedup traversal and to reduce open connections.
The datasets should be displayed outside of the connection scope to avoid the problem the current JDBC data model has within the faces definition (not caring about the update problem yet...)


What I basically did was to derive a class from the JSF Data model class and add the needed functionality.
Attached you can see my code, use it as you wish (I would be happy if it would be integrated in one way or the other in myfaces)


/*
 * Created on 05.01.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package jsf.model;

import framework.cache.ICacheObject;


import misc.JSFUtil;
import misc.NavigationBean;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import javax.faces.model.DataModel;

import jsf.common.Constants;


/**
 * @author Werner Punz MediaData
 * A simple data model which marries
 * hibernate with JSF
 */
public class HibernateDataModel extends DataModel implements ICacheObject {
    //define a cachingWindow to keep the number of
    //connections/accessno at a sane level
    //from time to time an isolation level error
    //can occur since we seal the objects off
    //and then close the connection
    //so no real data integrity can be performed that way
    //but it should be good enough for the average webapp
    int _cacheWindowSize = 500;

    //array list due to the fact that this one has the fastes
    //absolute access which is necessary for paging
    List _cacheWindow = new ArrayList(_cacheWindowSize);

    //the cache window Position relative to the 0 absolute
    int _cacheWindowPos       = 0;
    boolean _cacheInvalidated = false;

    //the standard variables which are defined by the system
    int _rowCount = -1;
    int _rowIndex = -1;

    //the associated query accessor
    IDataAccessor _queryDelegate = null;

    /**
     *
     */
    public HibernateDataModel(IDataAccessor queryDelegate) {
        super();

        setQueryDelegate(queryDelegate);

        refillCache();
    }

    public HibernateDataModel() {
        super();
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#getRowCount()
     *
     * Return the number of rows of data objects
     * represented by this DataModel. If the number
     * of rows is unknown, or no wrappedData is available, return -1.
     *
     */
    public int getRowCount() {
        if (_rowCount == 0) {
            return -1;
        }

        return _rowCount;
    }

    /**
     * setter accessed by the query object
     * @param rowCount
     */
    public void setRowCount(int rowCount) {
        _rowCount = rowCount;
    }

    //returns true if the dataset is within 
    boolean isCached() {
        return ((_cacheWindowPos <= getRowIndex()) &&
        (getRowIndex() < (_cacheWindowPos + _cacheWindow.size())));
    }

    /**
     * refills the cache window upon the given
     * row index
     * this one in the long term will possible be called from outside also
     */
    public void refillCache() {
        //calculate a decent new cache window pos, if possible with the current row index
        //in the middle
        _cacheWindowPos = Math.max(0, getRowIndex() - (_cacheWindowSize / 2));

        //refill the cache with the query 
        if (_cacheWindowSize > 0) {
            _cacheWindow = _queryDelegate.queryDatasets(_cacheWindowPos,
                    _cacheWindowSize);

            //store the current displayed datasets in the navigatinal bean
            //to allow compound ops
            NavigationBean navBean = (NavigationBean) JSFUtil.getManagedBean(Constants.MBEAN_NAVBEAN);
            navBean.setCurrentNavSelection(_cacheWindow);
        }

        _cacheInvalidated = false;
    }

    /*
     *  (non-Javadoc)
     * @see jsf.common.ICacheObject#invalidateCache()
     */
    public void invalidateCache() {
        _cacheInvalidated = true;
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#getRowData()
     *
     * Return an object representing the data for the
     * currenty selected row index. If no wrappedData
     * is available, return null.
     */
    public Object getRowData() {
        //synchronize this just in case we implemnt
        //an internal cache dumping mechanism
        //for hit fault reduction
        synchronized (_cacheWindow) {
            //fetch a cached object
            if ((!_cacheInvalidated) && isCached()) {
                return getObjectFromCache();
            }

            //reload the cache and get the object
            refillCache();

            return getObjectFromCache();
        }
    }

    /**
     * @return
     */
    private Object getObjectFromCache() {
        return _cacheWindow.get(Math.max(0, getRowIndex() - _cacheWindowPos));
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#getRowIndex()
     *
     * Return the zero-relative index of the currently
     * selected row. If we are not currently positioned
     * on a row, or no wrappedData  is available, return -1.
     */
    public int getRowIndex() {
        return _rowIndex;
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#getWrappedData()
     * Return the object representing the data wrapped by
     * this DataModel, if any.
     */
    public Object getWrappedData() {
        // TODO Auto-generated method stub
        return null;
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#isRowAvailable()
     *
     * Return a flag indicating whether there is rowData
     * available at the current rowIndex. If no wrappedData
     * is available, return false.
     *
     */
    public boolean isRowAvailable() {
        return getRowIndex() < getRowCount();
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#setRowIndex(int)
     *
     * Set the zero-relative index of the currently selected
     * row, or -1 to indicate that we are not positioned on a
     * row. It is possible to set the row index at a value for
     * which the underlying data collection does not contain
     * any row data. Therefore, callers may use the
     * isRowAvailable() method to detect whether row data will
     * be available for use by the getRowData() method. If there
     * is no wrappedData available when this method is called,
     * the specified rowIndex is stored (and may be retrieved
     * by a subsequent call to getRowData()), but no event is
     * sent. Otherwise, if the currently selected row index
     * is changed by this call, a DataModelEvent will be sent
     * to the rowSelected() method of all registered
     * DataModelListeners.
     */
    public void setRowIndex(int arg0) {
        _rowIndex = arg0;
    }

    /* (non-Javadoc)
     * @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
     *
     * Set the object representing the data collection wrapped by this DataModel.
     * If the specified data is null, detach this DataModel from any previously
     * wrapped data collection instead.
     * If data is non-null, the currently selected row index must be set to zero,
     * and a DataModelEvent must be sent to the rowSelected() method of all
     * registered DataModelListeners indicating that this row is now selected.
     */
    public void setWrappedData(Object arg0) {
        // TODO Auto-generated method stub
    }

    /**
     * sets a query delegate, use a cached
     * one if wanted/needed
     *
     * @param queryDelegate
     * @param useCached
     */
    public void setQueryDelegate(IDataAccessor queryDelegate, boolean useCached) {
        Object tmpQueryDelegate = null;

        if (useCached) {
            tmpQueryDelegate = JSFUtil.getSessionMap().get(createQueryKey(
                        queryDelegate));

            if (tmpQueryDelegate == null) {
                JSFUtil.getSessionMap().put(createQueryKey(queryDelegate),
                    queryDelegate);
            } else {
                queryDelegate = (IDataAccessor) tmpQueryDelegate;
            }
        }

        setQueryDelegate(queryDelegate);
    }
    
    public IDataAccessor getQueryDelegate() {
    	return _queryDelegate;
    }

    /**
     * @param queryDelegate
     * @return
     */
    private String createQueryKey(IDataAccessor queryDelegate) {
        return "QUERY" + this.getClass().getName() +
        queryDelegate.getClass().getName();
    }

    /**
     * query delegate setter
     * @param queryDelegate
     */
    public void setQueryDelegate(IDataAccessor queryDelegate) {
        _queryDelegate = queryDelegate;
        _queryDelegate.setDataModel(this);
    }

    /**
     * @param mode
     */
    public void setOrderMode(String mode) {
        _queryDelegate.setOrderMode(mode);
    }
}
/*
 * Created on 05.01.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package jsf.model;

import java.util.List;

import javax.faces.model.DataModel;


/**
 * @author Werner Punz MediaData
 *
 * A generalized data accessor Interface
 * for querying the database
 * All the queries within the hibernateDatamodel
 * must be encapsulated in
 * TODO add throws clauses here
 */
public interface IDataAccessor {
    /**
     *
     * this methode has to query the datasets
     * and has to set the table size in the data model
     * upon the given query
     *
     * @param from                the current from position
     * @param pageSize        the current pageSize which has to be queried
     * @return
     */
    public List queryDatasets(int from, int pageSize);

  
    
    /**
     * internally used the data model is set from within
     * @return
     */
    public DataModel getDataModel();

    /**
     * internally used from the outside model
     * @param model
     */
    public void setDataModel(DataModel model);

    /**
     * Sets the order mode
     * @param mode
     */
    public void setOrderMode(String mode);

    /**
     * returns the order mode
     * @return
     */
    public String getOrderMode();
}

Reply via email to