[hibernate-commits] Hibernate SVN: r14333 - in shards/trunk/src: java/org/hibernate/shards/criteria and 8 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Feb 13 15:41:47 EST 2008


Author: max.ross
Date: 2008-02-13 15:41:47 -0500 (Wed, 13 Feb 2008)
New Revision: 14333

Added:
   shards/trunk/src/java/org/hibernate/shards/criteria/InMemoryOrderBy.java
   shards/trunk/src/test/org/hibernate/shards/criteria/InMemoryOrderByTest.java
   shards/trunk/src/test/org/hibernate/shards/criteria/ShardedCriteriaImplTest.java
Modified:
   shards/trunk/src/java/org/hibernate/shards/ShardedConfiguration.java
   shards/trunk/src/java/org/hibernate/shards/criteria/ExitOperationsCriteriaCollector.java
   shards/trunk/src/java/org/hibernate/shards/criteria/SetMaxResultsEvent.java
   shards/trunk/src/java/org/hibernate/shards/criteria/ShardedCriteriaImpl.java
   shards/trunk/src/java/org/hibernate/shards/criteria/ShardedSubcriteriaImpl.java
   shards/trunk/src/java/org/hibernate/shards/strategy/exit/ExitOperationUtils.java
   shards/trunk/src/java/org/hibernate/shards/strategy/exit/OrderExitOperation.java
   shards/trunk/src/java/org/hibernate/shards/strategy/exit/RowCountExitOperation.java
   shards/trunk/src/java/org/hibernate/shards/strategy/selection/ShardResolutionStrategyDataImpl.java
   shards/trunk/src/test/org/hibernate/shards/NonPermutedTests.java
   shards/trunk/src/test/org/hibernate/shards/criteria/ShardedSubcriteriaImplTest.java
   shards/trunk/src/test/org/hibernate/shards/integration/model/ModelCriteriaPermutedIntegrationTest.java
   shards/trunk/src/test/org/hibernate/shards/integration/platform/mysql/MySQLDatabasePlatform.java
   shards/trunk/src/test/org/hibernate/shards/model/Building.java
   shards/trunk/src/test/org/hibernate/shards/strategy/exit/OrderExitOperationTest.java
Log:
HSHARDS-52

Applies LIMIT clauses more aggresively to avoid pulling back entire tables.
Also reworked how in memory order-by works because, um, it wasn't working.
Also brought the MySQL schema in line with the hsql schema.

Modified: shards/trunk/src/java/org/hibernate/shards/ShardedConfiguration.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/ShardedConfiguration.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/ShardedConfiguration.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -220,8 +220,8 @@
   }
 
   /**
-   * Takes the values of the properties declared in VARIABLE_PROPERTIES from
-   * a shard-specific config and sets them as the values of the same properties
+   * Takes the values of the properties exposed by the ShardConfiguration
+   * interface and sets them as the values of the corresponding properties
    * in the prototype config.
    */
   void populatePrototypeWithVariableProperties(ShardConfiguration config) {

Modified: shards/trunk/src/java/org/hibernate/shards/criteria/ExitOperationsCriteriaCollector.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/criteria/ExitOperationsCriteriaCollector.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/criteria/ExitOperationsCriteriaCollector.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -66,7 +66,7 @@
   private SessionFactoryImplementor sessionFactoryImplementor;
 
   // Order operations applied to the Criteria
-  private List<Order> orders = Lists.newArrayList();
+  private List<InMemoryOrderBy> orders = Lists.newArrayList();
 
   // Our friendly neighborhood logger
   private final Log log = LogFactory.getLog(getClass());
@@ -123,11 +123,14 @@
   /**
    * Add the given Order
    *
+   * @param associationPath the association path leading to the object to which
+   * this order clause applies - null if the order clause applies to the top
+   * level object
    * @param order the order to add
    * @return this
    */
-  public ExitOperationsCollector addOrder(Order order) {
-    this.orders.add(order);
+  public ExitOperationsCollector addOrder(String associationPath, Order order) {
+    orders.add(new InMemoryOrderBy(associationPath, order));
     return this;
   }
 
@@ -153,9 +156,12 @@
       result = new DistinctExitOperation(distinct).apply(result);
     }
 
-    for(Order order : orders) {
-      result = new OrderExitOperation(order).apply(result);
-    }
+    // not clear to me why we need to create an OrderExitOperation
+    // are we even taking advantage of the fact that it implements the
+    // ExitOperation interface?
+    OrderExitOperation op = new OrderExitOperation(orders);
+    result = op.apply(result);
+
     if (firstResult != null) {
       result = new FirstResultExitOperation(firstResult).apply(result);
     }
@@ -189,4 +195,11 @@
     this.sessionFactoryImplementor = sessionFactoryImplementor;
   }
 
+  Integer getMaxResults() {
+    return maxResults;
+  }
+
+  Integer getFirstResult() {
+    return firstResult;
+  }
 }

Added: shards/trunk/src/java/org/hibernate/shards/criteria/InMemoryOrderBy.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/criteria/InMemoryOrderBy.java	                        (rev 0)
+++ shards/trunk/src/java/org/hibernate/shards/criteria/InMemoryOrderBy.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package org.hibernate.shards.criteria;
+
+import org.hibernate.criterion.Order;
+
+/**
+ * Describes an 'order by' that we're going to apply in memory
+ *
+ * @author maxr at google.com (Max Ross)
+ */
+public class InMemoryOrderBy {
+
+  // This is the full path to the property we're sorting by.
+  // For example, if the criteria is on Building and we're sorting by numFloors
+  // the expression is just 'numFloors.'  However, if the criteria is on Building
+  // and we're sorting by floor number (Building has 0 to n Floors and each Floor
+  // has a 'number' member) then the expression is 'floors.number'
+  private final String expression;
+  private final boolean isAscending;
+
+  /**
+   * Constructs an InMemoryOrderBy instance
+   * @param associationPath The association path leading to the object to which
+   * the provided {@link Order} parameter applies.  Null if the {@link Order}
+   * parameter applies to the top level object
+   * @param order A standard Hibernate {@link Order} object.
+   */
+  public InMemoryOrderBy(String associationPath, Order order) {
+    this.expression = getAssociationPrefix(associationPath) + getSortingProperty(order);
+    this.isAscending = isAscending(order);
+  }
+
+  private static String getAssociationPrefix(String associationPath) {
+    return associationPath == null ? "" : associationPath + ".";
+  }
+
+  private static boolean isAscending(Order order) {
+    return order.toString().toUpperCase().endsWith("ASC");
+  }
+
+  public String getExpression() {
+    return expression;
+  }
+
+  public boolean isAscending() {
+    return isAscending;
+  }
+
+  private static String getSortingProperty(Order order) {
+    /**
+     * This method relies on the format that Order is using:
+     * propertyName + ' ' + (ascending?"asc":"desc")
+     */
+    String str = order.toString();
+    return str.substring(0, str.indexOf(' '));
+  }
+
+
+}

