Package org.molgenis.framework.db.paging

Source Code of org.molgenis.framework.db.paging.PrimaryKeyPager

package org.molgenis.framework.db.paging;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.molgenis.framework.db.Database;
import org.molgenis.framework.db.DatabaseException;
import org.molgenis.framework.db.QueryRule;
import org.molgenis.framework.db.QueryRule.Operator;
import org.molgenis.util.Entity;

/**
* Page a database using the primary key.
*
* This pager was added to allow paging through large datasets which is much
* faster than OFFSET, given that it uses indexes of the primary key. The
* benefits depend on the implementation of Database.
*
* WARNING only faster when sorted by indexed fields!!
*
* @param <E>
*/
@Deprecated
public class PrimaryKeyPager<E extends Entity> extends AbstractPager<E>
{
  private static final long serialVersionUID = 1707494068232123242L;

  private static final Logger logger = Logger.getLogger(PrimaryKeyPager.class);

  /**
   * Constructor.
   *
   * @param entityClass
   *            class of the entity to be paged
   * @param primaryKeyField
   *            the primary key of the entity (must be unique and indexed)
   * @throws DatabaseException
   */
  public PrimaryKeyPager(Class<E> entityClass, String primaryKeyField) throws DatabaseException
  {
    super(entityClass, primaryKeyField);
    this.primaryKeyField = primaryKeyField;
    this.setPagingState(State.FIRST);
    // this.refresh();
  }

  /**
   * Secondary sorting field. This is necessary if orderByField is not an
   * indexed field. Typically this is the primary key of the entity.
   */
  private String primaryKeyField;

  /** remember index thresholds */
  private Object nextOrderByThreshold;
  private Object prevOrderByThreshold;
  private Object nextPKeyThreshold;
  private Object prevPKeyThreshold;

