Skip to content

Commit

Permalink
HSHARDS-52
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MrMaxRoss committed Feb 13, 2008
1 parent 51d967d commit e526f0a
Show file tree
Hide file tree
Showing 18 changed files with 674 additions and 151 deletions.
4 changes: 2 additions & 2 deletions src/java/org/hibernate/shards/ShardedConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ boolean doesNotSupportTopLevelSave(Property property) {
}

/**
* 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class ExitOperationsCriteriaCollector implements ExitOperationsCollector
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());
Expand Down Expand Up @@ -123,11 +123,14 @@ public ExitOperationsCollector addProjection(Projection projection) {
/**
* 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;
}

Expand All @@ -153,9 +156,12 @@ public List<Object> apply(List<Object> result) {
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);
}
Expand Down Expand Up @@ -189,4 +195,11 @@ public void setSessionFactory(SessionFactoryImplementor sessionFactoryImplemento
this.sessionFactoryImplementor = sessionFactoryImplementor;
}

Integer getMaxResults() {
return maxResults;
}

Integer getFirstResult() {
return firstResult;
}
}
75 changes: 75 additions & 0 deletions src/java/org/hibernate/shards/criteria/InMemoryOrderBy.java
Original file line number Diff line number Diff line change
@@ -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@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(' '));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ public SetMaxResultsEvent(int maxResults) {
public void onEvent(Criteria crit) {
crit.setMaxResults(maxResults);
}

public int getMaxResults() {
return maxResults;
}
}
69 changes: 60 additions & 9 deletions src/java/org/hibernate/shards/criteria/ShardedCriteriaImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ public class ShardedCriteriaImpl implements ShardedCriteria {

// 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
Expand Down Expand Up @@ -166,7 +172,16 @@ public Criteria add(Criterion criterion) {
}

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;
}

Expand Down Expand Up @@ -244,10 +259,18 @@ public Criteria createAlias(String associationPath, String alias,
* 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);
Expand All @@ -265,25 +288,25 @@ private ShardedSubcriteriaImpl createSubcriteria(SubcriteriaFactory factory) {
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) {
Expand All @@ -299,14 +322,42 @@ public Criteria setResultTransformer(ResultTransformer resultTransformer) {
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;
}

Expand Down
53 changes: 24 additions & 29 deletions src/java/org/hibernate/shards/criteria/ShardedSubcriteriaImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,29 @@ class ShardedSubcriteriaImpl implements ShardedSubcriteria {
// 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);
Expand Down Expand Up @@ -137,6 +148,7 @@ public Criteria add(Criterion criterion) {
}

public Criteria addOrder(Order order) {
criteriaCollector.addOrder(associationPath, order);
CriteriaEvent event = new AddOrderEvent(order);
for (Shard shard : shards) {
if (shardToCriteriaMap.get(shard) != null) {
Expand Down Expand Up @@ -223,33 +235,13 @@ public Criteria setResultTransformer(ResultTransformer resultTransformer) {
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;
}

Expand Down Expand Up @@ -358,9 +350,12 @@ public Object uniqueResult() throws HibernateException {
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) {
Expand Down Expand Up @@ -395,25 +390,25 @@ public void establishSubcriteria(Criteria parentCriteria, SubcriteriaFactory sub
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() {
Expand Down
Loading

0 comments on commit e526f0a

Please sign in to comment.