Modified: shards/trunk/src/java/org/hibernate/shards/criteria/SetMaxResultsEvent.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/criteria/SetMaxResultsEvent.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/criteria/SetMaxResultsEvent.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -44,4 +44,8 @@
   public void onEvent(Criteria crit) {
     crit.setMaxResults(maxResults);
   }
+
+  public int getMaxResults() {
+    return maxResults;
+  }
 }

Modified: shards/trunk/src/java/org/hibernate/shards/criteria/ShardedCriteriaImpl.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/criteria/ShardedCriteriaImpl.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/criteria/ShardedCriteriaImpl.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -65,8 +65,14 @@
 
   // the criteria collector we use to process the results of executing
   // the Criteria across multiple shards
-  private final ExitOperationsCriteriaCollector criteriaCollector;
+  final ExitOperationsCriteriaCollector criteriaCollector;
 
+  // the last value with which setFirstResult was called
+  private int firstResult;
+
+  // the last value with which maxResults was called
+  private Integer maxResults;
+
   /**
    * Construct a ShardedCriteriaImpl
    *
@@ -166,7 +172,16 @@
   }
 
   public Criteria addOrder(Order order) {
-    criteriaCollector.addOrder(order);
+    // Order applies to top-level object so we pass a null association path
+    criteriaCollector.addOrder(null, order);
+    CriteriaEvent event = new AddOrderEvent(order);
+    for (Shard shard : shards) {
+      if (shard.getCriteriaById(criteriaId) != null) {
+        shard.getCriteriaById(criteriaId).addOrder(order);
+      } else {
+        shard.addCriteriaEvent(criteriaId, event);
+      }
+    }
     return this;
   }
 
@@ -244,10 +259,18 @@
    * established we need to create the actual subcriteria, and for each shard
    * where the Criteria has not yet been established we need to register an
    * event that will create the Subcriteria when the Criteria is established.
+   *
+   * @param factory the factory to use to create the subcriteria
+   * @param associationPath the association path to the property on which we're
+   * creating a subcriteria
+   *
+   * @return a new ShardedSubcriteriaImpl
    */
-  private ShardedSubcriteriaImpl createSubcriteria(SubcriteriaFactory factory) {
+  private ShardedSubcriteriaImpl createSubcriteria(
+      SubcriteriaFactory factory, String associationPath) {
 
-    ShardedSubcriteriaImpl subcrit = new ShardedSubcriteriaImpl(shards, this);
+    ShardedSubcriteriaImpl subcrit =
+        new ShardedSubcriteriaImpl(shards, this, criteriaCollector, associationPath);
 
     for (Shard shard : shards) {
       Criteria crit = shard.getCriteriaById(criteriaId);
@@ -265,25 +288,25 @@
   public Criteria createCriteria(String associationPath)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, int joinType)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, joinType);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, String alias)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, alias);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, String alias,
       int joinType) throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, alias, joinType);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria setResultTransformer(ResultTransformer resultTransformer) {
@@ -299,14 +322,42 @@
     return this;
   }
 
+  /*
+      A description of the trickyness that goes on with first result and
+      max result:
+      You can safely apply the maxResult on each individual shard so long as there
+      is no firstResult specified.  If firstResult is specified you can't
+      safely apply it on each shard but you can set maxResult to be the existing
+      value of maxResult + firstResult.
+   */
+
   public Criteria setMaxResults(int maxResults) {
+    // the criteriaCollector will use the maxResult value that was passed in
     criteriaCollector.setMaxResults(maxResults);
-
+    this.maxResults = maxResults;
+    int adjustedMaxResults = maxResults + firstResult;
+    // the query executed against each shard will use maxResult + firstResult
+    SetMaxResultsEvent event = new SetMaxResultsEvent(adjustedMaxResults);
+    for (Shard shard : shards) {
+      if (shard.getCriteriaById(criteriaId) != null) {
+        shard.getCriteriaById(criteriaId).setMaxResults(adjustedMaxResults);
+      } else {
+        shard.addCriteriaEvent(criteriaId, event);
+      }
+    }
     return this;
   }
 
   public Criteria setFirstResult(int firstResult) {
     criteriaCollector.setFirstResult(firstResult);
+    this.firstResult = firstResult;
+    // firstResult cannot be safely applied to the Criteria that will be
+    // executed against the Shard.  If a maxResult has been set we need to adjust
+    // that to take the firstResult into account.  Just calling setMaxResults
+    // will take care of this for us.
+    if(maxResults != null) {
+      setMaxResults(maxResults);
+    }
     return this;
   }
 

Modified: shards/trunk/src/java/org/hibernate/shards/criteria/ShardedSubcriteriaImpl.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/criteria/ShardedSubcriteriaImpl.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/criteria/ShardedSubcriteriaImpl.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -63,18 +63,29 @@
   // when the actual Criteria objects are established
   private final Map<Shard, List<CriteriaEvent>> shardToEventListMap = Maps.newHashMap();
 
+  private final ExitOperationsCriteriaCollector criteriaCollector;
+
+  private final String associationPath;
+
   /**
    * Construct a ShardedSubcriteriaImpl
    *
    * @param shards the shards that we're aware of
    * @param parent our parent
+   * @param criteriaCollector the collector for extit operations
+   * @param associationPath the association path for the subcriteria
    */