  /**
   * {@inheritDoc}. Implemented using 'primary key' QueryRules. This means
   * that at least the primary key is used to sort the data in the database
   * underlying the paging.
   */
  @Override
  public void refresh(Database db) throws DatabaseException
  {
    // don't use getters and setters!!! these will call refresh resulting in
    // endless loops
    if (this.pagingState == State.UPTODATE) return;

    // get the rules
    List<QueryRule> rules = new ArrayList<QueryRule>();
    rules.addAll(Arrays.asList(this.getFilters()));

    logger.debug("refresh started with state '" + pagingState + "'");
    reloadCount(db, rules.toArray(new QueryRule[rules.size()]));

    // first: add sorting of order by field
    if (getOrderByField() != null || "".equals(getOrderByField()))
    {
      logger.debug("adding order by on " + getOrderByField());
      if (getOrderByOperator().equals(Operator.SORTASC)) rules.add(new QueryRule(Operator.SORTASC,
          getOrderByField()));
      else
        rules.add(new QueryRule(Operator.SORTDESC, getOrderByField()));
    }

    // second: add sorting by primary key (ensuring predictable ordering)
    if (!getOrderByField().equals(primaryKeyField))
    {
      logger.debug("adding order by on " + primaryKeyField);
      if (getOrderByOperator().equals(Operator.SORTASC)) rules.add(new QueryRule(Operator.SORTASC,
          primaryKeyField));
      else
        rules.add(new QueryRule(Operator.SORTDESC, primaryKeyField));

    }

    // based on the pagingState we add additional filterings
    switch (pagingState)
    {
      case REFRESH:
        if (count > offset)
        {
          rules.add(new QueryRule(Operator.LIMIT, limit));
          rules.add(new QueryRule(getOrderByField(), Operator.GREATER_EQUAL, prevOrderByThreshold));
          if (!getOrderByField().equals(primaryKeyField)) rules.add(new QueryRule(primaryKeyField,
              Operator.GREATER_EQUAL, prevPKeyThreshold));
          // FIXME how can we distinguish page when sort field is not
          // unique!!
          logger.debug("loaded filters for refresh. Added operator: limit=" + limit + ", offset=" + offset);
          break;
        }
        else
        {
          logger.debug("refresh is delegated to 'last' operation because count < offset (maybe cause of deletes)");
          pagingState = State.LAST;
          this.refresh(db);
          return;
        }
      case NEXT:
        if (limit + offset < count)
        {
          offset = limit + offset;
          rules.add(new QueryRule(Operator.LIMIT, Math.min(limit, count - offset)));
          rules.add(new QueryRule(getOrderByField(), Operator.GREATER, nextOrderByThreshold));
          if (!getOrderByField().equals(primaryKeyField)) rules.add(new QueryRule(primaryKeyField,
              Operator.GREATER, nextPKeyThreshold));
          // FIXME how can we distinguish page when sort field is not
          // unique!!

          // get first from next threshold (exclusive)
          logger.debug("loaded filters for next. Added operator: " + getOrderByField() + " > "
              + nextOrderByThreshold + ". Offset is: " + offset + ") and " + primaryKeyField
              + " greater than: " + nextPKeyThreshold);
          break;
        }
        else
        // page in 'last' range, go last
        {
          pagingState = State.LAST;
          logger.debug("next, is already in 'last' range (offset=" + offset + "), refresh to last.");
          this.refresh(db);
          return;
        }
      case LAST:
        // get last, limit to remaining from count
        if (count % limit != 0) rules.add(new QueryRule(Operator.LIMIT, count % limit));
        else
          rules.add(new QueryRule(Operator.LIMIT, limit));
        if (count > limit) offset = (int) Math.round(Math.floor((count - 1) / limit) * limit);
        else
          offset = 0;
        rules.add(new QueryRule(Operator.LAST));
        logger.debug("loaded filters for last. Added operator: 'last'. Offset is: " + offset);
        break;
      case PREV:
        // get last before prev threshold (exclusive)
        // set it to be the previous valid offset
        if (offset - limit >= 0)
        {
          offset = offset - limit;
          rules.add(new QueryRule(Operator.LIMIT, limit));
          rules.add(new QueryRule(Operator.LAST));
          rules.add(new QueryRule(getOrderByField(), Operator.LESS, prevOrderByThreshold));
          if (!getOrderByField().equals(primaryKeyField)) rules.add(new QueryRule(primaryKeyField,
              Operator.LESS, prevPKeyThreshold));
          // FIXME how can we distinguish page when sort field is not
          // unique!!
          logger.debug("prev, offset: " + offset + ", " + getOrderByField() + " < " + prevOrderByThreshold
              + " and " + primaryKeyField + " greater than: " + prevPKeyThreshold);
          break;
        }
        else
        { // delegate to first
          logger.debug("prev, is already in 'first' range (offset=" + offset + "), refresh to first");
          pagingState = State.FIRST;
          this.refresh(db);
          return;
        }
      case FIRST:
        // get first, no additional filtering needed
        offset = 0;
        rules.add(new QueryRule(Operator.LIMIT, limit));
        logger.debug("loaded filters for first: no filters needed");
        break;
    }

    reloadPage(db, rules.toArray(new QueryRule[rules.size()]));

    // remember prevThresholds and nextThresholds for next time (only for
    // prev/next)
    if (page.size() > 0)
    {
      prevOrderByThreshold = page.get(0).get(getOrderByField());
      prevPKeyThreshold = page.get(0).get(primaryKeyField);
      nextOrderByThreshold = page.get(page.size() - 1).get(getOrderByField());
      nextPKeyThreshold = page.get(page.size() - 1).get(this.primaryKeyField);
    }
    else
    {
      logger.error("should never happen unless the db was changed between count and find");
    }

    // don't forget!
    pagingState = State.UPTODATE;
  }
}
TOP

Related Classes of org.molgenis.framework.db.paging.PrimaryKeyPager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.