-  public ShardedSubcriteriaImpl(List<Shard> shards, ShardedCriteria parent) {
+  public ShardedSubcriteriaImpl(List<Shard> shards, ShardedCriteria parent,
+      ExitOperationsCriteriaCollector criteriaCollector, String associationPath) {
     Preconditions.checkNotNull(shards);
     Preconditions.checkNotNull(parent);
     Preconditions.checkArgument(!shards.isEmpty());
+    Preconditions.checkNotNull(criteriaCollector);
+    Preconditions.checkNotNull(associationPath);
     this.shards = shards;
     this.parent = parent;
+    this.criteriaCollector = criteriaCollector;
+    this.associationPath = associationPath;
     // let's set up our maps
     for(Shard shard : shards) {
       shardToCriteriaMap.put(shard, null);
@@ -137,6 +148,7 @@
   }
 
   public Criteria addOrder(Order order) {
+    criteriaCollector.addOrder(associationPath, order);
     CriteriaEvent event = new AddOrderEvent(order);
     for (Shard shard : shards) {
       if (shardToCriteriaMap.get(shard) != null) {
@@ -223,33 +235,13 @@
     return this;
   }
 
-  /**
-   * TODO(maxr)
-   * This clearly isn't what people want.  We should be building an
-   * exit strategy that returns once we've accumulated maxResults
-   * across _all_ shards, not each shard.
-   */
   public Criteria setMaxResults(int maxResults) {
-    CriteriaEvent event = new SetMaxResultsEvent(maxResults);
-    for (Shard shard : shards) {
-      if (shardToCriteriaMap.get(shard) != null) {
-        shardToCriteriaMap.get(shard).setMaxResults(maxResults);
-      } else {
-        shardToEventListMap.get(shard).add(event);
-      }
-    }
+    parent.setMaxResults(maxResults);
     return this;
   }
 
   public Criteria setFirstResult(int firstResult) {
-    CriteriaEvent event = new SetFirstResultEvent(firstResult);
-    for (Shard shard : shards) {
-      if (shardToCriteriaMap.get(shard) != null) {
-        shardToCriteriaMap.get(shard).setFirstResult(firstResult);
-      } else {
-        shardToEventListMap.get(shard).add(event);
-      }
-    }
+    parent.setFirstResult(firstResult);
     return this;
   }
 
@@ -358,9 +350,12 @@
     return getParentCriteria().uniqueResult();
   }
 
-  private ShardedSubcriteriaImpl createSubcriteria(SubcriteriaFactory factory) {
+  private ShardedSubcriteriaImpl createSubcriteria(SubcriteriaFactory factory,
+      String newAssociationPath) {
+    String fullAssociationPath = associationPath + "." + newAssociationPath;
     // first build our sharded subcrit
-    ShardedSubcriteriaImpl subcrit = new ShardedSubcriteriaImpl(shards, parent);
+    ShardedSubcriteriaImpl subcrit =
+        new ShardedSubcriteriaImpl(shards, parent, criteriaCollector, fullAssociationPath);
     for (Shard shard : shards) {
       // see if we already have a concreate Criteria object for each shard
       if (shardToCriteriaMap.get(shard) != null) {
@@ -395,25 +390,25 @@
   public Criteria createCriteria(String associationPath)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, int joinType)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, joinType);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, String alias)
       throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, alias);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public Criteria createCriteria(String associationPath, String alias,
       int joinType) throws HibernateException {
     SubcriteriaFactory factory = new SubcriteriaFactoryImpl(associationPath, alias, joinType);
-    return createSubcriteria(factory);
+    return createSubcriteria(factory, associationPath);
   }
 
   public ShardedCriteria getParentCriteria() {

Modified: shards/trunk/src/java/org/hibernate/shards/strategy/exit/ExitOperationUtils.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/strategy/exit/ExitOperationUtils.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/strategy/exit/ExitOperationUtils.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -50,7 +50,8 @@
   public static Comparable<Object> getPropertyValue(Object obj, String propertyName) {
     /**
      * TODO(maulik) respect the client's choice in how Hibernate accesses
-     * property values.
+     * property values.  Also need to implement some caching - this gets called
+     * from a Comparator!
      *
      * Currently this method access members of an object using getters only,
      * event of the client has specifed to use direct field access. Ideally,

Modified: shards/trunk/src/java/org/hibernate/shards/strategy/exit/OrderExitOperation.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/strategy/exit/OrderExitOperation.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/strategy/exit/OrderExitOperation.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -18,8 +18,8 @@
 
 package org.hibernate.shards.strategy.exit;
 
-import org.hibernate.criterion.Order;
-import org.hibernate.shards.util.Preconditions;
+import org.hibernate.shards.criteria.InMemoryOrderBy;
+import org.hibernate.shards.util.Lists;
 
 import java.util.Collections;
 import java.util.Comparator;
@@ -30,50 +30,68 @@
  */
 public class OrderExitOperation implements ExitOperation {
 
-  private final Order order;
-  private final String propertyName;
+  private final List<InMemoryOrderBy> orderByList;
 
-  public OrderExitOperation(Order order) {
-    //TODO(maulik) support Ignore case!
-    Preconditions.checkState(order.toString().endsWith("asc") ||
-                             order.toString().endsWith("desc"));
+  private static final Comparator<Object> EQUALS = new Comparator<Object>() {
+    public int compare(Object o, Object o1) {
+      return 0;
+    }
+  };
 
-    this.order = order;
-    this.propertyName = getSortingProperty(order);
+  public OrderExitOperation(List<InMemoryOrderBy> orderByList) {
+    this.orderByList = Lists.newArrayList(orderByList);
+    // need to reverse the list so we build the comparator from the inside out
+    Collections.reverse(this.orderByList);
   }
 
   public List<Object> apply(List<Object> results) {
     List<Object> nonNullList = ExitOperationUtils.getNonNullList(results);
-    Comparator<Object> comparator = new Comparator<Object>() {
-      public int compare(Object o1, Object o2) {
-        if (o1 == o2) {
-          return 0;
-        }
-        Comparable<Object> o1Value = ExitOperationUtils.getPropertyValue(o1, propertyName);
-        Comparable<Object> o2Value = ExitOperationUtils.getPropertyValue(o2, propertyName);
-        if (o1Value == null) {
-          return -1;
-        }
-        return o1Value.compareTo(o2Value);
-      }
-    };
 
+    Comparator<Object> comparator = buildComparator();
     Collections.sort(nonNullList, comparator);
-    if (order.toString().endsWith("desc")) {
-      Collections.reverse(nonNullList);
-    }
 
     return nonNullList;
   }
 
-  private static String getSortingProperty(Order order) {
-    /**
-     * This method relies on the format that Order is using:
-     * propertyName + ' ' + (ascending?"asc":"desc")
-     */
-    String str = order.toString();
-    return str.substring(0, str.indexOf(' '));
+  private Comparator<Object> buildComparator() {
+    // the most-inner comparator is one that returns 0 for everything.
+    Comparator<Object> inner = EQUALS;
+    for(InMemoryOrderBy order : orderByList) {
+      inner = new PropertyComparator(order.getExpression(), inner);
+      if(!order.isAscending()) {
+        inner = Collections.reverseOrder(inner);
+      }
+    }
+    return inner;
   }
 
-  
+  private static final class PropertyComparator implements Comparator<Object> {
+
+    private final String propertyName;
+    private final Comparator<Object> tieBreaker;
+
+    public PropertyComparator(String propertyName, Comparator<Object> tieBreaker) {
+      this.propertyName = propertyName;
+      this.tieBreaker = tieBreaker;
+    }
+
+    public int compare(Object o1, Object o2) {
+      int result;
+      if (o1 == o2) {
+        result = 0;
+      } else {
+        Comparable<Object> o1Value = ExitOperationUtils.getPropertyValue(o1, propertyName);
+        Comparable<Object> o2Value = ExitOperationUtils.getPropertyValue(o2, propertyName);
+        if (o1Value == null) {
+          result = -1;
+        } else {
+          result = o1Value.compareTo(o2Value);
+        }
+      }
+      if(result == 0) {
+        result = tieBreaker.compare(o1, o2);
+      }
+      return result;
+    }
+  }
 }

Modified: shards/trunk/src/java/org/hibernate/shards/strategy/exit/RowCountExitOperation.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/strategy/exit/RowCountExitOperation.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/strategy/exit/RowCountExitOperation.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -36,7 +36,6 @@
 
   public List<Object> apply(List<Object> results) {
     List<Object> nonNullResults = ExitOperationUtils.getNonNullList(results);
-    
     return Collections.singletonList((Object) nonNullResults.size());
   }
 }

Modified: shards/trunk/src/java/org/hibernate/shards/strategy/selection/ShardResolutionStrategyDataImpl.java
===================================================================
--- shards/trunk/src/java/org/hibernate/shards/strategy/selection/ShardResolutionStrategyDataImpl.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/java/org/hibernate/shards/strategy/selection/ShardResolutionStrategyDataImpl.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -44,4 +44,31 @@
     return id;
   }
 
+
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    ShardResolutionStrategyDataImpl that = (ShardResolutionStrategyDataImpl) o;
+
+    if (!entityName.equals(that.entityName)) {
+      return false;
+    }
+    if (!id.equals(that.id)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = entityName.hashCode();
+    result = 31 * result + id.hashCode();
+    return result;
+  }
 }

Modified: shards/trunk/src/test/org/hibernate/shards/NonPermutedTests.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/NonPermutedTests.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/NonPermutedTests.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -48,6 +48,7 @@
     classes.add(org.hibernate.shards.criteria.CreateAliasEventTest.class);
     classes.add(org.hibernate.shards.criteria.CreateSubcriteriaEventTest.class);
     classes.add(org.hibernate.shards.criteria.CriteriaFactoryImplTest.class);
+    classes.add(org.hibernate.shards.criteria.InMemoryOrderByTest.class);
     classes.add(org.hibernate.shards.criteria.SetCacheModeEventTest.class);
     classes.add(org.hibernate.shards.criteria.SetCacheRegionEventTest.class);
     classes.add(org.hibernate.shards.criteria.SetCacheableEventTest.class);
@@ -61,6 +62,7 @@
     classes.add(org.hibernate.shards.criteria.SetProjectionEventTest.class);
     classes.add(org.hibernate.shards.criteria.SetResultTransformerEventTest.class);
     classes.add(org.hibernate.shards.criteria.SetTimeoutEventTest.class);
+    classes.add(org.hibernate.shards.criteria.ShardedCriteriaImplTest.class);
     classes.add(org.hibernate.shards.criteria.ShardedSubcriteriaImplTest.class);
     classes.add(org.hibernate.shards.criteria.SubcriteriaFactoryImplTest.class);
     classes.add(org.hibernate.shards.id.ShardedTableHiLoGeneratorTest.class);

Added: shards/trunk/src/test/org/hibernate/shards/criteria/InMemoryOrderByTest.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/criteria/InMemoryOrderByTest.java	                        (rev 0)
+++ shards/trunk/src/test/org/hibernate/shards/criteria/InMemoryOrderByTest.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package org.hibernate.shards.criteria;
+
+import junit.framework.TestCase;
+
+import org.hibernate.criterion.Order;
+
+/**
+ * @author maxr at google.com (Max Ross)
+ */
+public class InMemoryOrderByTest extends TestCase {
+
+  public void testTopLevel() {
+    InMemoryOrderBy imob = new InMemoryOrderBy(null, Order.asc("yam"));
+    assertEquals("yam", imob.getExpression());
+    assertTrue(imob.isAscending());
+
+    imob = new InMemoryOrderBy(null, Order.desc("yam"));
+    assertEquals("yam", imob.getExpression());
+    assertFalse(imob.isAscending());
+  }
+
+  public void testSubObject() {
+    InMemoryOrderBy imob = new InMemoryOrderBy("a.b.c", Order.asc("yam"));
+    assertEquals("a.b.c.yam", imob.getExpression());
+    assertTrue(imob.isAscending());
+
+    imob = new InMemoryOrderBy("a.b.c", Order.desc("yam"));
+    assertEquals("a.b.c.yam", imob.getExpression());
+    assertFalse(imob.isAscending());
+  }
+}

Added: shards/trunk/src/test/org/hibernate/shards/criteria/ShardedCriteriaImplTest.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/criteria/ShardedCriteriaImplTest.java	                        (rev 0)
+++ shards/trunk/src/test/org/hibernate/shards/criteria/ShardedCriteriaImplTest.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package org.hibernate.shards.criteria;
+
+import junit.framework.TestCase;
+
+import org.hibernate.Criteria;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.shards.Shard;
+import org.hibernate.shards.ShardDefaultMock;
+import org.hibernate.shards.strategy.access.ShardAccessStrategy;
+import org.hibernate.shards.strategy.access.ShardAccessStrategyDefaultMock;
+import org.hibernate.shards.util.Lists;
+
+import java.util.List;
+
+/**
+ * @author maxr at google.com (Max Ross)
+ */
+public class ShardedCriteriaImplTest extends TestCase {
+
+  public void testSetFirstResultAfterMaxResult() {
+    CriteriaId id = new CriteriaId(0);
+    final List<CriteriaEvent> events = Lists.newArrayList();
+    Shard shard = new ShardDefaultMock() {
+      @Override
+      public SessionFactoryImplementor getSessionFactoryImplementor() {
+        return null;
+      }
+      @Override
+      public Criteria getCriteriaById(CriteriaId id) {
+        return null;
+      }
+      public void addCriteriaEvent(CriteriaId id, CriteriaEvent event) {
+        events.add(event);
+      }
+    };
+    List<Shard> shards = Lists.newArrayList(shard);
+    CriteriaFactory cf = new CriteriaFactoryDefaultMock();
+    ShardAccessStrategy sas = new ShardAccessStrategyDefaultMock();
+    ShardedCriteriaImpl crit = new ShardedCriteriaImpl(id, shards, cf, sas);
+    crit.setMaxResults(2);
+    assertEquals(2, crit.criteriaCollector.getMaxResults().intValue());
+    assertNull(crit.criteriaCollector.getFirstResult());
+    assertEquals(1, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+
+    crit.setMaxResults(5);
+    assertEquals(5, crit.criteriaCollector.getMaxResults().intValue());
+    assertNull(crit.criteriaCollector.getFirstResult());
+    assertEquals(2, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+
+    crit.setFirstResult(2);
+    assertEquals(5, crit.criteriaCollector.getMaxResults().intValue());
+    assertEquals(2, crit.criteriaCollector.getFirstResult().intValue());
+    assertEquals(3, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(2)).getMaxResults());
+
+    crit.setFirstResult(2);
+    assertEquals(5, crit.criteriaCollector.getMaxResults().intValue());
+    assertEquals(2, crit.criteriaCollector.getFirstResult().intValue());
+    assertEquals(4, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(2)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(3)).getMaxResults());
+
+    crit.setFirstResult(1);
+    assertEquals(5, crit.criteriaCollector.getMaxResults().intValue());
+    assertEquals(1, crit.criteriaCollector.getFirstResult().intValue());
+    assertEquals(5, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(2)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(3)).getMaxResults());
+    assertEquals(6, ((SetMaxResultsEvent)events.get(4)).getMaxResults());
+
+    crit.setFirstResult(0);
+    assertEquals(5, crit.criteriaCollector.getMaxResults().intValue());
+    assertEquals(0, crit.criteriaCollector.getFirstResult().intValue());
+    assertEquals(6, events.size());
+    assertEquals(2, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(2)).getMaxResults());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(3)).getMaxResults());
+    assertEquals(6, ((SetMaxResultsEvent)events.get(4)).getMaxResults());
+    assertEquals(5, ((SetMaxResultsEvent)events.get(5)).getMaxResults());
+  }
+
+  public void testSetMaxResultAfterFirstResult() {
+    CriteriaId id = new CriteriaId(0);
+    final List<CriteriaEvent> events = Lists.newArrayList();
+    Shard shard = new ShardDefaultMock() {
+      @Override
+      public SessionFactoryImplementor getSessionFactoryImplementor() {
+        return null;
+      }
+      @Override
+      public Criteria getCriteriaById(CriteriaId id) {
+        return null;
+      }
+      public void addCriteriaEvent(CriteriaId id, CriteriaEvent event) {
+        events.add(event);
+      }
+    };
+    List<Shard> shards = Lists.newArrayList(shard);
+    CriteriaFactory cf = new CriteriaFactoryDefaultMock();
+    ShardAccessStrategy sas = new ShardAccessStrategyDefaultMock();
+    ShardedCriteriaImpl crit = new ShardedCriteriaImpl(id, shards, cf, sas);
+
+    crit.setFirstResult(3);
+    assertEquals(3, crit.criteriaCollector.getFirstResult().intValue());
+    assertNull(crit.criteriaCollector.getMaxResults());
+    assertEquals(0, events.size());
+
+    crit.setFirstResult(5);
+    assertEquals(5, crit.criteriaCollector.getFirstResult().intValue());
+    assertNull(crit.criteriaCollector.getMaxResults());
+    assertEquals(0, events.size());
+
+    crit.setMaxResults(2);
+    crit.setMaxResults(3);
+    assertEquals(5, crit.criteriaCollector.getFirstResult().intValue());
+    assertEquals(3, crit.criteriaCollector.getMaxResults().intValue());
+    assertEquals(2, events.size());
+    assertEquals(7, ((SetMaxResultsEvent)events.get(0)).getMaxResults());
+    assertEquals(8, ((SetMaxResultsEvent)events.get(1)).getMaxResults());
+  }
+}

Modified: shards/trunk/src/test/org/hibernate/shards/criteria/ShardedSubcriteriaImplTest.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/criteria/ShardedSubcriteriaImplTest.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/criteria/ShardedSubcriteriaImplTest.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -19,6 +19,7 @@
 package org.hibernate.shards.criteria;
 
 import junit.framework.TestCase;
+
 import org.hibernate.Criteria;
 import org.hibernate.HibernateException;
 import org.hibernate.ScrollMode;
@@ -50,7 +51,10 @@
     Shard s2 = new MyShard();
     Shard s3 = new MyShard();
     List<Shard> shards = Lists.newArrayList(s1, s2, s3);
-    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(shards,  new ShardedCriteriaDefaultMock());
+    ExitOperationsCriteriaCollector collector =
+        new ExitOperationsCriteriaCollector();
+    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(
+        shards,  new ShardedCriteriaDefaultMock(), collector, "");
     assertEquals(shards.size(), ss.getShardToCriteriaMap().size());
     assertEquals(shards.size(), ss.getShardToEventListMap().size());
   }
@@ -65,7 +69,10 @@
         return null;
       }
     };
-    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(shards,  parent);
+    ExitOperationsCriteriaCollector collector =
+        new ExitOperationsCriteriaCollector();
+    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(
+        shards, parent, collector, "");
     ss.list();
     assertTrue(called[0]);
   }
@@ -80,7 +87,10 @@
         return null;
       }
     };
-    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(shards,  parent);
+    ExitOperationsCriteriaCollector collector =
+        new ExitOperationsCriteriaCollector();
+    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(
+        shards, parent, collector, "");
     ss.uniqueResult();
     assertTrue(called[0]);
   }
@@ -103,7 +113,10 @@
         return null;
       }
     };
-    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(shards,  parent);
+    ExitOperationsCriteriaCollector collector =
+        new ExitOperationsCriteriaCollector();
+    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(
+        shards, parent, collector, "");
     ss.scroll();
     assertTrue(scrollNoArgsCalled[0]);
     assertFalse(scroll1ArgCalled[0]);
@@ -124,7 +137,10 @@
     Shard someOtherShard = new ShardDefaultMock();
     List<Shard> shards = Lists.newArrayList(shard, someOtherShard);
     ShardedCriteria parent = new ShardedCriteriaDefaultMock();
-    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(shards,  parent);
+    ExitOperationsCriteriaCollector collector =
+        new ExitOperationsCriteriaCollector();
+    ShardedSubcriteriaImpl ss = new ShardedSubcriteriaImpl(
+        shards, parent, collector, "");
     ss.getShardToEventListMap().get(shard).add(new CriteriaEventDefaultMock());
     final Criteria subcritToReturn = new CriteriaDefaultMock();
     SubcriteriaFactory factory = new SubcriteriaFactoryDefaultMock() {

Modified: shards/trunk/src/test/org/hibernate/shards/integration/model/ModelCriteriaPermutedIntegrationTest.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/integration/model/ModelCriteriaPermutedIntegrationTest.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/integration/model/ModelCriteriaPermutedIntegrationTest.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -229,10 +229,10 @@
     Criteria officeCrit = floorCrit.createCriteria("offices");
     officeCrit.add(Restrictions.eq("label", "LAHGE"));
     // now how we execute the query via the floorcrit
-    List<Integer> list = list(officeCrit);
-    assertEquals(1, list.size());
+    List<Integer> result = list(officeCrit);
+    assertEquals(1, result.size());
     int total = 0;
-    for(int shardTotal : list) {
+    for(int shardTotal : result) {
       total += shardTotal;
     }
     assertEquals(2, total);
@@ -240,17 +240,58 @@
 
   public void testAvgProjection() {
     Criteria crit = session.createCriteria(Floor.class).setProjection(Projections.avg("squareFeet"));
-    List<Double> l = list(crit);
-    assertEquals(1, l.size());
-    assertEquals(20.0, l.get(0));
+    List<Double> result = list(crit);
+    assertEquals(1, result.size());
+    assertEquals(20.0, result.get(0));
   }
 
-
   public void testMaxResults() throws Exception {
     Criteria crit = session.createCriteria(Building.class).setMaxResults(1);
     assertEquals(1, list(crit).size());
   }
 
+  public void testFirstAndMaxResults() {
+    Building b3 = ModelDataFactory.building("b3");
+    Building b4 = ModelDataFactory.building("b4");
+    Building b5 = ModelDataFactory.building("b5");
+    session.beginTransaction();
+    session.save(b5);
+    session.save(b3);
+    session.save(b4);
+    commitAndResetSession();
+
+    Criteria crit =
+        session.createCriteria(Building.class).addOrder(Order.desc("name")).
+            setFirstResult(2).setMaxResults(2);
+    List<Building> buildings = list(crit);
+    assertEquals(2, buildings.size());
+    assertEquals(b3, buildings.get(0));
+    assertEquals(b2, buildings.get(1));
+  }
+
+  public void testFirstAndMaxResultsWithSubCrit() {
+    Building b3 = ModelDataFactory.building("b3");
+    Floor b3f1 = ModelDataFactory.floor(b3, 1);
+    Building b4 = ModelDataFactory.building("b4");
+    Floor b4f1 = ModelDataFactory.floor(b4, 1);
+    Building b5 = ModelDataFactory.building("b5");
+    Floor b5f1 = ModelDataFactory.floor(b5, 1);
+    session.beginTransaction();
+    session.save(b5);
+    session.save(b3);
+    session.save(b4);
+    commitAndResetSession();
+
+    Criteria crit =
+        session.createCriteria(Building.class).addOrder(Order.desc("name")).
+            createCriteria("floors").add(Restrictions.eq("number", 1)).
+            setFirstResult(2).setMaxResults(2);
+    List<Building> buildings = list(crit);
+    assertEquals(2, buildings.size());
+    assertEquals(b3, buildings.get(0));
+    assertEquals(b2, buildings.get(1));
+  }
+
   public void testAggregateProjection() throws Exception {
     Criteria crit = session.createCriteria(Floor.class).setProjection(Projections.sum("number"));
     List<Integer> l = list(crit);
@@ -276,10 +317,11 @@
 
   public void testMultiOrdering() throws Exception {
     Criteria crit = session.createCriteria(Office.class);
-    crit.addOrder(Order.asc("label")).addOrder(Order.desc("floor.building.name"));
-    List<Office> l = list(crit);
-    List<Office> answer = Lists.newArrayList(b2f1o1, b1f3o1, b1f3o2);
-    assertTrue(answer.equals(l));
+    crit.addOrder(Order.asc("label")).createCriteria("floor").
+        createCriteria("building").addOrder(Order.desc("name"));
+    List<Office> listResult = list(crit);
+    List<Office> answer = Lists.newArrayList(b2f1o1, b1f3o2, b1f3o1);
+    assertTrue(answer.equals(listResult));
   }
 }
 

Modified: shards/trunk/src/test/org/hibernate/shards/integration/platform/mysql/MySQLDatabasePlatform.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/integration/platform/mysql/MySQLDatabasePlatform.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/integration/platform/mysql/MySQLDatabasePlatform.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -38,15 +38,15 @@
 
   private static final Iterable<String> CREATE_TABLE_STATEMENTS = Lists.newArrayList(
      "CREATE TABLE hibernate_unique_key (id DECIMAL(40,0) PRIMARY KEY, next_hi DECIMAL(40,0))"
-    ,"INSERT INTO hibernate_unique_key(next_hi) VALUES(1)"
+    ,"INSERT INTO hibernate_unique_key(id, next_hi) VALUES(1,1)"
     ,"CREATE TABLE sample_table (id DECIMAL(40,0) PRIMARY KEY, str_col VARCHAR(256))"
     ,"CREATE TABLE sample_table2 (id DECIMAL(40,0) PRIMARY KEY, str_col VARCHAR(256))"
     ,"CREATE TABLE Elevator (elevatorId DECIMAL(40,0) PRIMARY KEY, buildingId DECIMAL(40,0))"
     ,"CREATE TABLE Building (buildingId DECIMAL(40,0) PRIMARY KEY, name VARCHAR(50))"
-    ,"CREATE TABLE Floor (floorId DECIMAL(40,0) PRIMARY KEY, buildingId DECIMAL(40,0), upEscalatorId DECIMAL(40,0), downEscalatorId DECIMAL(40,0), number DECIMAL(40,0))"
+    ,"CREATE TABLE Floor (floorId DECIMAL(40,0) PRIMARY KEY, buildingId DECIMAL(40,0), upEscalatorId DECIMAL(40,0), downEscalatorId DECIMAL(40,0), number DECIMAL(40,0), squareFeet DECIMAL(40, 4))"
     ,"CREATE TABLE Tenant (tenantId DECIMAL(40,0) PRIMARY KEY, name VARCHAR(50))"
     ,"CREATE TABLE BuildingTenant (buildingId DECIMAL(40,0), tenantId DECIMAL(40,0), PRIMARY KEY(buildingId, tenantId))"
-    ,"CREATE TABLE Office (officeId DECIMAL(40,0) PRIMARY KEY, floorId DECIMAL(40,0), label VARCHAR(50))"
+    ,"CREATE TABLE Office (officeId DECIMAL(40,0) PRIMARY KEY, floorId DECIMAL(40,0), label VARCHAR(50), windowId DECIMAL(40,0))"
     ,"CREATE TABLE FloorElevator (floorId DECIMAL(40,0), elevatorId DECIMAL(40,0), PRIMARY KEY(floorId, elevatorId))"
     ,"CREATE TABLE Escalator (escalatorId DECIMAL(40,0) PRIMARY KEY, bottomFloorId DECIMAL(40,0), topFloorId DECIMAL(40,0))"
     ,"CREATE TABLE Person (personId DECIMAL(40,0) PRIMARY KEY, name VARCHAR(50), tenantId DECIMAL(40,0), officeId DECIMAL(40,0))"

Modified: shards/trunk/src/test/org/hibernate/shards/model/Building.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/model/Building.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/model/Building.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -108,4 +108,9 @@
   public ShardId getShardId() {
     return shardId;
   }
+
+  @Override
+  public String toString() {
+    return buildingId.toString() + "," + name;
+  }
 }

Modified: shards/trunk/src/test/org/hibernate/shards/strategy/exit/OrderExitOperationTest.java
===================================================================
--- shards/trunk/src/test/org/hibernate/shards/strategy/exit/OrderExitOperationTest.java	2008-02-13 04:27:52 UTC (rev 14332)
+++ shards/trunk/src/test/org/hibernate/shards/strategy/exit/OrderExitOperationTest.java	2008-02-13 20:41:47 UTC (rev 14333)
@@ -26,6 +26,7 @@
 import org.hibernate.criterion.Order;
 import org.hibernate.metadata.ClassMetadata;
 import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.shards.criteria.InMemoryOrderBy;
 import org.hibernate.shards.defaultmock.ClassMetadataDefaultMock;
 import org.hibernate.shards.defaultmock.EntityPersisterDefaultMock;
 import org.hibernate.shards.defaultmock.SessionFactoryDefaultMock;
@@ -34,7 +35,6 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -43,30 +43,23 @@
  */
 public class OrderExitOperationTest extends TestCase {
 
-  private List<Object> data;
-  private ArrayList<Object> shuffledList;
-  private List<Object> nonNullData;
-
   private class MyInt {
     private final Integer i;
 
     private final String name;
 
-    private MyInt innerMyInt;
+    private final MyInt innerMyInt;
 
-    public MyInt(int i, String name) {
+    public MyInt(int i, String name, MyInt inner) {
       this.i = i;
       this.name = name;
+      this.innerMyInt = inner;
     }
 
     public MyInt getInnerMyInt() {
       return innerMyInt;
     }
 
-    public void setInnerMyInt(MyInt innerMyInt) {
-      this.innerMyInt = innerMyInt;
-    }
-
     public Number getValue() {
       return i;
     }
@@ -83,53 +76,139 @@
     }
   }
 
-  protected void setUp() throws Exception {
-    super.setUp();
-    String[] names = {"tomislav", "max", "maulik", "gut", "null", "bomb"};
-    data = Lists.newArrayList();
-    for(int i=0; i<6; i++) {
-      if (i == 4) {
-        data.add(null);
-      } else {
-        data.add(new MyInt(i, names[i]));
-      }
-    }
+  public void testApplySingleOrdering() throws Exception {
+    List<Object> data = Lists.newArrayList();
+    data.add(new MyInt(3, "tomislav", null));
+    data.add(new MyInt(1, "bomb", null));
+    data.add(new MyInt(27, "max", null));
+    data.add(new MyInt(2, "maulik", null));
+    data.add(new MyInt(5, "gut", null));
+    
+    InMemoryOrderBy imob = new InMemoryOrderBy(null, Order.asc("value"));
+    OrderExitOperation oeo = new OrderExitOperation(Collections.singletonList(imob));
+    List<MyInt> sortedList = (List<MyInt>)(List)oeo.apply(data);
 
-    nonNullData = ExitOperationUtils.getNonNullList(data);
+    assertEquals(1, sortedList.get(0).getValue());
+    assertEquals(2, sortedList.get(1).getValue());
+    assertEquals(3, sortedList.get(2).getValue());
+    assertEquals(5, sortedList.get(3).getValue());
+    assertEquals(27, sortedList.get(4).getValue());
 
-    shuffledList = Lists.newArrayList(data);
-    Collections.shuffle(shuffledList);
+    imob = new InMemoryOrderBy(null, Order.desc("value"));
+    oeo = new OrderExitOperation(Collections.singletonList(imob));
+    sortedList = (List<MyInt>)(List)oeo.apply(data);
+
+    assertEquals(27, sortedList.get(0).getValue());
+    assertEquals(5, sortedList.get(1).getValue());
+    assertEquals(3, sortedList.get(2).getValue());
+    assertEquals(2, sortedList.get(3).getValue());
+    assertEquals(1, sortedList.get(4).getValue());
   }
 
-  public void testApply() throws Exception {
-    Order order = Order.asc("value");
-    OrderExitOperation oeo = new OrderExitOperation(order);
-    List unShuffledList = oeo.apply(shuffledList);
+  public void testApplySingleNestedOrdering() throws Exception {
+    List<Object> data = Lists.newArrayList();
+    data.add(new MyInt(3, "tomislav", new MyInt(3, "tomislav", null)));
+    data.add(new MyInt(1, "bomb", new MyInt(1, "bomb", null)));
+    data.add(new MyInt(27, "max", new MyInt(27, "max", null)));
+    data.add(new MyInt(2, "maulik", new MyInt(2, "maulik", null)));
+    data.add(new MyInt(5, "gut", new MyInt(5, "gut", null)));
 
-    assertTrue(nonNullData.equals(unShuffledList));
+    InMemoryOrderBy imob = new InMemoryOrderBy(null, Order.asc("innerMyInt.value"));
+    OrderExitOperation oeo = new OrderExitOperation(Collections.singletonList(imob));
+    List<MyInt> sortedList = (List<MyInt>)(List)oeo.apply(data);
+
+    assertEquals(1, sortedList.get(0).getValue());
+    assertEquals(2, sortedList.get(1).getValue());
+    assertEquals(3, sortedList.get(2).getValue());
+    assertEquals(5, sortedList.get(3).getValue());
+    assertEquals(27, sortedList.get(4).getValue());
+
+    imob = new InMemoryOrderBy(null, Order.desc("innerMyInt.value"));
+    oeo = new OrderExitOperation(Collections.singletonList(imob));
+    sortedList = (List<MyInt>)(List)oeo.apply(data);
+
+    assertEquals(27, sortedList.get(0).getValue());
+    assertEquals(5, sortedList.get(1).getValue());
+    assertEquals(3, sortedList.get(2).getValue());
+    assertEquals(2, sortedList.get(3).getValue());
+    assertEquals(1, sortedList.get(4).getValue());
   }
 
-  public void testMultipleOrderings() throws Exception {
-    Order orderValue = Order.asc("value");
-    Order orderName = Order.desc("name");
+  public void testApplyMultipleOrderings() {
+    List<Object> data = Lists.newArrayList();
+    data.add(new MyInt(2, "tomislav", null));
+    data.add(new MyInt(1, "bomb", null));
+    data.add(new MyInt(27, "max", null));
+    data.add(new MyInt(2, "maulik", null));
+    data.add(new MyInt(5, "gut", null));
 
-    OrderExitOperation oeoValue = new OrderExitOperation(orderValue);
-    OrderExitOperation oeoName = new OrderExitOperation(orderName);
+    List<InMemoryOrderBy> list = Lists.newArrayList(
+        new InMemoryOrderBy(null, Order.asc("value")),
+        new InMemoryOrderBy(null, Order.desc("name"))
+    );
 
-    List<MyInt> answer =
-        Lists.newArrayList(
-            new MyInt(0, "tomislav"),
-            new MyInt(1, "max"),
-            new MyInt(2, "maulik"),
-            new MyInt(3, "gut"),
-            new MyInt(5, "bomb"));
-    List unShuffledList = oeoName.apply(oeoValue.apply(shuffledList));
+    OrderExitOperation operation = new OrderExitOperation(list);
+    List<MyInt> sortedList = (List<MyInt>)(List)operation.apply(data);
 
+    assertEquals(1, sortedList.get(0).getValue());
+    assertEquals(2, sortedList.get(1).getValue());
+    assertEquals("tomislav", sortedList.get(1).getName());
+    assertEquals(2, sortedList.get(2).getValue());
+    assertEquals("maulik", sortedList.get(2).getName());
+    assertEquals(5, sortedList.get(3).getValue());
+    assertEquals(27, sortedList.get(4).getValue());
+  }
 
-    assertEquals(answer, unShuffledList);
+  public void testApplyMultipleNestedOrderings() {
+    List<Object> data = Lists.newArrayList();
+    data.add(new MyInt(2, "tomislav", new MyInt(2, "tomislav", null)));
+    data.add(new MyInt(1, "bomb", new MyInt(1, "bomb", null)));
+    data.add(new MyInt(27, "max", new MyInt(27, "max", null)));
+    data.add(new MyInt(2, "maulik", new MyInt(2, "maulik", null)));
+    data.add(new MyInt(5, "gut", new MyInt(5, "gut", null)));
 
+    List<InMemoryOrderBy> list = Lists.newArrayList(
+        new InMemoryOrderBy(null, Order.asc("innerMyInt.value")),
+        new InMemoryOrderBy(null, Order.desc("innerMyInt.name"))
+    );
+
+    OrderExitOperation operation = new OrderExitOperation(list);
+    List<MyInt> sortedList = (List<MyInt>)(List)operation.apply(data);
+
+    assertEquals(1, sortedList.get(0).getValue());
+    assertEquals(2, sortedList.get(1).getValue());
+    assertEquals("tomislav", sortedList.get(1).getName());
+    assertEquals(2, sortedList.get(2).getValue());
+    assertEquals("maulik", sortedList.get(2).getName());
+    assertEquals(5, sortedList.get(3).getValue());
+    assertEquals(27, sortedList.get(4).getValue());
   }
 
+  public void testApplyMultipleMixedOrderings() {
+    List<Object> data = Lists.newArrayList();
+    data.add(new MyInt(2, "tomislav", new MyInt(2, "tomislav", null)));
+    data.add(new MyInt(1, "bomb", new MyInt(1, "bomb", null)));
+    data.add(new MyInt(27, "max", new MyInt(27, "max", null)));
+    data.add(new MyInt(2, "maulik", new MyInt(2, "maulik", null)));
+    data.add(new MyInt(5, "gut", new MyInt(5, "gut", null)));
+
+    List<InMemoryOrderBy> list = Lists.newArrayList(
+        new InMemoryOrderBy(null, Order.asc("innerMyInt.value")),
+        new InMemoryOrderBy(null, Order.desc("name"))
+    );
+
+    OrderExitOperation operation = new OrderExitOperation(list);
+    List<MyInt> sortedList = (List<MyInt>)(List)operation.apply(data);
+
+    assertEquals(1, sortedList.get(0).getValue());
+    assertEquals(2, sortedList.get(1).getValue());
+    assertEquals("tomislav", sortedList.get(1).getName());
+    assertEquals(2, sortedList.get(2).getValue());
+    assertEquals("maulik", sortedList.get(2).getName());
+    assertEquals(5, sortedList.get(3).getValue());
+    assertEquals(27, sortedList.get(4).getValue());
+  }
+
   static class SessionFactoryMock extends SessionFactoryDefaultMock {
 
     public ClassMetadata getClassMetadata(Class persistentClass)




More information about the hibernate-commits mailing list